2

I need to update the style for a particular element after ng-repeat has finished changing the dom. Here is my challenge. The directive I have written to fire ng-repeat work's just fine when I am adding items to the model, it doesn't get called though when I remove an object from the item.

I have added a function that randomly pushes and pops data from the list. You will see that the pop doesn't trigger the ng-repeat directive call. Anyway to work around that?

var module = angular.module('testApp', [])
.directive('onFinishRender', function ($timeout) {
return {
    restrict: 'A',
    link: function (scope, element, attr) {
        if (scope.$last === true) {
            scope.$evalAsync(attr.onFinishRender);
        }
    }
}
});

Fiddle Link

Shouvik
  • 11,350
  • 16
  • 58
  • 89

5 Answers5

0

You can do this:

<p ng-repeat="t in ta" on-finish-render="test()"
    ng-init="myFunction($last, t)">{{t}}</p>
$scope.myFunction = function(isLast, item){
    if(isLast)
        console.log(item)
}

ng-init is an attribute that runs when the element is created.
$last is a boolean value that is true if the current element is the last element in an ng-repeat.

Updated Fiddle

Cerbrus
  • 70,800
  • 18
  • 132
  • 147
  • I did a quick check, `ng-init` is not getting fired when I remove an element. That is essentially the event I need to update the dom height. – Shouvik Nov 27 '15 at 13:50
  • Hm, that may be because the existing elements aren't being updated. I'm not quite sure how to fix that. – Cerbrus Nov 27 '15 at 13:51
  • Yep. the directive works perfectly fine as long as I am adding things to the array. It's the removal that I need help with – Shouvik Nov 27 '15 at 13:52
0

Why not use the $watch?

$scope.$watch('ta', function (newVal, oldValue) {
    if(oldValue.length > 0)
    {
        var i = 0; // do something
    }
}, true);

This way you can control when you either push or pop items to the array.

http://jsfiddle.net/W8nhv/114/

Gen4ik
  • 360
  • 1
  • 8
  • `if(oldValue.length !== newValue.length)`, then this should work. – Cerbrus Nov 27 '15 at 13:57
  • I actually need the dom element's height after the ng-repeat has finished it's operation. This will be triggered before the dom operation is over. – Shouvik Nov 27 '15 at 14:02
  • You want the height of the entire list after the list item has been removed? – Gen4ik Nov 27 '15 at 14:05
  • @Gen4ik yep, the parent element's height. Also I have this ng-repeat in another ng-repeat. Therefore, I am not sure of the object's name that I could watch individual ones for change. Essentially looking for not a very dirty way to achieve this. – Shouvik Nov 27 '15 at 14:07
0

Well, you add an element to the array. Therefore, it surely triggers its own link() function when being rendered. Just keep in mind that you create a onFinishRender directive for each element in your array with its own scope.

What do you want to do after the array length has changed? Maybe a directive is not the right attempt.

DonJuwe
  • 4,477
  • 3
  • 34
  • 59
0

Simple and effective way

<li ng-repeat="data in dataList" ng-init="$last && $parent.myOnFinishHandler();" ></li>

$parent pointing to the scope where datalist is stored

angular.module("abc", []).controller('mc', function($scope) {
  $scope.dataList = ["abc", "123", "xyz", "orange", "apple"];
  $scope.myHandler = function() {
    console.log("render finish ");
  }

});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="abc">
  <ul ng-controller="mc">
    <li ng-repeat="data in dataList" ng-init="$last && $parent.myHandler();"> {{data}} </li>
  </ul>
</div>
Sajan
  • 813
  • 9
  • 14
-1

I found the solution hunting around in one of the other solutions.

Turns out there is no good way to achieve this functionality. I have written a filter which essentially does what I want it to do. The basics are:

app.filter('ngRepeatFinish', function($timeout){
    return function(data, scope){
        //var scope = this;
        var flagProperty = '__finishedRendering__';
        if(!data[flagProperty]){
            Object.defineProperty(
                data, 
                flagProperty, 
                {enumerable:false, configurable:true, writable: false, value:{}});
            $timeout(function(){
                    delete data[flagProperty];                        
                    scope.$emit('ngRepeatFinished');
                },0,false);                
        }
        return data;
    };
})

The above filter broadcasts for every element that is created and I get to run my watch based on the listening event in the controller.

$scope.$on('ngRepeatFinished', function() {
    //Work thy magic
})

To call this directive I have to pass the scope from the HTML and I do that via, this in front of the ngRepeatFinish.

<div ng-repeat="item in (items | ngRepeatFinish:this)"></div>
Community
  • 1
  • 1
Shouvik
  • 11,350
  • 16
  • 58
  • 89