AngularJS : How to watch service variables?

后端 未结 21 1370
盖世英雄少女心
盖世英雄少女心 2020-11-22 09:12

I have a service, say:

factory(\'aService\', [\'$rootScope\', \'$resource\', function ($rootScope, $resource) {
  var service = {
    foo: []
  };

  return          


        
21条回答
  •  渐次进展
    2020-11-22 09:28

    I have written two simple utility services that help me track service properties changes.

    If you want to skip the long explanation, you can go strait to jsfiddle

    1. WatchObj

    mod.service('WatchObj', ['$rootScope', WatchObjService]);
    
    function WatchObjService($rootScope) {
      // returns watch function
      // obj: the object to watch for
      // fields: the array of fields to watch
      // target: where to assign changes (usually it's $scope or controller instance)
      // $scope: optional, if not provided $rootScope is use
      return function watch_obj(obj, fields, target, $scope) {
        $scope = $scope || $rootScope;
        //initialize watches and create an array of "unwatch functions"
        var watched = fields.map(function(field) {
          return $scope.$watch(
            function() {
              return obj[field];
            },
            function(new_val) {
              target[field] = new_val;
            }
          );
        });
        //unregister function will unregister all our watches
        var unregister = function unregister_watch_obj() {
          watched.map(function(unregister) {
            unregister();
          });
        };
        //automatically unregister when scope is destroyed
        $scope.$on('$destroy', unregister);
        return unregister;
      };
    }

    This service is used in the controller in the following way: Suppose you have a service "testService" with the properties 'prop1', 'prop2', 'prop3'. You want to watch and assign to scope 'prop1' and 'prop2'. With the watch service it will look like that:

    app.controller('TestWatch', ['$scope', 'TestService', 'WatchObj', TestWatchCtrl]);
    
    function TestWatchCtrl($scope, testService, watch) {
      $scope.prop1 = testService.prop1;
      $scope.prop2 = testService.prop2;
      $scope.prop3 = testService.prop3;
      watch(testService, ['prop1', 'prop2'], $scope, $scope);
    }

    1. apply Watch obj is great, but it is not enough if you have asynchronous code in your service. For that case, I use a second utility which looks like that:

    mod.service('apply', ['$timeout', ApplyService]);
    
    function ApplyService($timeout) {
      return function apply() {
        $timeout(function() {});
      };
    }

    I would trigger it in the end of my async code to trigger the $digest loop. Like that:

    app.service('TestService', ['apply', TestService]);
    
    function TestService(apply) {
      this.apply = apply;
    }
    TestService.prototype.test3 = function() {
      setTimeout(function() {
        this.prop1 = 'changed_test_2';
        this.prop2 = 'changed2_test_2';
        this.prop3 = 'changed3_test_2';
        this.apply(); //trigger $digest loop
      }.bind(this));
    }

    So, all of that together will look like that (you can run it or open fiddle):

    // TEST app code
    
    var app = angular.module('app', ['watch_utils']);
    
    app.controller('TestWatch', ['$scope', 'TestService', 'WatchObj', TestWatchCtrl]);
    
    function TestWatchCtrl($scope, testService, watch) {
      $scope.prop1 = testService.prop1;
      $scope.prop2 = testService.prop2;
      $scope.prop3 = testService.prop3;
      watch(testService, ['prop1', 'prop2'], $scope, $scope);
      $scope.test1 = function() {
        testService.test1();
      };
      $scope.test2 = function() {
        testService.test2();
      };
      $scope.test3 = function() {
        testService.test3();
      };
    }
    
    app.service('TestService', ['apply', TestService]);
    
    function TestService(apply) {
      this.apply = apply;
      this.reset();
    }
    TestService.prototype.reset = function() {
      this.prop1 = 'unchenged';
      this.prop2 = 'unchenged2';
      this.prop3 = 'unchenged3';
    }
    TestService.prototype.test1 = function() {
      this.prop1 = 'changed_test_1';
      this.prop2 = 'changed2_test_1';
      this.prop3 = 'changed3_test_1';
    }
    TestService.prototype.test2 = function() {
      setTimeout(function() {
        this.prop1 = 'changed_test_2';
        this.prop2 = 'changed2_test_2';
        this.prop3 = 'changed3_test_2';
      }.bind(this));
    }
    TestService.prototype.test3 = function() {
      setTimeout(function() {
        this.prop1 = 'changed_test_2';
        this.prop2 = 'changed2_test_2';
        this.prop3 = 'changed3_test_2';
        this.apply();
      }.bind(this));
    }
    //END TEST APP CODE
    
    //WATCH UTILS
    var mod = angular.module('watch_utils', []);
    
    mod.service('apply', ['$timeout', ApplyService]);
    
    function ApplyService($timeout) {
      return function apply() {
        $timeout(function() {});
      };
    }
    
    mod.service('WatchObj', ['$rootScope', WatchObjService]);
    
    function WatchObjService($rootScope) {
      // target not always equals $scope, for example when using bindToController syntax in 
      //directives
      return function watch_obj(obj, fields, target, $scope) {
        // if $scope is not provided, $rootScope is used
        $scope = $scope || $rootScope;
        var watched = fields.map(function(field) {
          return $scope.$watch(
            function() {
              return obj[field];
            },
            function(new_val) {
              target[field] = new_val;
            }
          );
        });
        var unregister = function unregister_watch_obj() {
          watched.map(function(unregister) {
            unregister();
          });
        };
        $scope.$on('$destroy', unregister);
        return unregister;
      };
    }
    
    
    
    prop1: {{prop1}}
    prop2: {{prop2}}
    prop3 (unwatched): {{prop3}}

提交回复
热议问题