AngularJs -Effective way to Monitor Changes to Model data


The two-way data binding in AngularJS is very powerful, especially when there is a direct mapping of input data to view-model-data. But, when the data mapping requires some translation, interpretation, or validation, you need to get your hands a little dirty - you need to get more involved in how data changes are propagated within your Controller(s). As this has happened to me, I've been forced to think more deeply about my AngularJS application architecture; and, as a result, I've found that my life is often simplified (in the long term) by calling a directive to watch before I bind the data to View model.


Factory To Monitor Changes


This is easiest and effective way because of these reasons :

1) I just need to call a Trigger Method with scope which should be monitored
2) Once the data is retrieved from Ajax call , I just Initialize the data
3) If there are any changes to bound data on the form and user tries to navigate to another page, I get Confirm box on the page and hence easy to watch.
4) If there are updates from the server, I just Reset the data - which means you are saying to watch it on new data forgetting old data .

Below is factory which can be used across your application :

Just create a JS file with below code in it and include factory in your base js, for example, app.js

/* Factories */
 'CYW.aysfactory',


 'use strict';  
 angular.module('CYW.aysfactory', [])  
   .factory('$CYWAreYouSure', ['$q', '$window',  
     function factories_ays($q, $window) {  
       /// <summary>  
       /// Factory for the ARE YOU SURE   
       /// </summary>  
       /// <param name="$q">The $q.</param>  
       /// <param name="$window">The $window.</param>  
       return {  
         Dirty: false,  
         Original: null,  
         Title: 'Are you sure?',  
         Message: 'Changes you made may not be saved. Do you wish to continue and lose your changes?',  
         SetTitle: function (title) {  
           /// <summary>  
           /// sets the title to 'Are you sure?'  
           /// </summary>  
           /// <param name="title">The title.</param>  
           this.Title = title;  
         },  
         SetMessage: function (msg) {  
           /// <summary>  
           /// Sets the message to msg  
           /// </summary>  
           /// <param name="msg">The MSG.</param>  
           this.Message = msg;  
         },  
         Initialize: function ($scope, dataToWatch) {  
           /// <summary>  
           /// Initialize tje data to watch in the scope   
           /// </summary>  
           /// <param name="$scope">The $scope.</param>  
           /// <param name="dataToWatch">The data to watch.</param>  
           var that = this;  
           $scope.$watch(function () {  
             return dataToWatch  
           }, function (newVal, oldVal) {  
             /// <summary>  
             /// sets the data to watch  
             /// </summary>  
             /// <param name="newVal">The new value.</param>  
             /// <param name="oldVal">The old value.</param>  
             if (newVal) {  
               if (!that.Original) {  
                 that.Original = JSON.stringify(newVal);  
               }  
               else {  
                 if (JSON.stringify(newVal) != that.Original) {  
                   that.Dirty = true;  
                 }  
               }  
             }  
           }, true);  
           $window.onbeforeunload = function () {  
             /// <summary>  
             /// returns dirty message  
             /// </summary>  
             if (that.Dirty) {  
               return that.Message  
             }  
             else {  
               return;  
             }  
           };  
         },  
         Reset: function (updatedData) {  
           /// <summary>  
           /// Reset the dirty message  
           /// </summary>  
           /// <param name="updatedData">The updated data.</param>  
           this.Dirty = false;  
           this.Original = null;  
         },  
         Trigger: function ($scope) {  
           /// <summary>  
           /// triggers dirt message for ARE YOU SURE message  
           /// </summary>  
           /// <param name="$scope">The $scope.</param>  
           var def = $q.defer();  
           if (this.Dirty) {  
             confirm($scope, this.Title, this.Message).then(function (result) {  
               if (result) {  
                 def.resolve();  
               }  
               else {  
                 def.reject();  
               }  
             }, function () {  
               def.reject();  
             });  
           }  
           else {  
             def.resolve();  
           }  
           return def.promise;  
         }  
       };  
     }  
   ])  
 ;  



Now you have code in place its time to use it  : 

I have a function which does an HTTP call to get dummy data . We just wrap it in trigger function and Initialize once data is back from the server as shown in Red.


 var getDataFromServerAndBind = function () {  
         var def = $q.defer();  
         $CYWAreYouSure.Trigger($scope).then(function () {  
           /// <summary>  
           /// AYS message based on scope retrieval  
           /// </summary>  
           $cywHttp.get('getSomeDummyData/').then(function (data) {  
             $scope.data = data;  
             $scope.bindGrid();  
             $CYWAreYouSure.Initialize($scope, $scope.data);  
             def.resolve();  
           }, function () {  
             def.reject();  
           });  
         }, function () {  
           def.reject();  
         });  
         return def.promise;  
       }  

So now we have data imagine person changes data and you have some other API to update model ,so we need to call Reset  function and bind it back as above .



  $scope.AddOrUpdateDummyData = function () {  
         return $cywHttp.put('putSomeDummyData/putEmployeeRoles', {  
           data: employeeRolesListingModel  
         }).then(function (data) {  
           $cywAreYouSure.Reset($scope.data);  
           // Just Rebind the Grid inorder to show new add/update of employee role which we created before  
           getDataFromServerAndBind();  
         });  
       };  

That's it !!! . Now Whenever a user changes values /there is a change in values of the model and there are no saved changes Factory automatically pops up a confirm box to show user it's not saved . 

Here is what it looks like :



You can even set the Title and message separately for each page you are using.

Thanks for Visiting .

Do share your views . For more updates just follow the Fb page

Comments

Popular posts from this blog

The Top 15 Google Products for People Who Build Websites

Google Translator using Windows forms

5 Useful Tricks for Ubuntu Power Users