424 Stimmen

AngularJS : Wie kann man Dienstvariablen überwachen?

Ich habe einen Dienst, sagen wir:

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

  return service;
}]);

Und ich würde gerne foo um eine Liste zu steuern, die in HTML gerendert wird:

<div ng-controller="FooCtrl">
  <div ng-repeat="item in foo">{{ item }}</div>
</div>

Damit der Controller erkennen kann, wann aService.foo aktualisiert wird, habe ich dieses Muster zusammengeschustert, bei dem ich einenService zum Controller hinzufüge $scope und verwenden Sie dann $scope.$watch() :

function FooCtrl($scope, aService) {                                                                                                                              
  $scope.aService = aService;
  $scope.foo = aService.foo;

  $scope.$watch('aService.foo', function (newVal, oldVal, scope) {
    if(newVal) { 
      scope.foo = newVal;
    }
  });
}

Das fühlt sich langatmig an, und ich habe es in jedem Controller wiederholt, der die Variablen des Dienstes verwendet. Gibt es einen besseren Weg, um die Überwachung gemeinsamer Variablen zu erreichen?

28voto

ganaraj Punkte 26809

Soweit ich das beurteilen kann, müssen Sie nicht so etwas Aufwändiges machen. Sie haben bereits foo aus dem Dienst Ihrem Bereich zugewiesen, und da foo ein Array (und damit ein Objekt) ist, wird es per Referenz zugewiesen. Alles, was Sie tun müssen, ist also so etwas wie dies:

function FooCtrl($scope, aService) {                                                                                                                              
  $scope.foo = aService.foo;

 }

Wenn eine andere Variable in demselben Ctrl von der Änderung von foo abhängt, dann ja, dann bräuchte man eine Watch, um foo zu beobachten und Änderungen an dieser Variable vorzunehmen. Aber solange es sich um eine einfache Referenz handelt, ist eine Beobachtung unnötig. Ich hoffe, das hilft.

9voto

Sie können den Dienst in $rootScope einfügen und beobachten:

myApp.run(function($rootScope, aService){
    $rootScope.aService = aService;
    $rootScope.$watch('aService', function(){
        alert('Watch');
    }, true);
});

In Ihrem Controller:

myApp.controller('main', function($scope){
    $scope.aService.foo = 'change';
});

Eine andere Möglichkeit ist die Verwendung einer externen Bibliothek wie: https://github.com/melanke/Watch.JS

Funktioniert mit: IE 9+, FF 4+, SF 5+, WebKit, CH 7+, OP 12+, BESEN, Node.JS , Rhino 1.7+

Sie können die Änderungen von einem, mehreren oder allen Objektattributen beobachten.

Beispiel:

var ex3 = {
    attr1: 0,
    attr2: "initial value of attr2",
    attr3: ["a", 3, null]
};   
watch(ex3, function(){
    alert("some attribute of ex3 changes!");
});
ex3.attr3.push("new value");

6voto

hayatbiralem Punkte 596

\==UPDATED==

Sehr einfach jetzt in $watch.

Stift hier .

HTML:

<div class="container" data-ng-app="app">

  <div class="well" data-ng-controller="FooCtrl">
    <p><strong>FooController</strong></p>
    <div class="row">
      <div class="col-sm-6">
        <p><a href="" ng-click="setItems([ { name: 'I am single item' } ])">Send one item</a></p>
        <p><a href="" ng-click="setItems([ { name: 'Item 1 of 2' }, { name: 'Item 2 of 2' } ])">Send two items</a></p>
        <p><a href="" ng-click="setItems([ { name: 'Item 1 of 3' }, { name: 'Item 2 of 3' }, { name: 'Item 3 of 3' } ])">Send three items</a></p>
      </div>
      <div class="col-sm-6">
        <p><a href="" ng-click="setName('Sheldon')">Send name: Sheldon</a></p>
        <p><a href="" ng-click="setName('Leonard')">Send name: Leonard</a></p>
        <p><a href="" ng-click="setName('Penny')">Send name: Penny</a></p>
      </div>
    </div>
  </div>

  <div class="well" data-ng-controller="BarCtrl">
    <p><strong>BarController</strong></p>
    <p ng-if="name">Name is: {{ name }}</p>
    <div ng-repeat="item in items">{{ item.name }}</div>
  </div>

</div>

JavaScript:

var app = angular.module('app', []);

app.factory('PostmanService', function() {
  var Postman = {};
  Postman.set = function(key, val) {
    Postman[key] = val;
  };
  Postman.get = function(key) {
    return Postman[key];
  };
  Postman.watch = function($scope, key, onChange) {
    return $scope.$watch(
      // This function returns the value being watched. It is called for each turn of the $digest loop
      function() {
        return Postman.get(key);
      },
      // This is the change listener, called when the value returned from the above function changes
      function(newValue, oldValue) {
        if (newValue !== oldValue) {
          // Only update if the value changed
          $scope[key] = newValue;
          // Run onChange if it is function
          if (angular.isFunction(onChange)) {
            onChange(newValue, oldValue);
          }
        }
      }
    );
  };
  return Postman;
});

app.controller('FooCtrl', ['$scope', 'PostmanService', function($scope, PostmanService) {
  $scope.setItems = function(items) {
    PostmanService.set('items', items);
  };
  $scope.setName = function(name) {
    PostmanService.set('name', name);
  };
}]);

app.controller('BarCtrl', ['$scope', 'PostmanService', function($scope, PostmanService) {
  $scope.items = [];
  $scope.name = '';
  PostmanService.watch($scope, 'items');
  PostmanService.watch($scope, 'name', function(newVal, oldVal) {
    alert('Hi, ' + newVal + '!');
  });
}]);

6voto

Flavien Volken Punkte 15992

Sie können die Änderungen innerhalb der Fabrik selbst beobachten und dann eine Änderung senden

angular.module('MyApp').factory('aFactory', function ($rootScope) {
    // Define your factory content
    var result = {
        'key': value
    };

    // add a listener on a key        
    $rootScope.$watch(function () {
        return result.key;
    }, function (newValue, oldValue, scope) {
        // This is called after the key "key" has changed, a good idea is to broadcast a message that key has changed
        $rootScope.$broadcast('aFactory:keyChanged', newValue);
    }, true);

    return result;
});

Dann in Ihrem Controller:

angular.module('MyApp').controller('aController', ['$rootScope', function ($rootScope) {

    $rootScope.$on('aFactory:keyChanged', function currentCityChanged(event, value) {
        // do something
    });
}]);

Auf diese Weise setzen Sie alle zugehörigen Fabrik-Code innerhalb seiner Beschreibung dann können Sie nur auf die Übertragung von außen verlassen

4voto

Jamie Punkte 4280

Aufbauend auf dtheodor's Antwort könnten Sie etwas Ähnliches wie das Folgende verwenden, um sicherzustellen, dass Sie nicht vergessen, den Rückruf zu deaktivieren... Einige mögen Einwände gegen die Übergabe des $scope zu einem Dienst.

factory('aService', function() {
  var observerCallbacks = [];

  /**
   * Registers a function that will be called when
   * any modifications are made.
   *
   * For convenience the callback is called immediately after registering
   * which can be prevented with `preventImmediate` param.
   *
   * Will also automatically unregister the callback upon scope destory.
   */
  this.registerObserver = function($scope, cb, preventImmediate){
    observerCallbacks.push(cb);

    if (preventImmediate !== true) {
      cb();
    }

    $scope.$on('$destroy', function () {
      observerCallbacks.remove(cb);
    });
  };

  function notifyObservers() {
    observerCallbacks.forEach(function (cb) {
      cb();
    });
  };

  this.foo = someNgResource.query().$then(function(){
    notifyObservers();
  });
});

Array.remove ist eine Erweiterungsmethode, die wie folgt aussieht:

/**
 * Removes the given item the current array.
 *
 * @param  {Object}  item   The item to remove.
 * @return {Boolean}        True if the item is removed.
 */
Array.prototype.remove = function (item /*, thisp */) {
    var idx = this.indexOf(item);

    if (idx > -1) {
        this.splice(idx, 1);

        return true;
    }
    return false;
};

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X