AngularJS 1.5+下ng-repeat数据更新完成后触发脚本的解决方案需求
I get it—your current directive works on initial load, but when you update data via Ajax and ng-repeat re-renders, it doesn’t fire. The issue is likely tied to how ng-repeat reuses elements (especially if you’re using track by), which means the $last value doesn’t change for existing elements, so your watcher never triggers again.
Here’s a robust fix that works even when elements are reused or the collection is updated in place:
Step 1: Update the Directive to Watch the Collection
Instead of watching just $last, we’ll watch the entire collection that ng-repeat is iterating over. This way, any change to the collection (new items, updated items, or removed items) will trigger our script after the DOM finishes rendering.
Modified Directive Code
.directive('onFinishRender', ['$timeout', function ($timeout) { return { restrict: 'A', scope: { // Bind the collection we want to watch to the directive targetCollection: '=onFinishRender' }, link: function (scope, element, attr) { var pendingTimeout; // Watch for changes to the collection scope.$watch('targetCollection', function () { // Cancel any pending timeout to avoid duplicate triggers if (pendingTimeout) { $timeout.cancel(pendingTimeout); } // Wait until Angular finishes updating the DOM pendingTimeout = $timeout(function () { // Replace this with your script loading logic console.log('ng-repeat has finished rendering all elements!'); // Example: Load a dynamic script // const script = document.createElement('script'); // script.src = '/path/to/your/script.js'; // script.onload = () => console.log('Script loaded successfully'); // document.body.appendChild(script); }); }, true); // Use true for deep watching (catches updates to individual items) // Clean up the timeout when the scope is destroyed scope.$on('$destroy', function () { if (pendingTimeout) { $timeout.cancel(pendingTimeout); } }); } }; }]);
Step 2: Update Your HTML
Move the directive to the parent container of your ng-repeat element, and pass the collection you’re iterating over:
<div class="things-container" on-finish-render="things"> <div class="thing" ng-repeat="thing in things track by thing.id"> thing {{thing}} </div> </div>
Key Improvements Over Your Original Directive
- Watches the Entire Collection: Detects any change to the collection—whether you replace it entirely (common in Ajax responses) or update individual items.
- Debounces Triggers: Cancels pending timeouts if the collection changes rapidly, so your script only runs once after the data stabilizes.
- Works with
track by: Even when elements are reused, the directive will trigger because it’s watching the collection itself, not just the$lastflag. - Cleanup: Properly cancels timeouts when the scope is destroyed to prevent memory leaks.
Performance Note
The true argument in $watch enables deep watching, which is necessary if you update individual items in the collection instead of replacing it. If you always replace the entire collection (e.g., $scope.things = response.data), you can remove the true to make the watcher more performant (it will only watch for reference changes instead of deep checking every item).
内容的提问来源于stack exchange,提问作者John Peter

