回答:
実際、ディレクティブを使用する必要があり、ng-Repeatループの終わりに関連付けられたイベントはありません(各要素は個別に作成され、独自のイベントを持っているため)。ただし、a)ディレクティブを使用するだけで十分な場合もあり、b)「on ngRepeat finished」イベントを作成するために使用できるng-Repeat固有のプロパティがいくつかあります。
具体的には、イベントをテーブル全体にスタイル/追加することだけが必要な場合は、すべての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の実際の動作をご覧ください。それが役に立てば幸い!
ループの最後でコードを実行するだけの場合は、追加のイベント処理を必要としない、もう少し簡単なバリエーションを次に示します。
<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');
}
};
});
特にng-repeat
完全なイベントを持つためだけにディレクティブを作成する必要はありません。
ng-init
あなたのための魔法をします。
<div ng-repeat="thing in things" ng-init="$last && finished()">
これ$last
によりfinished
、最後の要素がDOMにレンダリングされたときにのみ発生します。
$scope.finished
イベントを作成することを忘れないでください。
ハッピーコーディング!!
編集:2016年10月23日
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()">
以下は、trueのときに指定された関数を呼び出すrepeat-doneディレクティブです。呼び出された関数は、レンダリングされた要素のツールチップの初期化などのDOM操作を行う前に、interval = 0の$ timeoutを使用する必要があることを発見しました。jsFiddle:http ://jsfiddle.net/tQw6w/
$ scope.layoutDoneで、$ timeout行をコメント化し、「NOT CORRECT!」のコメントを外してみてください。行でツールチップの違いを確認します。
<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...
}
})
ng-init
カスタムディレクティブを必要としない簡単なアプローチを次に示します。これは、ページの読み込み時にng-repeatedアイテムのdivを特定のアイテムに自動スクロールする必要がある場合など、特定のシナリオでうまく機能します。そのため、スクロール機能は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-repeatが最初にレンダリングされた後に1回呼び出す必要がある関数に対してのみ役立つことに注意してください。ng-repeatの内容が更新されるたびに関数を呼び出す必要がある場合は、このスレッドでカスタムディレクティブを使用して他の回答のいずれかを使用する必要があります。
Pavelの回答を補足すると、より読みやすく、簡単に理解できるものは次のようになります。
<ul>
<li ng-repeat="item in items"
ng-init="$last ? doSomething() : angular.noop()">{{item}}</li>
</ul>
angular.noop
そもそもなぜ他にあると思う?
利点:
このためのディレクティブを作成する必要はありません...
ng-init="$last && doSomething( )"
おそらく、カスタムディレクティブを必要としないngInit
Lodashのdebounce
メソッドを使用した、もう少し簡単な方法です。
コントローラ:
$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はリンク前のコンパイルフェーズを使用することに注意してください。つまり、子ディレクティブが処理される前に式が呼び出されます。つまり、依然として非同期処理が必要になる場合があります。
ng-init="$last ? refresh() : false"
でも機能します。そして、それはLodashを必要としません。
これは、宣言構文を使用してスコープを分離するときにngRepeatプロパティ($ index、$ first、$ middle、$ last、$ even、$ odd)にアクセスする方法を示すために、他の回答で表現されたアイデアの改善です。 Googleはベストプラクティスを推奨しています)をelement-directiveとともに使用します。主な違いに注意してください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!");
}
}
};
});
私はこのようにしました。
ディレクティブを作成する
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の最後に準備ができて実行されます。
上記の回答は、ngRepeatの実行後に実行する必要があるコードが角度コードであることを前提としているため、別の回答を追加したいと思います。この場合、上記のすべての回答は、他よりも一般的で優れたシンプルなソリューションを提供します。そして、重要なダイジェストライフサイクルステージの場合は、$ evalの代わりに$ parseを使用することを除いて、Ben Nadelのブログをご覧ください。
しかし、私の経験では、OPが述べているように、通常は最終的にコンパイルされたDOMでいくつかのJQueryプラグインまたはメソッドを実行します。この場合、setTimeout関数がプッシュされるため、最も簡単な解決策はsetTimeoutでディレクティブを作成することです。ブラウザのキューの最後まで、常にすべてが角度で実行された直後、通常は親のpostLinking関数の後に続くngReapet
angular.module('myApp', [])
.directive('pluginNameOrWhatever', function() {
return function(scope, element, attrs) {
setTimeout(function doWork(){
//jquery code and plugins
}, 0);
};
});
その場合$ timeoutを使用しないのはなぜだろうと思っている人にとっては、それが完全に不要な別のダイジェストサイクルを引き起こすということです。
私はここでよく練習された答えを見つけましたが、それでも遅延を追加する必要がありました
次のディレクティブを作成します。
angular.module('MyApp').directive('emitLastRepeaterElement', function() {
return function(scope) {
if (scope.$last){
scope.$emit('LastRepeaterElement');
}
}; });
次のように、属性としてリピーターに追加します。
<div ng-repeat="item in items" emit-last-repeater-element></div>
ラドゥによると、
$scope.eventoSelecionado.internamento_evolucoes.forEach(ie => {mycode});
私にとってはうまくいきますが、それでもsetTimeoutを追加する必要があります
$scope.eventoSelecionado.internamento_evolucoes.forEach(ie => {
setTimeout(function() {
mycode
}, 100); });
別にレンダリングされるようにクラス名を変更したいだけの場合は、以下のコードでうまくいきます。
<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 troughフォントでレンダリングするという同様の要件がある場合に役立ちました。
myMainDirective
のコードでは、ループの終了後にのみ実行しますか?親divのスクロールバーを更新する必要があります。