I'm trying to write a very simple 3 star scoring system in Angular, however I have run into issues with directive scopes when using a reference to an item in an array.
For testing purposes my markup looks like this:
<div id="ref">
<span>Reference to item 1: </span>
<item-score ng-model="selected.score"></item-score>
</div>
<div ng-repeat="item in items track by $index">
<div class="row">
<span>{{item.name}}:</span>
<item-score ng-model="item.score"></item-score>
<item-score ng-model="item.score"></item-score>
</div>
</div>
And my JavaScript has been simplified but does the same thing:
var App = angular.module('testApp', []);
App.controller('MainCtrl', ['$scope', function ($scope) {
$scope.items = [
{ id: 1, score: 1, name: 'Item 1' },
{ id: 2, score: 2, name: 'Item 2' },
{ id: 3, score: 1, name: 'Item 3' }
];
$scope.selected = $scope.items[1];
}]);
App.directive('itemScore', function () {
return {
restrict: 'E',
require: '?ngModel',
replace: true,
template: '<div class="score"><i class="fa fa-star" ng-click="set($index+1)"' +
' ng-repeat="star in stars track by $index" ' +
'ng-class="{ yellow: star === true }"></i></div>',
link: function (scope, elm, attrs, ctrl) {
var num = 5;
scope.stars = new Array(num);
scope.set = function (score) {
if (ctrl.$viewValue === score) { score--; }
ctrl.$setViewValue(score);
};
function setStars () {
for (var i = 0; i < num; i += 1) {
scope.stars[i] = ((i+1) <= ctrl.$viewValue ? true : false);
}
}
ctrl.$render = function () {
setStars();
};
}
}
});
I have created a plunker http://plnkr.co/edit/QIXc1Nw68q7Zt1gsoa2P?p=preview
when clicking in the rows each score box will update correctly, but when you click the stars above the the rows (uses a reference to the 2nd item in the array) it will update both directives in the row, but will not update itself.
I need the directive to work with a reference as the item is passed into another directive to use inside a modal window (my app has the same behaviour as the plunker).
Any help would be appreciated thanks in advance.
When you click in the score boxes inside each row, both are update correctly because both directives are watching the same property. So when one of the directives changes the number of starts the other one is notified of that change and forces a render. Since both directives are inside the same ng-repeat
scope, both score boxes are re-rendered.
Try to remove one of the directives from the row inside the ng-repeat
and you will notice it will no longer update correctly.
One way to fix it is to call ctrl.$render()
after setting the view value. Like this:
scope.set = function (score) {
if (ctrl.$viewValue === score) { score--; }
ctrl.$setViewValue(score);
ctrl.$render();
};
The other option is to define a ng-model
reference in the scope
and update it directly.
require : 'ngModel',
scope: {
ngModel: '=?'
}
And then:
scope.set = function (score) {
scope.ngModel = score;
};
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments