Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular js - Highlight dom when value changes

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 manipulation logic in the controller and thats the job of directives.

Here is a plunkr

http://plnkr.co/edit/xWk5UBwifbCF2raVvw81?p=preview

Basically when the data changes by clicking the load data button in the plunkr above, i want the cells whose values changed to highlight automatically. I cant get it to work for the life of me.

Any help?

like image 233
Jenos Avatar asked Nov 21 '13 08:11

Jenos


2 Answers

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/

like image 79
Yoshi Avatar answered Jan 02 '23 19:01

Yoshi


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();
            }
        }
      });
    }
  };
}]);
like image 28
Soichi Hayashi Avatar answered Jan 02 '23 20:01

Soichi Hayashi