Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

custom directive inside ng-repeat doesn't update bound values correctly when using track by $index

I'm implementing a custom grid that applies formatting for certain columns. When you click the "Toggle Sort" link, the second column does not track alongside the first column. However, removing "track by $index" fixes it. At first I understood that binding primitive data was the problem but that's not the case.

<div ng-app="testApp">
    <div ng-controller="MyCtrl">
        <h3>With Track By</h3>
        <div ng-repeat="row in (filteredRows=(rows | orderBy:sort.column:sort.descending)) track by $index">
            <span>{{ filteredRows[$index].s }}</span><fmt data-model="filteredRows[$index]"></fmt>
        </div>
        <br/>
        <a href="#" ng-click="sort.descending = !sort.descending">Toggle Sort</a>
    </div>
</div>

angular.module('testApp', [])
.controller('MyCtrl', function($scope) {
    $scope.rows = [ 
        { v: 1.2345, s: 'foo' },
        { v: 0.1, s: 'bar' }, 
        { v: -3.33333, s: 'baz' }, 
        { v: 10, s: 'rar' } 
    ];
    $scope.sort = { column: 'v', descending: false };
})
.directive('fmt', function() {
    return {
        restrict: 'AE',
        scope: {
            model: '='
        },
        template: '<span style="display:inline-block;width:100px;text-align:right;">{{ model.v }}</span>' +
            '<span style="display:inline-block;width:100px;text-align:right;">{{ fmtModel }}</span>',
        controller: function($scope) {
            $scope.fmtModel = $scope.model.v.toFixed(2);
        }
    };
})

The issue that is demonstrated in this fiddle: http://jsfiddle.net/4Hs36/5/

This is a workaround that seems to work: http://jsfiddle.net/4Hs36/6/. Here I'm attaching the formatted value directly to the model but I'm not so sure whether this is a good idea.

Hoping someone can shed more light on what's going on with "track by $index"?

like image 227
Hackmad Avatar asked Mar 16 '26 12:03

Hackmad


1 Answers

There is something I didn't quite understand about scopes. I had the impression you could attach a property to $scope and it would update automatically when the bound value changes.

I updated the fiddle in the original post with this http://jsfiddle.net/4Hs36/7/

controller: function($scope) {
  $scope.fmtModel = function() { 
    return $scope.model.v.toFixed(2);
  }
}

I guess I could add a $watch and fix it that way: http://jsfiddle.net/4Hs36/8/

controller: function($scope) {
  $scope.$watch('model.v', function() {
    $scope.fmtModel = $scope.model.v.toFixed(2);
  });
}
like image 190
Hackmad Avatar answered Mar 18 '26 02:03

Hackmad



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!