There are tons of posts showing how to have callback functions based on a directive so that when an ng-repeat function is finished you can wait and then call a function. For example here is my example.
<div ng-repeat="Object in Objects" class="objectClass" on-finish-render>{{Object.Overlay}</div>
Then when it is pleted the following calls my function
.directive('onFinishRender', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
$timeout(function () {
scope.$emit('ngRepeatFinished');
}, 0);
}
}
}
});
This successfully calls my function below when it is pleted
$scope.$on('ngRepeatFinished', function(ngRepeatFinishedEvent) {
//my code goes here
}
Now all of that works perfectly and as intended the first time I set my $scope.Objects variable. My code will wait until all objects are fully rendered and then runs, literally perfect. However if after the initial everything I change $scope.Objects again, my function will still run but it will NOT wait for the pletion. Its actually visible with console logs, the first go round it will pause for about half a second after I go into the directive but before the actual emit, but on subsequent changes to my ng-repeat it does not pause and simply calls my function before the dom is finished rendering.
This is super annoying and any help would be great!
There are tons of posts showing how to have callback functions based on a directive so that when an ng-repeat function is finished you can wait and then call a function. For example here is my example.
<div ng-repeat="Object in Objects" class="objectClass" on-finish-render>{{Object.Overlay}</div>
Then when it is pleted the following calls my function
.directive('onFinishRender', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
$timeout(function () {
scope.$emit('ngRepeatFinished');
}, 0);
}
}
}
});
This successfully calls my function below when it is pleted
$scope.$on('ngRepeatFinished', function(ngRepeatFinishedEvent) {
//my code goes here
}
Now all of that works perfectly and as intended the first time I set my $scope.Objects variable. My code will wait until all objects are fully rendered and then runs, literally perfect. However if after the initial everything I change $scope.Objects again, my function will still run but it will NOT wait for the pletion. Its actually visible with console logs, the first go round it will pause for about half a second after I go into the directive but before the actual emit, but on subsequent changes to my ng-repeat it does not pause and simply calls my function before the dom is finished rendering.
This is super annoying and any help would be great!
Share Improve this question asked Feb 2, 2016 at 2:14 CMOSCMOS 2,9178 gold badges49 silver badges86 bronze badges 6-
Hi @Shohel Just wondering, in this senario, what is the difference between
promise
and$emit
? – Tyler.z.yang Commented Feb 2, 2016 at 2:27 -
Hi @Calvin Moss , you should notice every time you change something in $scope.Objects it will trigger ng-repeat to rerender. And then you directive
onFinishRender
emit an event. Do you want to emit the event only in the first time or? – Tyler.z.yang Commented Feb 2, 2016 at 2:29 - Right, that is working, what is not working is the timeout. My function is running AFTER the pleted render on the first run, and BEFORE on subsequent runs. I want the function to run EVERY SINGLE TIME I edit the $scope.Objects, and I want it to wait to fully render and run after, every single time. – CMOS Commented Feb 2, 2016 at 2:31
- How do you know it calls your function before the dom is finished rendering? Can you post some runable code to jsfiddle or somewhere else so that I can debug? – Tyler.z.yang Commented Feb 2, 2016 at 2:36
- I can try and make a jsfiddle, I know it is happening because I am getting errors in my function after the first run. However if I set a very small delay on the $timeout function, say 100 ms, it works fine. I know this is because of the delay, I will work on a jsfiddle. – CMOS Commented Feb 2, 2016 at 2:38
2 Answers
Reset to default 3Angular’s $emit, $broadcast and $on fall under the mon “publish/subscribe” design pattern, or can do, in which you’d publish an event and subscribe/unsubscribe to it somewhere else. The Angular event system is brilliant, it makes things flawless and easy to do (as you’d expect!) but the concept behind it isn’t so simple to master and you can often be left wondering why things don’t work as you thought they might.
Using $scope.$emit will fire an event up the $scope. Using $scope.$broadcast will fire an event down the $scope. Using $scope.$on is how we listen for these events.
(reference)
I have provided two solution according to your problem.
Solution One
<div ng-controller="MyCtrl">
<div ng-repeat="Object in Objects" class="objectClass" on-finish-render isolated-expression-foo="updateItem(item,temp)">{{Object|json}</div>
</div>
var app = angular.module('app', []);
app.directive('onFinishRender', function () {
return {
restrict: 'A',
scope: {
isolatedExpressionFoo: '&'
},
link: function (scope, element, attr) {
if (scope.$parent.$last) {
scope.isolatedExpressionFoo({ temp: "some value" });
}
}
};
});
app.controller('MyCtrl', ['$scope', function ($scope) {
$scope.Objects = [{ id: 1, value: "test" }, { id: 2, value: "TEst2" }];
$scope.updateItem = function (item, temp) {
console.log("Item param " + item.id);
console.log("temp param " + temp);
}
}]);
Solution Two
<div ng-controller="MyCtrl">
<div ng-repeat="Object in Objects" class="objectClass" on-finish-render>{{Object|json}</div>
</div>
var app = angular.module('app', []);
app.directive('onFinishRender', function ($rootScope) {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last) {
$rootScope.$broadcast("ngRepeatFinished", { temp: "some value" });
}
}
};
});
app.controller('MyCtrl', ['$scope', function ($scope) {
$scope.Objects = [{ id: 1, value: "test" }, { id: 2, value: "TEst2" }];
$scope.$on('ngRepeatFinished', function (temp) {
console.log("Item param " + temp);
});
}]);
One thing you should understand about ng-repeat
is that it reuses DOM elements whenever possible, so if you have two objects in your repeater and you add a third, the first two elements will not be re-rendered. Only the third will be rendered, thus your directive's link function will only be called once for the newly-added object.
Similarly, if you remove an item, your directive's link function will not be run again.
Observe the behavior in this JSFiddle.