Angular noobie here. I would like to know what is the best way to change the dom when a value in the scope changes by some means. I read that its not good to put the dom man
Thank for posting the answer above. I've noticed that the animation will flicker if value changes frequently and timeout will be fired while another animation is already active.
I've fixed this by resetting the timeout if the timeout is already set.
Also, I've added code to check if value is increasing or decreasing and set different css class.
app.directive('newvalue', ['$timeout', function($timeout) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
element.addClass('newvalue');
scope.$watch(attrs.newvalue, function (nv, ov) {
function settimeout() {
attrs.timeout = $timeout(function () {
element.removeClass('newvalue-up');
element.removeClass('newvalue-down');
attrs.timeout = null;
}, 1000);
}
if (nv !== ov) {
if(attrs.timeout) {
//newvalue already set.. reset timeout
$timeout.cancel(attrs.timeout);
settimeout();
} else {
if(nv > ov) {
element.addClass('newvalue-up');
} else {
element.addClass('newvalue-down');
}
settimeout();
}
}
});
}
};
}]);
I think it would be better to observer a concrete value per highlighter instead of watching the whole collection. E.g.:
<td highlighter="person.firstName">{{ person.firstName }}</td>
This way, the highlighter
-directive could be very simple, like:
app.directive('highlighter', ['$timeout', function($timeout) {
return {
restrict: 'A',
scope: {
model: '=highlighter'
},
link: function(scope, element) {
scope.$watch('model', function (nv, ov) {
if (nv !== ov) {
// apply class
element.addClass('highlight');
// auto remove after some delay
$timeout(function () {
element.removeClass('highlight');
}, 1000);
}
});
}
};
}]);
Though, for this to work you'll have to tell angular that the data actually changed. Currently this is not the case as angular tracks people
by object-identity. The moment you overwrite it, angular will remove all associated dom-elements. To accomodate for this, use:
ng-repeat="person in people track by $index"
which will tell angular to treat the index of the array as identity.
demo: http://jsbin.com/vutevifadi/1/
After some reading I noticed there are some doubts about using $watch
, considering performance. I found another solution using $observe
.
A good read on $watch
and $observe
: https://stackoverflow.com/a/14907826/2901207
javascript:
var app = angular.module('angularjs-starter', []);
app.directive('highlightOnChange', function() {
return {
link : function(scope, element, attrs) {
attrs.$observe( 'highlightOnChange', function ( val ) {
console.log("Highlighting", val);
element.effect('highlight');
});
}
};
});
app.controller('myController', function($scope, $timeout) {
$scope.val = 1;
$scope.updateVal = function() {
$scope.val = $scope.val + 1;
};
});
html:
<body ng-controller="myController">
<div highlight-on-change="{{val}}">
Total: {{ val }}
</div>
<button ng-click="updateVal()">Add to Total</button>
</body>
original source: http://plnkr.co/edit/FFBhPIRuT0NA2DZhtoAD?p=preview from this post: https://groups.google.com/d/msg/angular/xZptsb-NYc4/YH35m39Eo2wJ
A bit more complex usage, which works perfect for me because it highlights the specific column when an update is coming.
<table class="table table-hover">
<tr>
<th ng-repeat="col in fc.tableColumns"><!--fc is a controller-->
{{col.displayName}}
</th>
</tr>
<tr ng-repeat="item in fc.items track by item.id">
<td highlight-on-change="{{value}}" ng-repeat="(key,value) in item">
@*{{key}} =*@ {{value}}
</td>
</tr>
</table>
As I said, updates the specific column, while doing this somewhere in the controller.
items[index] = item;//item from server