ng-ulangi acara selesai

207

Saya ingin memanggil beberapa fungsi penargetan jQuery div dengan tabel. Tabel itu diisi dengan ng-repeat.

Ketika saya menyebutnya

$(document).ready()

Saya tidak punya hasil.

Juga

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

tidak membantu.

Apakah ada cara untuk menjalankan fungsi setelah populasi ng-repeat selesai? Saya sudah membaca saran tentang penggunaan custom directive, tapi saya tidak tahu bagaimana menggunakannya dengan ng-repeat dan div saya ...

ChruS
sumber

Jawaban:

241

Memang, Anda harus menggunakan arahan, dan tidak ada peristiwa yang terikat pada akhir loop Ulangi (karena setiap elemen dibangun secara individual, dan memiliki acara itu sendiri). Tetapi a) menggunakan arahan yang mungkin Anda butuhkan dan b) ada beberapa sifat spesifik yang dapat Anda gunakan untuk membuat acara "saat ngRepeat selesai".

Khususnya, jika semua yang Anda inginkan adalah gaya / menambahkan acara ke seluruh tabel, Anda dapat melakukannya dengan menggunakan arahan yang mencakup semua elemen ngRepeat. Di sisi lain, jika Anda ingin membahas setiap elemen secara khusus, Anda dapat menggunakan arahan dalam ngRepeat, dan itu akan bertindak pada setiap elemen, setelah itu dibuat.

Lalu, ada $index, $first, $middledan $lastsifat dapat Anda gunakan untuk acara pemicu. Jadi untuk HTML ini:

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

Anda dapat menggunakan arahan seperti:

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

Lihat beraksi di Plunker ini . Semoga ini bisa membantu!

Tiago Roldão
sumber
Bisakah saya membuat myMainDirectivekode dieksekusi hanya setelah akhir dari loop? Saya perlu memperbarui scrollbar div induk.
ChruS
2
Tentu saja! Cukup ubah "window.alert" menjadi fungsi firing acara, dan tangkap dengan arahan utama. Saya memperbarui de Plunker untuk melakukan ini, sebagai contoh.
Tiago Roldão
9
Disarankan untuk memasukkan perpustakaan jQuery terlebih dahulu (sebelum Angular). Ini akan menyebabkan semua elemen Angular dibungkus dengan jQuery (bukan jqLite). Kemudian alih-alih angular.element (elemen) .css () dan $ (elemen) .children (). Css () Anda cukup menulis element.css () dan element.children (). Css (). Lihat juga groups.google.com/d/msg/angular/6A3Skwm59Z4/oJ0WhKGAFK0J
Mark Rajcok
11
Adakah yang punya ide bagaimana membuatnya bekerja untuk render berikutnya pada arahan ngRepeat? yaitu: tautan
RavenHursT
1
Ini adalah konvensi penamaan untuk semua arahan sudut. Lihat docs.angularjs.org/guide/directive#normalisasi
Tiago Roldão
68

Jika Anda hanya ingin mengeksekusi beberapa kode pada akhir loop, inilah variasi yang sedikit lebih sederhana yang tidak memerlukan penanganan peristiwa tambahan:

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

Lihat demo langsung.

karlgold
sumber
Apakah ini berfungsi jika saya menggunakan controller sebagai sintaks? Silahkan? Tidak, apakah ada cara lain yang bekerja dengan controller?
Dan Nguyen
63

Tidak perlu membuat arahan terutama hanya untuk memiliki ng-repeatacara yang lengkap.

ng-init melakukan keajaiban untuk Anda.

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

yang $lastmemastikan, bahwafinished hanya dipecat, ketika elemen terakhir telah diberikan kepada DOM.

Jangan lupa buat $scope.finished acara.

Selamat Coding !!

EDIT: 23 Okt 2016

Jika Anda juga ingin memanggil finishedfungsi ketika tidak ada item dalam array maka Anda dapat menggunakan solusi berikut

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

Cukup tambahkan baris di atas di bagian atas ng-repeat elemen. Ini akan memeriksa apakah array tidak memiliki nilai dan memanggil fungsi yang sesuai.

Misalnya

<div ng-if="things.length > 0" ng-init="finished()"></div>
<div ng-repeat="thing in things" ng-init="$last && finished()">
Vikas Bansal
sumber
Bagaimana jika "sesuatu" berubah menjadi array kosong?
Frane Poljak
1
@FranPoljak Saya telah menambahkan solusi dalam jawabannya.
Vikas Bansal
1
Dalam kasus array kosong, tangani di controller
JSAddict
Perbaiki jawaban Vikas Bansal: // gunakan div pertama jika Anda ingin memeriksa apakah array tidak memiliki nilai dan panggil fungsi sesuai. <div ng-if = "! things.length" ng-hide = "true" ng-init = "selesai ()"> </div> <div ng-repeat = "barang dalam hal" ng-init = "$ last && finished () ">
Alex Teslenko
41

Berikut adalah arahan yang dilakukan berulang yang memanggil fungsi yang ditentukan bila benar. Saya telah menemukan bahwa fungsi yang dipanggil harus menggunakan $ timeout dengan interval = 0 sebelum melakukan manipulasi DOM, seperti menginisialisasi tooltips pada elemen yang diberikan. jsFiddle: http://jsfiddle.net/tQw6w/

Di $ scope.layoutDone, coba komentari baris $ timeout dan batalkan komentar pada "NOT CORRECT!" baris untuk melihat perbedaan pada tooltips.

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

    })
Joseph Oster
sumber
Saya tidak ingin menggunakan batas waktu $, tetapi ketika Anda mengatakan itu bisa diatur ke 0, saya memutuskan untuk mencobanya dan itu bekerja dengan baik. Saya ingin tahu apakah ini masalah pencernaan, dan apakah ada cara untuk melakukan apa yang dilakukan $ timeout tanpa menggunakan $ timeout.
Jameela Huq
Bisakah Anda jelaskan mengapa ini bekerja? Saya mencoba mengakses ID dinamis dan hanya berfungsi setelah batas waktu 0 penundaan. Saya melihat solusi "peretasan" ini di beberapa posting tetapi tidak ada yang menjelaskan mengapa ia bekerja.
Jin
30

Berikut adalah pendekatan sederhana menggunakan ng-inityang bahkan tidak memerlukan arahan khusus. Ini bekerja dengan baik untuk saya dalam skenario tertentu misalnya perlu menggulir otomatis div item yang diulang ke item tertentu pada pemuatan halaman, jadi fungsi gulir harus menunggu sampai ng-repeatselesai selesai rendering ke DOM sebelum dapat diaktifkan.

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

    };
});

Perhatikan bahwa ini hanya berguna untuk fungsi-fungsi yang perlu Anda panggil satu kali setelah ng-repeat merender pada awalnya. Jika Anda perlu memanggil fungsi setiap kali konten ng-repeat diperbarui maka Anda harus menggunakan salah satu jawaban lain di utas ini dengan arahan khusus.

dshap
sumber
1
Cantik, ini berhasil. Saya ingin memilih secara otomatis teks dalam kotak teks, dan batas waktu melakukan trik. Jika tidak, teks {{model.value}} bisa dipilih dan kemudian tidak dipilih ketika model.value yang terikat data disuntikkan.
JoshGough
27

Melengkapi jawaban Pavel , sesuatu yang lebih mudah dibaca dan mudah dimengerti adalah:

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

Kenapa lagi menurutmu angular.noopada di tempat pertama ...?

Keuntungan:

Anda tidak perlu menulis arahan untuk ini ...

deostroll
sumber
20
Mengapa menggunakan terner ketika Anda dapat menggunakan logika boolean:ng-init="$last && doSomething( )"
Nate Whittaker
@NateWhittaker lebih mudah dibaca (dan lebih sulit dibaca daripada operator ternary sangat mengesankan). Sangat baik memiliki kode yang bersih dan mudah dipahami
Justin
21

Mungkin pendekatan yang sedikit lebih sederhana dengan ngInitdan Lodashdebounce metode tanpa perlu arahan khusus:

Pengendali:

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

Templat:

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

Memperbarui

Bahkan ada solusi AngularJS murni yang lebih sederhana menggunakan operator ternary:

Templat:

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

Ketahuilah bahwa ngInit menggunakan fase kompilasi pranala - yaitu ekspresi dipanggil sebelum arahan anak diproses. Ini berarti bahwa masih diperlukan pemrosesan asinkron.

Pavel Horal
sumber
Harus dicatat bahwa Anda memerlukan lodash.js agar ini berfungsi :) Juga: bagian ", 20" dari $ scope.refresh =, apakah jumlah milis sebelum metode ini dipanggil setelah iterasi terakhir?
Jørgen Skår Fischer
Ditambahkan Lodash di depan menghilangkan bounce link di. Ya, 20 adalah penundaan sebelum metode dijalankan - diubah menjadi 0 karena tidak masalah selama panggilannya async.
Pavel Horal
1
Juga memikirkan hal ini, karena operator ternary diperbolehkan dalam ekspresi sederhana juga ng-init="$last ? refresh() : false"akan berfungsi. Dan itu bahkan tidak memerlukan Lodash.
Pavel Horal
<div ng-repeat = "i in [1,2,4]" ng-init = "doSomething ($ last)"> </div> $ scope.doSomething = fungsi (lastElement) {if (lastElem) bla bla bla }
JSAddict
4

Mungkin juga diperlukan saat Anda memeriksa scope.$lastvariabel untuk membungkus pemicu Anda dengan a setTimeout(someFn, 0). A setTimeout 0adalah teknik yang diterima dalam javascript dan itu penting bagi saya directiveuntuk berjalan dengan benar.

Cody
sumber
saya menemukan masalah yang sama. baik di backbone + angular, jika Anda perlu melakukan sesuatu seperti membaca nilai properti css yang diterapkan pada elemen DOM, saya tidak bisa menemukan cara w / o hack timeout.
chovy
3

Ini merupakan peningkatan dari ide-ide yang diekspresikan dalam jawaban lain untuk menunjukkan cara mendapatkan akses ke properti ngRepeat ($ index, $ first, $ middle, $ last, $ even, $ odd) ketika menggunakan sintaks deklaratif dan ruang lingkup isolasi ( Google merekomendasikan praktik terbaik) dengan arahan elemen. Perhatikan perbedaan utama: 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!");
      }
    }
  };
});
JoshuaDavid
sumber
Bukankah lebih baik menggunakan arahan sebagai atribut daripada elemen? yaitu membatasi: 'A'?
Tejasvi Hegde
2
Intinya adalah untuk menunjukkan sintaks deklaratif + ruang lingkup isolat ... ya, Anda dipersilakan untuk menggunakan atribut direktif di sini jika Anda inginkan. Ada waktu dan tempat untuk tergantung pada tujuan Anda.
JoshuaDavid
3

Saya melakukannya dengan cara ini.

Buat arahan

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

Setelah Anda menambahkannya pada label tempat ini ng-repeat

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

Dan siap dengan yang akan dijalankan pada akhir ng-repeat.

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

Solusi saya adalah menambahkan div untuk memanggil fungsi jika item tersebut adalah yang terakhir dalam pengulangan.

scotty3
sumber
2

saya ingin menambahkan jawaban lain, karena jawaban sebelumnya menganggap bahwa kode yang diperlukan untuk menjalankan setelah ngRepeat dilakukan adalah kode sudut, yang dalam hal ini semua jawaban di atas memberikan solusi yang bagus dan sederhana, beberapa lebih umum daripada yang lain, dan jika penting tahap siklus hidup intisari Anda dapat melihat blog Ben Nadel tentang hal itu, dengan pengecualian menggunakan $ parse bukannya $ eval.

tetapi dalam pengalaman saya, seperti yang dinyatakan OP, biasanya menjalankan beberapa plugin JQuery atau metode pada DOM yang dikompilasi secara finnaly, yang dalam hal ini saya menemukan bahwa solusi paling sederhana adalah membuat arahan dengan setTimeout, karena fungsi setTimeout akan didorong ke akhir antrian browser, selalu tepat setelah semuanya dilakukan dalam sudut, biasanya ngReapet yang berlanjut setelah orang tuanya fungsi postLinking

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

untuk siapa pun yang bertanya-tanya dalam hal itu mengapa tidak menggunakan $ timeout, itu yang menyebabkan siklus intisari yang sama sekali tidak perlu

bresleveloper
sumber
1

Saya harus membuat rumus menggunakan MathJax setelah ng-repeat berakhir, tidak ada jawaban di atas yang memecahkan masalah saya, jadi saya buat seperti di bawah ini. Ini bukan solusi yang bagus , tetapi berhasil untuk saya ...

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

Saya menemukan jawaban di sini dipraktekkan dengan baik, tetapi masih perlu menambahkan penundaan

Buat arahan berikut:

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

Tambahkan ke pengulang Anda sebagai atribut, seperti ini:

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

Menurut Radu ,:

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

Bagi saya ini berfungsi, tetapi saya masih perlu menambahkan setTimeout

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

Jika Anda hanya ingin mengubah nama kelas sehingga akan diberikan secara berbeda, kode di bawah ini akan membantu.

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

Kode ini berfungsi untuk saya ketika saya memiliki persyaratan yang sama untuk membuat barang belanjaan dalam daftar belanja saya dalam font Strick trough.

pengguna4808836
sumber