angular_learner
angular_learner

Reputation: 671

Angularjs - how to watch for changes in DOM and apply a new style to it?

I have a very simple angular app that pushes data in without refreshing the page using setInterval. Now, how can I listen or watch for new data/changes, so that if the new value/data differ from the previous one a new css style will be applied to that particular new value (for example it will change the font color to red).

My code is below:

view:

<h1>{{title}}</h1>
        <ul>
            <li ng-repeat="friend in friends"><strong>Name: </strong>{{friend.name}} : {{friend.username}}</li>
        </ul>

data:

angular
    .module ('myApp')
    .factory ('Friends', ['$http', function ($http) {
        return {
            get: function () {
                return $http.get ('users.json').then (function (response) {  
                    return response.data;
                });
            }
        };
    }]);

Controller:

angular
    .module ('myApp')
    .controller ('summaryCtrl', ['$scope', 'Friends', function ($scope, Friends) {
        $scope.title = "Friends";
        $scope.loadData = function () {
            Friends.get ().then (function (data) {
                $scope.friends = data;
            });
        };

        //initial load
        $scope.loadData();

        var timer = setInterval(function(){
            $scope.loadData();

        },5000);

    }]);

many thanks

Upvotes: 1

Views: 247

Answers (2)

JoshuaJ
JoshuaJ

Reputation: 979

My recommendation would be to manually compare each friend item and assign a changeFlag whenever the data has changed.

To start, keep a reference to the old data and whenever new data comes in, compare the two, like this:

var oldData = undefined; // Somewhere in initialization.
...
Friends.get().then(function (response) {
    var newData = response;
    if (oldData && JSON.stringify(oldData) != JSON.stringify(newData))
    {
        $scope.friends = newData;
        $scope.$apply(); // Force the entire page to be redrawn. You can do style bindings to change a style.
    }
    oldData = response;
}

This will get you half-way to your goal. You will only be refreshing the page whenever something has changed, but there is no indication as to which friend has changed. I imagine this is what you are attempting to accomplish. You want to highlight those friends that have changed.

To do this we could simply create a comparison function that applies a flag to each object that has changed. However, this code assumes that some property on each friend remains fixed. This is normally why an id property is given to each item in a database. I'm going to assume you have an id property for each friend that never changes regardless if their name, age, email, etc. does.

var changeFlagFriendsObjects = function(oldData, newData) {
    var idToOldDataMap = {};
    oldData.forEach(function (friend) {
       idToOldDataMap[friend.id] = friend;
    });

    newData.forEach(function (friend) {
        var oldFriendData = idToOldDataMap[friend.id];
        friend.changeFlag = JSON.stringify(oldFriendData) != JSON.stringify(friend);             
    });
};

// You would call changeFlagFriendsObjects in the other example above. I'm sure this would be easy to figure out how to place.

Regarding binding styles in the HTML to properties, see here.

An example would be like the following:

<!-- Apply the 'highlight' style when changeFlag is true -->
<li ng-repeat="friend in friends" ng-style="highlight={changeFlag: true}"><strong>Name: </strong>{{friend.name}} : {{friend.username}}</li>

Upvotes: 0

Edminsson
Edminsson

Reputation: 2514

Use $interval instead of setInterval, since it triggers a digest loop it will update your data automatically

angular
    .module ('myApp')
    .controller ('summaryCtrl', ['$scope', 'Friends', '$interval' function ($scope, Friends, $interval) {
        $scope.title = "Friends";
        $scope.loadData = function () {
            Friends.get ().then (function (data) {
                $scope.friends = data;
            });
        };

        //initial load
        $scope.loadData();

        var timer = $interval(function(){
            $scope.loadData();

        },5000);

    }]);

Upvotes: 3

Related Questions