ng-tekrar bitirme olayı


207

Bazı jQuery işlev div ile tablo hedefleme çağırmak istiyorum. Bu tablo ile doldurulur ng-repeat.

Onu aradığımda

$(document).ready()

Sonuç yok.

Ayrıca

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

yardımcı olmuyor.

Tekrarlanan popülasyon tamamlandıktan hemen sonra işlevi yerine getirmenin bir yolu var mı? Özel kullanımı hakkında bir tavsiye okudum directive, ama ng-repeat ve benim div ile nasıl kullanılacağına dair hiçbir fikrim yok ...

Yanıtlar:


241

Gerçekten, direktifleri kullanmalısınız ve bir ng-Tekrar döngüsünün sonuna bağlı bir olay yoktur (her eleman ayrı ayrı inşa edildiğinden ve kendi olayına sahip olduğundan). Ancak a) direktifleri kullanmak ihtiyacınız olan tek şey olabilir ve b) "on ngRepeat bitmiş" etkinliğinizi yapmak için kullanabileceğiniz birkaç ng-Repeat özel özelliği vardır.

Özellikle, istediğiniz tek şey tüm tablonun etkinliklerini biçimlendirmek / eklemekse, bunu tüm ngRepeat öğelerini kapsayan bir yönergede kullanarak yapabilirsiniz. Öte yandan, her öğeye özel olarak hitap etmek istiyorsanız, ngRepeat içinde bir yönerge kullanabilirsiniz ve oluşturulduktan sonra her öğeye etki eder.

Sonra, orada $index, $first, $middleve $lastözellikleri, tetik olaylara kullanabilirsiniz. Bu HTML için:

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

Bunun gibi yönergeleri kullanabilirsiniz:

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');
  };
});

Bu Plunker'da çalışırken görün . Umarım yardımcı olur!


myMainDirectiveKodunu yalnızca döngü bittikten sonra yürütebilir miyim ? Ana div'in kaydırma çubuğunu güncellemem gerekiyor.
ChruS

2
Elbette! Sadece "window.alert" i olay ateşleme fonksiyonuyla değiştirin ve ana yönerge ile yakalayın. Bunu yapmak için de Plunker'ı güncelledim.
Tiago Roldão

9
Önce jQuery kütüphanesini (Açısaldan önce) dahil etmeniz önerilir. Bu, tüm Açısal öğelerin jQuery ile sarılmasına neden olur (jqLite yerine). Sonra angular.element (element) .css () ve $ (element) .children (). Css () yerine element.css () ve element.children (). Css () yazabilirsiniz. Ayrıca bkz. Groups.google.com/d/msg/angular/6A3Skwm59Z4/oJ0WhKGAFK0J
Mark Rajcok

11
Any1, bunu ngRepeat yönergesinde sonraki render işlemleri için nasıl çalıştıracağına dair bir fikriniz var mı? ie: bağlantı
RavenHursT

1
Bu, tüm açısal direktifler için bir adlandırma kuralıdır. Bkz. Docs.angularjs.org/guide/directive#normalization
Tiago Roldão

68

Döngünün sonunda bazı kodları yürütmek istiyorsanız, ek olay işleme gerektirmeyen biraz daha basit bir varyasyon:

<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');
    }
  };
});

Canlı bir demoya bakın.


Denetleyiciyi sözdizimi olarak kullanırsam bu işe yarar mı? Lütfen? Değil, denetleyici ile çalışan başka bir yolu var mı?
Dan Nguyen

63

Özellikle ng-repeattam bir etkinlik için direktif oluşturmaya gerek yoktur .

ng-init sihri senin için yapar.

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

Bu $last, finishedyalnızca DOM'a son öğe oluşturulduğunda tetiklenmesini sağlar.

$scope.finishedEtkinlik oluşturmayı unutmayın .

Mutlu Kodlama !!

DÜZENLEME: 23 Ekim 2016

finishedDizide hiçbir öğe olmadığında da işlevi çağırmak istiyorsanız , aşağıdaki geçici çözümü kullanabilirsiniz

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

Yukarıdaki satırı ekranın üst kısmına ng-repeat öğenin yeterlidir. Dizinin herhangi bir değeri olup olmadığını kontrol eder ve işlevi buna göre çağırır.

Örneğin

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

"Şeyler" boş bir dizi haline gelirse ne olur?
Frane Poljak

1
@FranePoljak Cevaba çözümü ekledim.
Vikas Bansal

1
Boş dizi durumunda, denetleyicide kullanın
JSAddict

Vikas Bansal'ı düzeltin answer: // dizinin herhangi bir değeri olup olmadığını kontrol etmek ve işlevi buna göre çağırmak istiyorsanız first div kullanın. <div ng-if = "! things.length" ng-hide = "true" ng-init = "tamamlandı ()"> </div> <div ng-repeat = "şeylerde bir şey" ng-init = "$ last && finish () ">
Alex Teslenko

41

İşte true olduğunda belirli bir işlevi çağıran bir tekrarlanan yönerge. Ben çağrılan fonksiyonu DOM zamanlama önce $ zaman aşımı kullanmak gerekir, örneğin işlenmiş öğeler üzerinde araç ipuçları başlatma gibi. jsFiddle: http://jsfiddle.net/tQw6w/

$ Scope.layoutDone içinde, $ timeout satırını yorumlamayı ve "CORRECT!" ipucundaki farkı görmek için

<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...
        }

    })

Bir $ zaman aşımı kullanmak istemedim, ancak 0 olarak ayarlanabileceğini söylediğinizde, denemeye karar verdim ve iyi çalıştı. Bunun bir sindirim meselesi olup olmadığını ve $ zaman aşımının $ timeout kullanmadan ne yaptığını yapmanın bir yolu olup olmadığını merak ediyorum.
Jameela Huq

Bunun neden işe yaradığını açıklayabilir misiniz? Dinamik kimliklere erişmeye çalışıyorum ve sadece 0 gecikmeli bir zaman aşımı yaptıktan sonra çalışıyor. Bu "hacky" çözümünü birkaç gönderide gördüm ama kimse neden işe yaradığını açıklamıyor.
Jin

30

ng-initÖzel bir yönerge bile gerektirmeyen basit bir yaklaşım . Belirli senaryolarda benim için iyi çalıştı, örneğin, tekrarlanan öğelerin bir bölümünü sayfa yükündeki belirli bir öğeye otomatik olarak kaydırmak gerekiyor, bu nedenle kaydırma işlevinin ng-repeat, tetiklenebilmesi için DOM'da işlenmesi bitene kadar beklemesi gerekiyor .

<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);

    };
});

Bunun yalnızca ng-repeat ilk kez oluşturulduktan sonra bir kez çağırmanız gereken fonksiyonlar için yararlı olduğunu unutmayın. Tekrarlanan içerikler güncellendiğinde bir işlevi çağırmanız gerekirse, bu konudaki diğer yanıtlardan birini özel bir yönergeyle kullanmanız gerekir.


1
Güzel, bu işi yaptı. Bir metin kutusundaki metni otomatik olarak seçmek istedim ve zaman aşımı hile yaptı. Aksi takdirde, {{model.value}} metni seçildi ve veriye bağlı model.value enjekte edildiğinde seçimi kaldırıldı.
JoshGough

27

Pavel'in cevabını tamamlamak , daha okunabilir ve kolay anlaşılabilir bir şey olacaktır:

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

Neden başka bir şey olduğunu düşünüyorsun angular.noop...?

Avantajları:

Bunun için bir direktif yazmak zorunda değilsiniz ...


20
Boole mantığını kullanabileceğiniz zaman neden bir üçlü kullanıyorsunuz:ng-init="$last && doSomething( )"
Nate Whittaker

@NateWhittaker'ı okumak daha kolay (ve bir üçlü operatörden daha zor okunması etkileyici). Temiz ve anlaşılması kolay kodlara sahip olmak güzel
Justin

21

Belki ngInitve Lodash'larla biraz daha basit bir yaklaşımdebounce özel yönerge gerek duymadan yöntemiyle :

Denetleyici:

$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);

Şablon:

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

Güncelleme

Üçlü operatör kullanarak daha basit saf AngularJS çözümü vardır:

Şablon:

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

Farkında olduğunu Be ngInit kullandığı ön bağlantı derleme aşaması - çocuk direktifleri işlenmeden önce yani ifade çağrılır. Bu, yine de eşzamansız bir işlemin gerekli olabileceği anlamına gelir.


Bunun çalışması için lodash.js'ye ihtiyacınız olduğunu unutmayın :) Ayrıca: $ scope.refresh = 'in ", 20" kısmı, son yinelemeden sonra bu yöntemin çağrılmasından önceki milis sayısı mı?
Jørgen Skår Fischer

Geri dönme bağlantısının önüne Lodash eklendi . Evet, yöntem yürütülmeden önce 20 gecikmedir - çağrı zaman uyumsuz olduğu sürece önemli olmadığı için 0 olarak değiştirilir.
Pavel Horal

1
Ayrıca bunu düşünerek, üçlü operatörlere ifadelerde izin verildiği için basit ng-init="$last ? refresh() : false"de işe yarayacaktır. Ve bu Lodash'ı bile gerektirmiyor.
Pavel Horal

<div ng-repeat = "[1,2,4] 'de i" ng-init = "doSomething ($ last)"> </div> $ scope.doSomething = function (lastElement) {if (lastElem) blah blah blah }
JSAddict

4

scope.$lastDeğişkeni, tetikleyicinizi a ile sarmak için kontrol ettiğinizde de gerekli olabilir setTimeout(someFn, 0). A setTimeout 0, javascript'te kabul edilen bir tekniktir ve directivedoğru çalışmam zorunluydu .


aynı sorunu buldum. her iki omurga + açısal, bir DOM öğesine uygulanan css özellik değerleri okumak gibi bir şey yapmanız gerekiyorsa, ben zaman aşımı kesmek w / o bir yol bulamadı.
chovy

3

Bu, bildirimsel sözdizimi ve tecrit kapsamını () kullanırken ( Google, en iyi uygulamaları önerdi). Önemli fark Not: 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!");
      }
    }
  };
});

Direktifleri elemanlar olarak attribs olarak kullanmak daha iyi bir fikir olmaz mı? yani kısıtlama: 'A'?
Tejasvi Hegde

2
Mesele, açıklayıcı sözdizimi + tecrit kapsamını göstermekti ... evet, isterseniz bir nitelik yönergesi kullanabilirsiniz. Amacınıza bağlı olarak bir zaman ve yer vardır.
JoshuaDavid

3

Bu şekilde yaptım.

Yönerge oluşturun

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);

Etikete ekledikten sonra bu tekrarlamanın olduğu yere

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

Ve bununla hazır ng-tekrar sonunda çalıştırılacaktır.


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

Benim çözümüm, öğenin tekrarında sonuncuysa bir işlevi çağırmak için bir div eklemekti.


2

önceki cevaplar ngRepeat tamamlandıktan sonra çalıştırmak için gereken kod alır bu durumda yukarıdaki tüm cevaplar büyük ve basit bir çözüm, bazıları diğerlerinden daha genel bir açısal kod olduğu için başka bir cevap eklemek istiyorum, ve eğer önemli bir yaşam döngüsü aşaması olması durumunda Ben Nadel'in bloguna bakabilirsiniz. , $ eval yerine $ parse kullanmak dışında .

ama benim deneyimime göre, OP belirttiği gibi, genellikle derlenmiş fin üzerinde bazı JQuery eklentileri veya yöntemleri çalıştıran DOM, bu durumda ben setTimeout işlevi itildi beri en basit çözüm bir setTimeout ile bir yönerge oluşturmak olduğunu buldum tarayıcı kuyruğunun sonuna kadar, her şey hemen her zaman açısal olarak yapıldıktan hemen sonra, genellikle ngReapet, ebeveynlerinden sonra devam eder

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

bu durumda neden $ timeout kullanmamanızı merak edenler için, tamamen gereksiz olan başka bir sindirim döngüsüne neden olması


1

Ng-yineleme bittikten sonra MathJax kullanarak formüller oluşturmak zorunda kaldım, yukarıdaki cevapların hiçbiri sorunumu çözmedi, bu yüzden aşağıdaki gibi yaptım. O var güzel bir çözüm değildir , ama benim için çalıştı ...

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

0

Burada iyi uygulanmış bir cevap buldum , ancak yine de bir gecikme eklemek gerekiyordu

Aşağıdaki yönergeyi oluşturun:

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

Yineleyicinize bir özellik olarak ekleyin, örneğin:

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

Radu'ya göre:

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

Benim için çalışıyor, ama yine de bir set eklemem gerekiyor

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

-4

Sadece sınıf adını farklı şekilde işlenecek şekilde değiştirmek isterseniz, aşağıdaki kod işe yarayacaktır.

<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>

Bu kod, alışveriş listemdeki alışveriş malzemesini Strick yalak yazı tipinde oluşturmaya benzer bir gereksinimim olduğunda benim için çalıştı.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.