テーブル付きのdivをターゲットにしたjQuery関数を呼び出したい。そのテーブルはng-repeat
で埋められています。
私がそれを呼ぶとき
$(document).ready()
結果が出ません。
また
$scope.$on('$viewContentLoaded', myFunc);
助けにはならない。
Ng-repeatポピュレーションが完了した直後に関数を実行する方法はありますか?私はカスタムdirective
を使うことについてのアドバイスを読みました、しかし私はそれをng-repeatと私のdivと一緒に使う方法の手がかりがありません...
実際には、ディレクティブを使用する必要があり、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
completeイベントを持つためだけにディレクティブを作成する必要はありません。
ng-init
があなたのために魔法をかけます。
<div ng-repeat="thing in things" ng-init="$last && finished()">
$last
は、最後の要素がDOMにレンダリングされたときにのみfinished
が起動されることを確認します。
$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の繰り返し項目の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が最初にレンダリングされた後に一度だけ呼び出す必要がある関数にのみ役立ちます。 ng-repeatの内容が更新されるたびに関数を呼び出す必要がある場合は、このスレッドで他の回答のいずれかをカスタムディレクティブで使用する必要があります。
Pavelの 答え を補足すると、もっと読みやすく理解しやすいものになります。
<ul>
<li ng-repeat="item in items"
ng-init="$last ? doSomething() : angular.noop()">{{item}}</li>
</ul>
他になぜangular.noop
がそもそもあると思いますか?
利点:
このための指令を書く必要はありません...
カスタムディレクティブを必要としない、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>
三項演算子を使用したさらに単純な純粋なAngular JSソリューションがあります。
テンプレート:
<ul>
<li ng-repeat="item in items" ng-init="$last ? doSomething() : null">{{item}}</li>
</ul>
ngInitはリンク前コンパイルフェーズを使用します。つまり、式は子ディレクティブが処理される前に呼び出されます。これは、まだ非同期処理が必要な場合があることを意味します。
トリガーをsetTimeout(someFn, 0)
でラップするためにscope.$last
変数をチェックするときにも必要かもしれません。 setTimeout 0
はjavascriptで認められた手法であり、私のdirective
が正しく動作するためには必須でした。
<div ng-repeat="i in items">
<label>{{i.Name}}</label>
<div ng-if="$last" ng-init="ngRepeatFinished()"></div>
</div>
私の解決策は、項目が繰り返しの最後であった場合に関数を呼び出すためにdivを追加することでした。
宣言構文を使用してスコープを分離するときに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!");
}
}
};
});
前述の答えはngRepeatが完了した後に実行するのに必要なコードは角度付きコードであると考えているので、私は別の答えを追加したいと思います。そして、それが重要なダイジェストライフサイクルの段階である場合、あなたはそれについて Ben Nadelのブログ を見てみることができます。評価します。
しかし私の経験では、OPが述べているように、通常はコンパイルされたDOM上でJQueryプラグインやメソッドを実行しています。ブラウザのキューの最後まで、すべてが角度付きで行われた直後は常に、その親の後に続くngReapet関数
angular.module('myApp', [])
.directive('pluginNameOrWhatever', function() {
return function(scope, element, attrs) {
setTimeout(function doWork(){
//jquery code and plugins
}, 0);
};
});
その場合、なぜ$ timeoutを使用しないのかと疑問に思っている人は誰でも、それはそれが完全に不要な別のダイジェストサイクルを引き起こすということです。
私はこうやった。
ディレクティブを作成する
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の最後に実行されます。
Ng-repeatの終了後にMathJaxを使って数式をレンダリングしなければなりませんでしたが、上記の答えのどれもが私の問題を解決しなかったので、以下のようにしました。それはいい解決策ではありません、しかし私のために働きました...
<div ng-repeat="formula in controller.formulas">
<div>{{formula.string}}</div>
{{$last ? controller.render_formulas() : ""}}
</div>
私は答え をここ でよく練習しているのを見つけました、しかしそれでも遅れを加える必要がありました
次のディレクティブを作成してください。
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); });