เหตุการณ์เสร็จสิ้นการทำซ้ำ ng


207

ฉันต้องการเรียกฟังก์ชั่น jQuery กำหนดเป้าหมาย div ด้วยตาราง ng-repeatตารางที่เป็นประชากรที่มี

เมื่อฉันเรียกมันว่า

$(document).ready()

ฉันไม่มีผลลัพธ์

ด้วย

$scope.$on('$viewContentLoaded', myFunc);

ไม่ช่วย

มีวิธีการดำเนินการฟังก์ชั่นใด ๆ หลังจากที่ประชากรซ้ำซ้อนเสร็จสมบูรณ์? ฉันได้อ่านคำแนะนำเกี่ยวกับการใช้งานที่กำหนดเองdirectiveแล้ว แต่ฉันไม่รู้ว่าจะใช้งานอย่างไรกับ ng-repeat และ div ของฉัน ...

คำตอบ:


241

แน่นอนคุณควรใช้คำสั่งและไม่มีเหตุการณ์ผูกติดกับจุดสิ้นสุดของ ng-Repeat loop (เนื่องจากแต่ละองค์ประกอบถูกสร้างเป็นเอกเทศและมีเหตุการณ์เป็นของตัวเอง) แต่ a) การใช้คำสั่งอาจเป็นสิ่งที่คุณต้องการและ b) มีคุณสมบัติเฉพาะ ng-Repeat ที่คุณสามารถใช้เพื่อทำให้เหตุการณ์ "on ngRepeat เสร็จสิ้น" ของคุณ

โดยเฉพาะถ้าสิ่งที่คุณต้องการคือการจัดรูปแบบ / เพิ่มเหตุการณ์ลงในตารางทั้งหมดคุณสามารถทำได้โดยใช้คำสั่งที่ครอบคลุมองค์ประกอบ ngRepeat ทั้งหมด ในทางกลับกันหากคุณต้องการระบุแต่ละองค์ประกอบโดยเฉพาะคุณสามารถใช้คำสั่งภายใน ngRepeat และมันจะทำหน้าที่กับแต่ละองค์ประกอบหลังจากที่มันถูกสร้างขึ้น

จากนั้นยังมี$index, $first, $middleและ$lastคุณสมบัติที่คุณสามารถใช้ทริกเกอร์เหตุการณ์ ดังนั้นสำหรับ HTML นี้:

<div ng-controller="Ctrl" my-main-directive>
  <div ng-repeat="thing in things" my-repeat-directive>
    thing {{thing}}
  </div>
</div>

คุณสามารถใช้คำสั่งดังนี้:

angular.module('myApp', [])
.directive('myRepeatDirective', function() {
  return function(scope, element, attrs) {
    angular.element(element).css('color','blue');
    if (scope.$last){
      window.alert("im the last!");
    }
  };
})
.directive('myMainDirective', function() {
  return function(scope, element, attrs) {
    angular.element(element).css('border','5px solid red');
  };
});

ดูการทำงานจริงในPlunkerนี้ หวังว่ามันจะช่วย!


ฉันจะทำให้myMainDirectiveโค้ดทำงานหลังจากสิ้นสุดลูปได้หรือไม่ ฉันจำเป็นต้องอัปเดตแถบเลื่อนของ div div
ChruS

2
แน่นอน! เพียงแค่เปลี่ยน "window.alert" เป็นฟังก์ชั่นการยิงเหตุการณ์และจับมันด้วยคำสั่งหลัก ฉันอัปเดต Plunker เพื่อทำสิ่งนี้เป็นตัวอย่าง
Tiago Roldão

9
ขอแนะนำให้รวมไลบรารี jQuery ก่อน (ก่อนหน้า Angular) สิ่งนี้จะทำให้องค์ประกอบเชิงมุมทั้งหมดถูกห่อด้วย jQuery (แทนที่จะเป็น jqLite) จากนั้นแทน angular.element (องค์ประกอบ) .css () และ $ (องค์ประกอบ) .children (). css () คุณสามารถเขียน element.css () และ element.children (). css () ได้ ดูเพิ่มเติมที่groups.google.com/d/msg/angular/6A3Skwm59Z4/oJ0WhKGAFK0J
Mark Rajcok

11
Any1 มีแนวคิดใดที่จะทำให้สิ่งนี้ทำงานเพื่อแสดงผลในคำสั่ง ngRepeat? ie: link
RavenHursT

1
นี่คือแบบแผนการตั้งชื่อสำหรับคำสั่งเชิงมุมทั้งหมด ดูdocs.angularjs.org/guide/directive#normalization
Tiago Roldão

68

หากคุณต้องการรันโค้ดบางอย่างในตอนท้ายของลูปนี่เป็นรูปแบบที่เรียบง่ายกว่าเล็กน้อยซึ่งไม่ต้องการการจัดการเหตุการณ์เพิ่มเติม:

<div ng-controller="Ctrl">
  <div class="thing" ng-repeat="thing in things" my-post-repeat-directive>
    thing {{thing}}
  </div>
</div>
function Ctrl($scope) {
  $scope.things = [
    'A', 'B', 'C'  
  ];
}

angular.module('myApp', [])
.directive('myPostRepeatDirective', function() {
  return function(scope, element, attrs) {
    if (scope.$last){
      // iteration is complete, do whatever post-processing
      // is necessary
      element.parent().css('border', '1px solid black');
    }
  };
});

ดูการสาธิตสด


ใช้งานได้หรือไม่ถ้าฉันใช้คอนโทรลเลอร์เป็นไวยากรณ์ กรุณา? ไม่ใช่มีวิธีอื่นที่ใช้กับคอนโทรลเลอร์ได้หรือไม่?
Dan Nguyen

63

ไม่จำเป็นต้องสร้างคำสั่งโดยเฉพาะเพื่อให้มีng-repeatเหตุการณ์ที่สมบูรณ์

ng-init มหัศจรรย์สำหรับคุณไหม

  <div ng-repeat="thing in things" ng-init="$last && finished()">

$lastทำให้แน่ใจว่าfinishedเพียง แต่ถูกไล่ออกเมื่อองค์ประกอบสุดท้ายที่ได้รับการแสดงให้ DOM

อย่าลืมที่จะสร้าง$scope.finishedกิจกรรม

Happy Coding !!

แก้ไข: 23 ต.ค. 2559

ในกรณีที่คุณต้องการเรียกใช้finishedฟังก์ชันเมื่อไม่มีรายการในอาเรย์คุณสามารถใช้วิธีแก้ปัญหาต่อไปนี้

<div style="display:none" ng-init="things.length < 1 && finished()"></div>
//or
<div ng-if="things.length > 0" ng-init="finished()"></div>

เพียงเพิ่มบรรทัดด้านบนที่ด้านบนของng-repeatองค์ประกอบ มันจะตรวจสอบว่าอาร์เรย์ไม่มีค่าใด ๆ และเรียกใช้ฟังก์ชันตาม

เช่น

<div ng-if="things.length > 0" ng-init="finished()"></div>
<div ng-repeat="thing in things" ng-init="$last && finished()">

เกิดอะไรขึ้นถ้า "สิ่ง" กลายเป็นอาร์เรย์ว่างเปล่า?
Frane Poljak

1
@FranePoljak ฉันได้เพิ่มวิธีแก้ปัญหาในคำตอบ
Vikas Bansal

1
ในกรณีที่ไม่มีอาเรย์ให้จัดการกับมันในคอนโทรลเลอร์
JSAddict

แก้ไขคำตอบของ Vikas Bansal: // ใช้ div แรกถ้าคุณต้องการตรวจสอบว่า array ไม่มีค่าใด ๆ และเรียกใช้ฟังก์ชันตามนั้นหรือไม่ <div ng-if = "! things.length" ng-hide = "true" ng-init = "เสร็จสิ้น ()"> </div> <div ng-ซ้ำ = "สิ่งในสิ่งต่าง ๆ " ng-init = "$ สุดท้าย && เสร็จสิ้น () ">
Alex Teslenko

41

นี่คือคำสั่งทำซ้ำที่เรียกใช้ฟังก์ชันที่ระบุเมื่อเป็นจริง ฉันได้พบว่าฟังก์ชั่นที่เรียกใช้ต้องใช้ $ timeout กับ interval = 0 ก่อนที่จะทำการจัดการ DOM เช่นการเตรียมใช้งานคำแนะนำเครื่องมือบนองค์ประกอบที่แสดงผล jsFiddle: http://jsfiddle.net/tQw6w/

ใน $ scope.layoutDone ลองใส่ความคิดเห็นในบรรทัด $ timeout และยกเลิกการใส่เครื่องหมาย "ไม่ถูกต้อง!" บรรทัดเพื่อดูความแตกต่างในเคล็ดลับเครื่องมือ

<ul>
    <li ng-repeat="feed in feedList" repeat-done="layoutDone()" ng-cloak>
    <a href="{{feed}}" title="view at {{feed | hostName}}" data-toggle="tooltip">{{feed | strip_http}}</a>
    </li>
</ul>

JS:

angular.module('Repeat_Demo', [])

    .directive('repeatDone', function() {
        return function(scope, element, attrs) {
            if (scope.$last) { // all are rendered
                scope.$eval(attrs.repeatDone);
            }
        }
    })

    .filter('strip_http', function() {
        return function(str) {
            var http = "http://";
            return (str.indexOf(http) == 0) ? str.substr(http.length) : str;
        }
    })

    .filter('hostName', function() {
        return function(str) {
            var urlParser = document.createElement('a');
            urlParser.href = str;
            return urlParser.hostname;
        }
    })

    .controller('AppCtrl', function($scope, $timeout) {

        $scope.feedList = [
            'http://feeds.feedburner.com/TEDTalks_video',
            'http://feeds.nationalgeographic.com/ng/photography/photo-of-the-day/',
            'http://sfbay.craigslist.org/eng/index.rss',
            'http://www.slate.com/blogs/trending.fulltext.all.10.rss',
            'http://feeds.current.com/homepage/en_US.rss',
            'http://feeds.current.com/items/popular.rss',
            'http://www.nytimes.com/services/xml/rss/nyt/HomePage.xml'
        ];

        $scope.layoutDone = function() {
            //$('a[data-toggle="tooltip"]').tooltip(); // NOT CORRECT!
            $timeout(function() { $('a[data-toggle="tooltip"]').tooltip(); }, 0); // wait...
        }

    })

ฉันไม่ต้องการใช้การหมดเวลา $ แต่เมื่อคุณบอกว่าสามารถตั้งค่าเป็น 0 ได้ฉันตัดสินใจลองและใช้งานได้ดี ฉันสงสัยว่านี่เป็นเรื่องของการย่อยอาหารหรือไม่และหากมีวิธีการทำสิ่งที่ $ timeout กำลังทำอยู่โดยไม่ใช้ $ timeout
Jameela Huq

คุณกรุณาอธิบายว่าทำไมการทำงานนี้ ฉันพยายามเข้าถึง ID แบบไดนามิกและใช้งานได้หลังจากหมดเวลาด้วยการหน่วงเวลาเป็น 0 ฉันเห็นโซลูชัน "แฮ็ค" นี้ในหลายบทความ แต่ไม่มีใครอธิบายว่าทำไมมันจึงใช้งานได้
Jin

30

นี่คือวิธีการง่ายๆที่ใช้ng-initที่ไม่จำเป็นต้องมีคำสั่งที่กำหนดเอง มันทำงานได้ดีสำหรับฉันในบางสถานการณ์เช่นจำเป็นต้องเลื่อน div ของรายการ ng-ซ้ำโดยอัตโนมัติไปยังรายการเฉพาะในการโหลดหน้าดังนั้นฟังก์ชันการเลื่อนต้องรอจนกว่าการng-repeatเรนเดอร์ไปที่ DOM เสร็จสิ้นก่อนจึงจะสามารถดำเนินการได้

<div ng-controller="MyCtrl">
    <div ng-repeat="thing in things">
        thing: {{ thing }}
    </div>
    <div ng-init="fireEvent()"></div>
</div>

myModule.controller('MyCtrl', function($scope, $timeout){
    $scope.things = ['A', 'B', 'C'];

    $scope.fireEvent = function(){

        // This will only run after the ng-repeat has rendered its things to the DOM
        $timeout(function(){
            $scope.$broadcast('thingsRendered');
        }, 0);

    };
});

โปรดทราบว่าสิ่งนี้มีประโยชน์สำหรับฟังก์ชั่นที่คุณต้องการโทรหนึ่งครั้งหลังจากการแสดงซ้ำ ng เริ่มแรก หากคุณต้องการเรียกใช้ฟังก์ชันเมื่อใดก็ตามที่มีการปรับปรุงเนื้อหา ng-repeat คุณจะต้องใช้หนึ่งในคำตอบอื่น ๆ ในกระทู้นี้ด้วยคำสั่งที่กำหนดเอง


1
สวยนี่ทำงานได้ดี ฉันต้องการเลือกข้อความในกล่องข้อความโดยอัตโนมัติและการหมดเวลาใช้เพื่อหลอกลวง มิฉะนั้นจะมีการเลือกข้อความ {{model.value}} จากนั้นยกเลิกการเลือกเมื่อ data-bound model.value ถูกฉีด
JoshGough

27

การเสริมคำตอบของพาเวลสิ่งที่สามารถอ่านได้และเข้าใจได้ง่ายกว่าคือ:

<ul>
    <li ng-repeat="item in items" 
        ng-init="$last ? doSomething() : angular.noop()">{{item}}</li>
</ul>

ทำไมคุณคิดว่าangular.noopจะมีอย่างอื่นในตอนแรก ... ?

ข้อดี:

คุณไม่จำเป็นต้องเขียนคำสั่งสำหรับสิ่งนี้ ...


20
ทำไมต้องใช้ ternary เมื่อคุณสามารถใช้ตรรกะบูลีน:ng-init="$last && doSomething( )"
Nate Whittaker

มันง่ายต่อการอ่าน @NateWhittaker (และการอ่านยากกว่าผู้ประกอบการที่น่าประทับใจมาก) เป็นการดีที่จะทำความสะอาดและเข้าใจรหัสได้ง่าย
Justin

21

อาจเป็นวิธีที่ง่ายขึ้นเล็กน้อยด้วยngInitและdebounceวิธีของ Lodash โดยไม่จำเป็นต้องมีคำสั่งที่กำหนดเอง:

ควบคุม:

$scope.items = [1, 2, 3, 4];

$scope.refresh = _.debounce(function() {
    // Debounce has timeout and prevents multiple calls, so this will be called 
    // once the iteration finishes
    console.log('we are done');
}, 0);

แม่แบบ:

<ul>
    <li ng-repeat="item in items" ng-init="refresh()">{{item}}</li>
</ul>

ปรับปรุง

มีวิธีแก้ปัญหา AngularJS บริสุทธิ์ที่ง่ายยิ่งขึ้นโดยใช้ผู้ประกอบการที่ประกอบไปด้วย:

แม่แบบ:

<ul>
    <li ng-repeat="item in items" ng-init="$last ? doSomething() : null">{{item}}</li>
</ul>

โปรดทราบว่าngInitใช้เฟสการคอมไพล์ล่วงหน้าลิงก์ - เช่นนิพจน์ถูกเรียกใช้ก่อนประมวลผลคำสั่งย่อย ซึ่งหมายความว่ายังจำเป็นต้องมีการประมวลผลแบบอะซิงโครนัส


ควรทราบว่าคุณต้องการ lodash.js เพื่อให้ทำงานได้ :) นอกจากนี้: ส่วน ", 20" ของ $ scope.refresh = จำนวนมิลลิวินาทีก่อนที่วิธีนี้จะถูกเรียกใช้หลังจากการทำซ้ำครั้งล่าสุดหรือไม่
JørgenSkår Fischer

เพิ่มLodashไว้หน้าลิงก์debounce ใช่ 20 มีความล่าช้าก่อนที่จะมีการดำเนินการเมธอด - เปลี่ยนเป็น 0 เนื่องจากไม่สำคัญตราบใดที่การโทรเป็นแบบอะซิงก์
Pavel Horal

1
ยังคิดเกี่ยวกับเรื่องนี้เนื่องจากผู้ประกอบการที่ได้รับอนุญาตในการแสดงออกง่ายng-init="$last ? refresh() : false"จะทำงานเช่นกัน และนั่นก็ไม่ได้ต้องการ Lodash
Pavel Horal

<div ng-repeat = "i ใน [1,2,4]" ng-init = "doSomething ($ last)"> </div> $ scope.doSomething = ฟังก์ชัน (lastElement) {ถ้า (lastElem) blah blah blah }
JSAddict

4

นอกจากนี้ยังอาจมีความจำเป็นเมื่อคุณตรวจสอบตัวแปรในการห่อทริกเกอร์ของคุณด้วยscope.$last setTimeout(someFn, 0)A setTimeout 0เป็นเทคนิคที่ได้รับการยอมรับในจาวาสคริปต์และมันเป็นสิ่งสำคัญที่ฉันdirectiveจะต้องทำงานอย่างถูกต้อง


ฉันพบปัญหาเดียวกัน ทั้งในแบ็กโบน + เชิงมุมหากคุณต้องการทำอะไรเช่นอ่านคุณสมบัติของ css ที่ใช้กับองค์ประกอบ DOM ฉันไม่สามารถหาวิธีที่ไม่มีแฮ็กการหมดเวลา
chovy

3

นี่คือการปรับปรุงแนวคิดที่แสดงในคำตอบอื่น ๆ เพื่อแสดงวิธีเข้าถึงคุณสมบัติ ngRepeat ($ index, $ first, $ first, $ middle, $ last, $ Even, $ odd, $ odd) เมื่อใช้ไวยากรณ์ที่ประกาศและแยกขอบเขต ( Google แนะนำวิธีปฏิบัติที่ดีที่สุด) พร้อมองค์ประกอบคำสั่ง บันทึกความแตกต่างหลัก: scope.$parent.$last.

angular.module('myApp', [])
.directive('myRepeatDirective', function() {
  return {
    restrict: 'E',
    scope: {
      someAttr: '='
    },
    link: function(scope, element, attrs) {
      angular.element(element).css('color','blue');
      if (scope.$parent.$last){
        window.alert("im the last!");
      }
    }
  };
});

จะเป็นการดีกว่าหรือไม่ที่จะใช้คำสั่งเป็น attribs มากกว่าองค์ประกอบ? ข้อ จำกัด ie: 'A'?
Tejasvi Hegde

2
ประเด็นก็คือแสดงให้เห็นถึงการประกาศไวยากรณ์ + ขอบเขตแยก ... ใช่คุณสามารถใช้คำสั่งแอตทริบิวต์ที่นี่ถ้าคุณต้องการ มีเวลาและสถานที่สำหรับทั้งขึ้นอยู่กับวัตถุประสงค์ของคุณ
JoshuaDavid

3

ฉันทำอย่างนี้

สร้างคำสั่ง

function finRepeat() {
    return function(scope, element, attrs) {
        if (scope.$last){
            // Here is where already executes the jquery
            $(document).ready(function(){
                $('.materialboxed').materialbox();
                $('.tooltipped').tooltip({delay: 50});
            });
        }
    }
}

angular
    .module("app")
    .directive("finRepeat", finRepeat);

หลังจากที่คุณเพิ่มลงในป้ายกำกับที่ ng-repeat นี้

<ul>
    <li ng-repeat="(key, value) in data" fin-repeat> {{ value }} </li>
</ul>

และพร้อมที่จะถูกเรียกใช้ในตอนท้ายของ ng-repeat


3
<div ng-repeat="i in items">
        <label>{{i.Name}}</label>            
        <div ng-if="$last" ng-init="ngRepeatFinished()"></div>            
</div>

ทางออกของฉันคือการเพิ่ม div เพื่อเรียกใช้ฟังก์ชั่นหากรายการนั้นเป็นครั้งสุดท้ายในการทำซ้ำ


2

ฉันต้องการเพิ่มคำตอบอื่นเนื่องจากคำตอบก่อนหน้านี้ใช้รหัสที่จำเป็นในการเรียกใช้หลังจากทำ ngRepeat คือรหัสเชิงมุมซึ่งในกรณีนั้นคำตอบทั้งหมดข้างต้นให้คำตอบที่ดีและเรียบง่าย และในกรณีที่มีความสำคัญขั้นตอนวงจรชีวิตของการย่อยคุณสามารถดูบล็อกของ Ben Nadelเกี่ยวกับเรื่องนี้ได้ยกเว้นการใช้ $ parse แทน $ eval

แต่จากประสบการณ์ของฉันในขณะที่สถานะ OP มักใช้งานปลั๊กอิน JQuery หรือวิธีการบน DOM ที่รวบรวม finnaly ซึ่งในกรณีนั้นฉันพบว่าวิธีที่ง่ายที่สุดคือการสร้างคำสั่งด้วย setTimeout เนื่องจากฟังก์ชั่น setTimeout ได้รับการผลักดัน ไปยังจุดสิ้นสุดของคิวของเบราว์เซอร์มันมักจะทันทีหลังจากที่ทุกอย่างทำในเชิงมุมมักจะ ngReapet ซึ่งยังคงดำเนินต่อไปหลังจากพ่อแม่ของมันฟังก์ชั่น postLinking

angular.module('myApp', [])
.directive('pluginNameOrWhatever', function() {
  return function(scope, element, attrs) {        
    setTimeout(function doWork(){
      //jquery code and plugins
    }, 0);        
  };
});

สำหรับใครก็ตามที่สงสัยว่าในกรณีนั้นทำไมไม่ใช้ $ timeout มันทำให้เกิดวงจรย่อยอื่นที่ไม่จำเป็นโดยสิ้นเชิง


1

ฉันต้องแสดงสูตรโดยใช้ MathJax หลังจากสิ้นสุดการทำซ้ำ ng ไม่มีคำตอบข้างต้นแก้ปัญหาของฉันดังนั้นฉันทำเหมือนด้านล่าง มันไม่ใช่ทางออกที่ดีแต่ทำงานให้ฉัน ...

<div ng-repeat="formula in controller.formulas">
    <div>{{formula.string}}</div>
    {{$last ? controller.render_formulas() : ""}}
</div>

0

ฉันพบคำตอบที่นี่ซึ่งได้รับการฝึกฝนมาเป็นอย่างดี แต่ก็ยังจำเป็นต้องเพิ่มความล่าช้า

สร้างคำสั่งต่อไปนี้:

angular.module('MyApp').directive('emitLastRepeaterElement', function() {
return function(scope) {
    if (scope.$last){
        scope.$emit('LastRepeaterElement');
    }
}; });

เพิ่มไปยัง repeater ของคุณเป็นคุณลักษณะเช่นนี้:

<div ng-repeat="item in items" emit-last-repeater-element></div>

ตาม Radu:

$scope.eventoSelecionado.internamento_evolucoes.forEach(ie => {mycode});

สำหรับฉันมันใช้งานได้ แต่ฉันยังต้องเพิ่ม setTimeout

$scope.eventoSelecionado.internamento_evolucoes.forEach(ie => {
setTimeout(function() { 
    mycode
}, 100); });

-4

ถ้าคุณเพียงต้องการเปลี่ยนชื่อคลาสดังนั้นมันจะแสดงผลแตกต่างกันรหัสด้านล่างจะทำเคล็ดลับ

<div>
<div ng-show="loginsuccess" ng-repeat="i in itemList">
    <div id="{{i.status}}" class="{{i.status}}">
        <div class="listitems">{{i.item}}</div>
        <div class="listitems">{{i.qty}}</div>
        <div class="listitems">{{i.date}}</div>
        <div class="listbutton">
            <button ng-click="UpdateStatus(i.$id)" class="btn"><span>Done</span></button>
            <button ng-click="changeClass()" class="btn"><span>Remove</span></button>
        </div>
    <hr>
</div>

รหัสนี้ทำงานสำหรับฉันเมื่อฉันมีความต้องการคล้ายกันในการแสดงรายการที่ซื้อในรายการช้อปปิ้งของฉันในแบบอักษรรางน้ำ Strick

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.