Tengo un servicio, digamos:

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

  return service;
}]);

Y me gustaría usar foo para el control de una lista en la que se representa en HTML:

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

Para que el controlador de detectar cuando aService.foo se actualiza he improvisado este patrón donde puedo agregar el servicio a la del controlador de $scope y, a continuación, utilizar $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;
    }
  });
}

Esto se siente larga mano, y he estado repitiendo en cada controlador que utiliza el servicio de las variables. Existe una mejor manera de llevar a cabo observando las variables compartidas?

  • Usted puede pasar un tercer parámetro a $reloj se establece en true para observación en profundidad servicio y de todas sus propiedades.
  • $scope.foo= aService.foo es suficiente, se puede perder la línea de arriba. Y lo que hace en el interior de $reloj no tiene sentido, si desea asignar un nuevo valor a $scope.foo just do it…
  • Podría usted acaba de referencia aService.foo en el lenguaje de marcado html? (como esto: plnkr.co/editar/aNrw5Wo4Q0IxR2loipl5?p=vista previa)
  • He añadido un ejemplo sin las devoluciones de llamada o $relojes, véase la respuesta de abajo (jsfiddle.net/zymotik/853wvv7s)
  • Esta es una pregunta muy interesante. Y también me di cuenta de que podía felizmente enlazar un control a «aService.foo» (y entonces sería actualiza sin ningún tipo de relojes), pero si mi controlador utilizado «$scope.foo = aService.foo»; y que unido a las «foo», entonces nunca se actualiza. Muy extraño. Gran pregunta, sin embargo.
  • estás en lo correcto. Creo que es debido a la naturaleza de Javascript, podemos ver este modelo en otros muchos lugares (no sólo en Angular, pero en JS hablando en general). Por un lado, la transferencia de valor (y no obligado) y en el otro lado de transferencia de un objeto (o el valor que hace referencia al objeto…), y es por eso que las propiedades se actualizan correctamente (como perfectamente se muestra en el ejemplo de Zymotik arriba).

InformationsquelleAutor berto | 2012-09-25

21 Comentarios

  1. 275

    Usted puede utilizar siempre el buen viejo patrón observer si usted desea evitar la tiranía y la sobrecarga de $watch.

    En el servicio:

    factory('aService', function() {
      var observerCallbacks = [];
    
      //register an observer
      this.registerObserverCallback = function(callback){
        observerCallbacks.push(callback);
      };
    
      //call this when you know 'foo' has been changed
      var notifyObservers = function(){
        angular.forEach(observerCallbacks, function(callback){
          callback();
        });
      };
    
      //example of when you may want to notify observers
      this.foo = someNgResource.query().$then(function(){
        notifyObservers();
      });
    });

    Y en el controlador:

    function FooCtrl($scope, aService){
      var updateFoo = function(){
        $scope.foo = aService.foo;
      };
    
      aService.registerObserverCallback(updateFoo);
      //service now in control of updating foo
    };
    • ¿Cómo quitar el registrado de devolución de llamada cuando el usuario sale de la página con el FooCtrl?
    • escuchar la $destory de eventos en el ámbito de aplicación y agregar un método para anular el registro de aService
    • Me corrija si estoy equivocado, pero usted debe $scope.$aplicar el cambio en el observador de devolución de llamada en el controlador. Al menos tuve, porque todos los cambios a mi servicio venir a través de eventos de un flash.
    • ¿Qué son las ventajas de esta solución? Se necesita más de código en un servicio, y un poco de la misma cantidad de código en el controlador (ya que también tenemos que anular el registro de $destruir). Me podría decir por la velocidad de ejecución, pero en la mayoría de los casos simplemente no importa.
    • no está seguro de cómo esto es una mejor solución que $watch, el interrogador estaba pidiendo una forma simple de compartir los datos, se ve aún más complicado. Prefiero utilizar $difusión de este
    • $watch vs observador patrón es simplemente elegir si la encuesta o a la inserción, y es básicamente una cuestión de rendimiento, por lo que uso es un alto rendimiento. Yo uso el patrón observer cuando de lo contrario tendría que «profundo» ver objetos complejos. Me fije conjunto de servicios para el $scope en lugar de ver solo servicio de los valores. I evitar angulares de dólares de ver como el diablo, no hay suficiente de lo que está sucediendo en las directivas y en los nativos angular de enlace de datos.
    • Yo sugeriría que esta solución, incluso cuando el rendimiento no es tanto un problema debido a que el comportamiento se define explícitamente en el código y no depende de la biblioteca de la magia. Buena respuesta!
    • La razón por la que estamos usando un framework como Angular es no cocinar nuestro propio observador patrones.
    • No me parece que esto es demasiado increíble … disculpas si me parece crudo, pero esto es una simple getSet enfoque. Utilizar un Object.defineProperties(object, ...) o si usted está buscando un pubSub metodología, usted puede también utilizar $rootScope.$on('changed:someData', setSomeData) & $rootScope.$broadcast('changed:someData', someData). IMAGINARIO PUNTOS para esta respuesta 🙁
    • Usted necesita tener un muy buen caso de uso para justificar reimplementing eventos en Angular.
    • No nos falta el regreso de esta;’ al final de la definición de servicio?
    • No muy Angular, en comparación con el stackoverflow.com/a/20863597/418150
    • ¿por qué hemos de utilizar angulars $difusión aquí? Por qué iba yo a difundir un evento que es probablemente significaba para un único controlador, en lugar de toda la aplicación? Y ¿por qué no deberíamos usar un patrón bien conocido si encajaría en el caso?
    • no entiendo el odio. esta es una respuesta aceptable.
    • $ver y $rootScope.$emiten son muy caros ya que tiene que recorrer todo el ámbito, derecho? Como estoy trabajando mucho con la realidad de la cpu caro frontend, esto es importante para mí y yo también ya lo utilizó en la 1.4. Patrón Observer todavía es una conexión directa, y nada tiene que ser atravesado, que para la alta n puede ser más rápido. Especialmente si hay ya un montón de rootScope oyentes? Yo no tenía punto de referencia, pero eso es como yo lo entiendo. si tiene otra información, por favor me ilumine!
    • upvoted respuesta no debe ser tan upvoted. Si bien es cierto, que ignora por completo los fundamentos de la creación de su propio patrón observer en un servicio, es decir, el rendimiento. Si alguna vez has utilizado observadores en una aplicación angularjs sabes lo rápido que pueden degradar el rendimiento. Un peso más ligero enfoque como este seguramente tiene algún aplicabilidad.

  2. 225

    En un escenario como este, donde varios/desconocido objetos podrían estar interesados en los cambios, utilice $rootScope.$broadcast desde el punto de cambiar.

    Lugar de crear su propio registro de los oyentes (que tiene que ser limpiada en varios $destruye), usted debería ser capaz de $broadcast del servicio en cuestión.

    Debe todavía código de la $on controladores en cada oyente, pero el patrón está desacoplado de varias llamadas a $digest y así se evita el riesgo de tiempo de ejecución de los vigilantes.

    De esta manera, también, los oyentes pueden entrar y salir de la DOM y/o diferentes ámbitos secundarios sin el servicio de cambiar su comportamiento.

    ** actualización: ejemplos **

    Emisiones tendría más sentido en «global» de los servicios que podrían afectar a otras innumerables cosas en su aplicación. Un buen ejemplo es el servicio de atención al Usuario, donde hay una serie de acontecimientos que puedan tener lugar como inicio de sesión, cierre de sesión, de actualización, de inactividad, etc. Creo que esto es donde las emisiones tener más sentido, ya que cualquier ámbito puede escuchar de un evento, sin siquiera inyectar el servicio, y no es necesario para evaluar cualquier tipo de expresión o de la caché de resultados para inspeccionar en busca de cambios. Simplemente dispara y olvida (así que asegúrese de que es un fuego y se olvida de la notificación, no es algo que se requiere de la acción)

    .factory('UserService', [ '$rootScope', function($rootScope) {
       var service = <whatever you do for the object>
    
       service.save = function(data) {
         .. validate data and update model ..
         //notify listeners and provide the data that changed [optional]
         $rootScope.$broadcast('user:updated',data);
       }
    
       //alternatively, create a callback function and $broadcast from there if making an ajax call
    
       return service;
    }]);

    El servicio anterior difundir un mensaje a cada ámbito a la hora de guardar() la función se ha completado y la información era válida. Alternativamente, si se trata de un $recurso o un ajax presentación, mueva la llamada de difusión en la devolución de llamada por lo que se desencadena cuando el servidor ha respondido. Emisiones de adaptarse a ese modelo en particular y porque cada oyente sólo espera para el evento sin la necesidad de inspeccionar el alcance en cada uno de los $digerir. El oyente se vería como:

    .controller('UserCtrl', [ 'UserService', '$scope', function(UserService, $scope) {
    
      var user = UserService.getUser();
    
      //if you don't want to expose the actual object in your scope you could expose just the values, or derive a value for your purposes
       $scope.name = user.firstname + ' ' +user.lastname;
    
       $scope.$on('user:updated', function(event,data) {
         //you could inspect the data to see if what you care about changed, or just update your own scope
         $scope.name = user.firstname + ' ' + user.lastname;
       });
    
       //different event names let you group your code and logic by what happened
       $scope.$on('user:logout', function(event,data) {
         .. do something differently entirely ..
       });
    
     }]);

    Una de las ventajas de esto es la eliminación de varios relojes. Si usted fuera la combinación de los campos o de los derivados de los valores como en el ejemplo anterior, tendríamos que ver tanto el nombre y apellidos de las propiedades. Viendo el getUser() función solo funciona si el objeto de usuario fue reemplazado en las actualizaciones, no se activan si el objeto de usuario simplemente tenía sus propiedades actualizada. En cuyo caso tendrías que hacer una observación en profundidad y que es más intenso.

    $broadcast envía el mensaje de que el ámbito de aplicación que se llama en abajo en cualquier niño ámbitos. Así llamarlo desde $rootScope se activará en cada ámbito. Si se va a $difusión de su controlador del ámbito de aplicación, por ejemplo, es el fuego sólo en los ámbitos de la que heredan de su controlador de alcance. $emiten va en la dirección opuesta y se comporta de manera similar a un DOM evento en el que las burbujas de la cadena de ámbitos.

    Tenga en cuenta que hay situaciones donde $difusión de hace un montón de sentido, y hay escenarios donde $watch es una mejor opción, especialmente si en aislar un ámbito muy específico reloj de expresión.

    • Salir de la $digerir ciclo es una Buena Cosa, especialmente si los cambios que estamos viendo no es un valor que va directamente e ir de inmediato en el DOM.
    • Es allí lejos para evitar .método save (). Parece una exageración cuando se le acaba la supervisión de la actualización de una sola variable en el sharedService. Podemos ver que la variable dentro de la sharedService y la emisión cuando se cambia?
    • He intentado de muchas maneras para compartir los datos entre los controladores, pero este es el único que trabajaba. Bien jugado, señor.
    • Yo prefiero esto a las otras respuestas, parece menos hacky, gracias
    • Este es el correcto patrón de diseño sólo si su consumo de controlador tiene varias posibles fuentes de los datos; en otras palabras, si usted tiene un MIMO situación (Múltiple Entrada Múltiple Salida). Si usted está usando sólo un uno-a-muchos de patrón, usted debe utilizar directa del objeto de referencia y dejar que el marco Angular de los dos-manera vinculante para usted. Horkyze vinculado por debajo de este, y es una buena explicación de la válvula de dos vías de enlace, y sus limitaciones: stsc3000.github.io/blog/2013/10/26/…
  3. 44

    Estoy utilizando un método similar al de @dtheodot pero el uso de angulares de la promesa en lugar de pasar las devoluciones de llamada

    app.service('myService', function($q) {
        var self = this,
            defer = $q.defer();
    
        this.foo = 0;
    
        this.observeFoo = function() {
            return defer.promise;
        }
    
        this.setFoo = function(foo) {
            self.foo = foo;
            defer.notify(self.foo);
        }
    })

    Donde sólo uso myService.setFoo(foo) método para actualizar foo en el servicio. En el controlador se puede utilizar como:

    myService.observeFoo().then(null, null, function(foo){
        $scope.foo = foo;
    })

    Dos primeros argumentos de then son el éxito y el error de las devoluciones de llamada, la tercera parte es notificar de devolución de llamada.

    De referencia para $q.

    • ¿Cuál sería la ventaja de este método frente a los $emisión describe abajo por Matt Pileggi?
    • Bien ambos métodos tienen sus usos. Ventajas de emisión para mí sería humana, la legibilidad y la posibilidad de escuchar en más lugares para el mismo evento. Supongo que la principal desventaja es que la emisión de emiting mensaje a todos los descendientes de los ámbitos de lo que puede ser un problema de rendimiento.
    • Yo estaba teniendo un problema donde hacer $scope.$watch en una variable del servicio no era aparente para el trabajo (en el ámbito que yo estaba viendo era un modal heredado de $rootScope) – esto funcionó. Truco interesante, gracias por compartir!
    • ¿Cómo limpiar después de ti mismo con este enfoque? Es posible quitar el registrado de devolución de llamada de la promesa, cuando el alcance es destruido?
    • Buena pregunta. Sinceramente, no lo sé. Voy a tratar de hacer algunas pruebas sobre cómo podría quitar notificar de devolución de llamada de la promesa.
  4. 39

    Sin relojes u observador devoluciones de llamada (http://jsfiddle.net/zymotik/853wvv7s/):

    JavaScript:

    angular.module("Demo", [])
        .factory("DemoService", function($timeout) {
    
            function DemoService() {
                var self = this;
                self.name = "Demo Service";
    
                self.count = 0;
    
                self.counter = function(){
                    self.count++;
                    $timeout(self.counter, 1000);
                }
    
                self.addOneHundred = function(){
                    self.count+=100;
                }
    
                self.counter();
            }
    
            return new DemoService();
    
        })
        .controller("DemoController", function($scope, DemoService) {
    
            $scope.service = DemoService;
    
            $scope.minusOneHundred = function() {
                DemoService.count -= 100;
            }
    
        });

    HTML

    <div ng-app="Demo" ng-controller="DemoController">
        <div>
            <h4>{{service.name}}</h4>
            <p>Count: {{service.count}}</p>
        </div>
    </div>

    Este JavaScript trabaja como estamos pasando un objeto del servicio en lugar de un valor. Cuando un objeto de JavaScript que se vuelve de un servicio, Angular añade relojes a todas sus propiedades.

    También tenga en cuenta que estoy usando ‘var = auto’ como tengo que mantener una referencia al objeto original cuando el $tiempo de espera se ejecuta, de lo contrario, «esto» se refieren a la ventana de objetos.

    • Este es un acercamiento muy grande! Es allí una manera de enlazar sólo una propiedad de un servicio al alcance en lugar de todo el servicio? Sólo haciendo $scope.count = service.count no funciona.
    • También puede anidar la propiedad dentro de un (arbitrario) objeto, por lo que se pasa por referencia. $scope.data = service.data <p>Count: {{ data.count }}</p>
    • Excelente enfoque! Mientras que hay un montón de fuerte, respuestas funcionales en esta página, este es por lejos a) la más fácil de implementar, y b) la forma más fácil de entender al leer el código. Esta respuesta debería ser mucho mayor de lo que actualmente es.
    • Gracias @CodeMoose, he simplificado aún más el día de hoy para aquellos que son nuevos en AngularJS/JavaScript.
    • podría usted por favor dígame ¿cuál es la ventaja de tener la línea de var self = this; ? por qué no usar simplemente this
    • Hola @Afflatus, el auto se utiliza para mantener una referencia a la original esta. Por favor, consulte stackoverflow.com/questions/962033/…
    • En mi opinión, usted es confundir a la gente con self a la derecha aquí. No hay ningún punto de utilizarlos para la creación de la función debido a la this y self se refieren al mismo objeto. Tiene sentido usar self sólo dentro de la función y, a continuación, de nuevo no es necesario usarlo porque se puede utilizar .bind(this) y cuando usted tiene un caso cuando no se puede utilizar .bind() sólo entonces se puede utilizar self.
    • Gracias por tus comentarios @Stamy. Por favor, usted puede demostrar un enfoque más sencillo en un jsFiddle? Podemos entonces comparar y actualizar el ejemplo, si es necesario. Requerimos una referencia a la original this cuando se utiliza el $timeout función.
    • Aquí hay un enlace con un código: jsbin.com/guwogo/edit?js,consola . He añadido dos enfoques, estoy bien con cualquiera de los dos enfoques. Pero yo nunca usaría self para la creación de todo. Sería bueno si usted no entiende cómo this y los ámbitos de las obras, pero nunca he visto nada como esto en cualquier biblioteca decente.
    • De modo que, usted todavía necesita $reloj o $difusión ,a pesar de que, cuando se muestran los datos de servicio en más de un controlador al mismo tiempo? jsfiddle.net/qqejytbz/1
    • no, no
    • Gracias por la respuesta. Sé que esto es viejo. En mi violín publicado anteriormente, (bifurcada de su ejemplo) el recuento no es capaz de actualizar en ambos controladores, a menos que utilice un $watch. ¿Cuál es la manera correcta de hacerlo? Violín: jsfiddle.net/qqejytbz/1
    • me mudé a este a su propia pregunta, si le interesa: stackoverflow.com/questions/41786796/…
    • favor de ver los dos primeros comentarios, ya que creo que esto contesta a tu pregunta.
    • Que Dios los bendiga. He perdido como millones de horas diría yo. Porque yo estaba luchando con el 1.5 y angularjs pasando de 1 a 2 y también quería compartir datos

  5. 28

    Como lo que puedo decir, no tienes que hacer algo tan elaborado como eso. Se han asignado foo de los servicios a su alcance y desde foo es una matriz ( y a su vez un objeto es asignado por la referencia! ). Así que, todo lo que usted necesita hacer es algo como esto :

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

    Si algunas, otras variables en esta misma Ctrl depende de foo cambiar entonces sí, usted necesita un reloj para observar foo y hacer cambios en esa variable. Pero como el tiempo es una simple referencia de la observación es innecesario. Espero que esto ayude.

    • Gracias por la respuesta. Si la variable en el servicio es un «primitivo» en lugar de un objeto, como una cadena, para que, a su vez, requieren de una $watch?
    • Lo he intentado y no pude conseguir $watch a trabajar con un primitivo. En lugar de eso, he definido un método en el servicio que le devuelva el valor primitivo: somePrimitive() = function() { return somePrimitive } Y yo le asigna un $el ámbito de la propiedad para que método: $scope.somePrimitive = aService.somePrimitive;. Luego he utilizado el ámbito de aplicación del método en el código HTML: <span>{{somePrimitive()}}</span>
    • Gracias por la sugerencia de Marca. Funciona a la perfección.
    • No No uso de primitivas. Añadir en un objeto. Primitivas no son mutables y así 2way de enlace de datos no funcionará
    • sí, primitivas no debe ser utilizado para la 2-forma de enlace de datos, pero creo que la pregunta era sobre la observación de variables del servicio, no la configuración de 2 vías de unión. Si usted sólo necesita mirar un servicio de propiedad/variable, un objeto no es necesaria-una primitiva puede ser utilizado.
    • En esto soy capaz de cambiar aService valores del ámbito de aplicación. Pero el alcance no cambia en respuesta, el servicio de cambiar.
    • Esto también no funciona para mí. Simplemente la asignación de $scope.foo = aService.foo no actualizar el alcance de la variable de forma automática.
    • Correcto no se puede asignar un alcance variable a partir de una variable del Servicio y, a continuación, $watch(‘foo’ ,… estoy usando angular de la 1.3 a la prueba y el reloj no se despidan cuando el servicio de cambios de variables
    • He intentado par de veces antes … yo sabía que no era mejor que el uso de su alcance, pero yo no tuve éxito cuando me lo probé. Esta noche finalmente lo hice, tan impresionante 🙂 !!! Usted señor merece más upvotes !
    • Esto no funciona.
    • ¿cuál fue su solución para la 1.3?
    • No puedo comprender upvotes ya que este no funciona. Incluso si en el servicio que tiene un objeto, su valor no se actualiza automáticamente en el controlador.
    • Todo ello a partir de un «marco» que se supone que iba a hacer nuestra vida más fácil! No es de extrañar que comenzó de nuevo! LOL.

  6. 28

    Me topé con esta pregunta buscando algo similar, pero creo que merece una explicación detallada de lo que está pasando, así como algunas soluciones adicionales.

    Cuando un angular de expresión tal como la que se utiliza está presente en el HTML, Angular configura automáticamente un $watch para $scope.foo, y actualizar el código HTML siempre $scope.foo cambios.

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

    Lo que no se dice de la cuestión aquí es que una de dos cosas están afectando aService.foo, para que los cambios no son detectados. Estas dos posibilidades son:

    1. aService.foo se establece una nueva matriz, en cada tiempo, haciendo que la referencia a ser obsoletos.
    2. aService.foo es actualizada de manera tal que $digest ciclo no está activa en la actualización.

    Problema 1: Referencias Antiguas

    Considerando la primera posibilidad, suponiendo un $digest se aplica, si aService.foo era siempre la misma matriz, la establece automáticamente $watch sería detectar los cambios, como se muestra en el siguiente fragmento de código.

    Solución 1: asegúrese de que el objeto o matriz es el mismo objeto en cada actualización

    JS:

    angular.module('myApp', [])
      .factory('aService', [
        '$interval',
        function($interval) {
          var service = {
            foo: []
          };
    
          //Create a new array on each update, appending the previous items and 
          //adding one new item each time
          $interval(function() {
            if (service.foo.length < 10) {
              var newArray = []
              Array.prototype.push.apply(newArray, service.foo);
              newArray.push(Math.random());
              service.foo = newArray;
            }
          }, 1000);
    
          return service;
        }
      ])
      .factory('aService2', [
        '$interval',
        function($interval) {
          var service = {
            foo: []
          };
    
          //Keep the same array, just add new items on each update
          $interval(function() {
            if (service.foo.length < 10) {
              service.foo.push(Math.random());
            }
          }, 1000);
    
          return service;
        }
      ])
      .controller('FooCtrl', [
        '$scope',
        'aService',
        'aService2',
        function FooCtrl($scope, aService, aService2) {
          $scope.foo = aService.foo;
          $scope.foo2 = aService2.foo;
        }
      ]);

    HTML:

    <!DOCTYPE html>
    <html>
    
    <head>
      <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
      <link rel="stylesheet" href="style.css" />
      <script src="script.js"></script>
    </head>
    
    <body ng-app="myApp">
      <div ng-controller="FooCtrl">
        <h1>Array changes on each update</h1>
        <div ng-repeat="item in foo">{{ item }}</div>
        <h1>Array is the same on each udpate</h1>
        <div ng-repeat="item in foo2">{{ item }}</div>
      </div>
    </body>
    
    </html>

    Como se puede ver, el ng-repeat supuestamente conectado a aService.foo no se actualiza cuando aService.foo cambios, pero el ng-repeat conectado a aService2.foo hace. Esto es debido a que nuestra referencia a aService.foo es anticuado, pero nuestra referencia a aService2.foo no lo es. Hemos creado una referencia a la inicial de la matriz con $scope.foo = aService.foo;, que luego fue descartada por el servicio en su próxima actualización, lo que significa $scope.foo ya no se hace referencia a la matriz de la que hemos querido más.

    Sin embargo, mientras que hay varias maneras de asegurarse de que la referencia inicial se mantiene intacto, a veces puede ser necesario el cambio del objeto o de la matriz. O ¿qué pasa si el servicio de referencias de propiedades de una primitiva como un String o Number? En esos casos, no podemos simplemente confiar en una referencia. Entonces, ¿qué puede hacemos?

    Varias de las respuestas dadas anteriormente ya dar algunas soluciones a ese problema. Sin embargo, personalmente, estoy a favor del uso de la simple método sugerido por Jin y thetallweeks en los comentarios:

    sólo de referencia del servicio.foo en el código html

    Solución 1-b: Fije el servicio para el alcance, y la referencia {service}.{property} en el HTML.

    Sentido, basta con hacer:

    HTML:

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

    JS:

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

    JS:

    angular.module('myApp', [])
      .factory('aService', [
        '$interval',
        function($interval) {
          var service = {
            foo: []
          };
    
          //Create a new array on each update, appending the previous items and 
          //adding one new item each time
          $interval(function() {
            if (service.foo.length < 10) {
              var newArray = []
              Array.prototype.push.apply(newArray, service.foo);
              newArray.push(Math.random());
              service.foo = newArray;
            }
          }, 1000);
    
          return service;
        }
      ])
      .controller('FooCtrl', [
        '$scope',
        'aService',
        function FooCtrl($scope, aService) {
          $scope.aService = aService;
        }
      ]);

    HTML:

    <!DOCTYPE html>
    <html>
    
    <head>
      <script data-require="[email protected]" data-semver="1.4.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.js"></script>
      <link rel="stylesheet" href="style.css" />
      <script src="script.js"></script>
    </head>
    
    <body ng-app="myApp">
      <div ng-controller="FooCtrl">
        <h1>Array changes on each update</h1>
        <div ng-repeat="item in aService.foo">{{ item }}</div>
      </div>
    </body>
    
    </html>

    De esa manera, el $watch va a resolver aService.foo en cada $digest, que va a obtener el valor actualizado correctamente.

    Esta es una especie de lo que estaban tratando de hacer con su solución, pero de una forma mucho menos redonda sobre manera. Agregó que es innecesario $watch en el controlador que explícitamente pone foo en el $scope siempre que cambia. Usted no necesita ese extra $watch al sujetar aService en lugar de aService.foo a la $scope, y enlazar explícitamente a aService.foo en el marcado.


    Ahora que es todo bien y bueno, suponiendo un $digest ciclo está siendo aplicada. En los ejemplos de arriba, yo Angular del $intervalo servicio para la actualización de las matrices, que automáticamente se activa un $digest bucle después de cada actualización. Pero ¿qué pasa si las variables del servicio (por el motivo que sea) no están recibiendo actualizado dentro de la «Angular mundo». En otras palabras, nos no tener un $digest ciclo que se activa automáticamente cada vez que el servicio de cambios de propiedad?


    Problema 2: Falta $digerir

    Muchas de las soluciones que aquí se va a resolver este problema, pero estoy de acuerdo con Código Whisperer:

    La razón por la que estamos usando un framework como Angular es no cocinar nuestro propio observador patrones

    Por lo tanto, prefiero seguir utilizando el aService.foo de referencia en el código HTML, como se muestra en el segundo ejemplo anterior, y no tener que registrar un adicional de devolución de llamada en el Controlador.

    Solución 2: Utilice un setter y getter $rootScope.$apply()

    Me sorprendió que aún nadie ha sugerido el uso de un setter y captador de. Esta capacidad se introdujo en ECMAScript5, por lo que ha estado alrededor por años. Por supuesto, esto significa que si, por cualquier razón, usted necesita realmente navegadores antiguos, a continuación, este método no funcionará, pero me siento como getters y setters son muy infrautilizado en JavaScript. En este caso en particular, que puede ser muy útil:

    factory('aService', [
      '$rootScope',
      function($rootScope) {
        var realFoo = [];
    
        var service = {
          set foo(a) {
            realFoo = a;
            $rootScope.$apply();
          },
          get foo() {
            return realFoo;
          }
        };
      //...
    }

    JS:

    angular.module('myApp', [])
      .factory('aService', [
        '$rootScope',
        function($rootScope) {
          var realFoo = [];
    
          var service = {
            set foo(a) {
              realFoo = a;
              $rootScope.$apply();
            },
            get foo() {
              return realFoo;
            }
          };
    
          //Create a new array on each update, appending the previous items and 
          //adding one new item each time
          setInterval(function() {
            if (service.foo.length < 10) {
              var newArray = [];
              Array.prototype.push.apply(newArray, service.foo);
              newArray.push(Math.random());
              service.foo = newArray;
            }
          }, 1000);
    
          return service;
        }
      ])
      .controller('FooCtrl', [
        '$scope',
        'aService',
        function FooCtrl($scope, aService) {
          $scope.aService = aService;
        }
      ]);

    HTML:

    <!DOCTYPE html>
    <html>
    
    <head>
      <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
      <link rel="stylesheet" href="style.css" />
      <script src="script.js"></script>
    </head>
    
    <body ng-app="myApp">
      <div ng-controller="FooCtrl">
        <h1>Using a Getter/Setter</h1>
        <div ng-repeat="item in aService.foo">{{ item }}</div>
      </div>
    </body>
    
    </html>

    Aquí he añadido un «privado» de la variable en la función de servicio: realFoo. Esta actualizado y se puede recuperar utilizando el get foo() y set foo() funciones, respectivamente, en la service objeto.

    Nota el uso de $rootScope.$apply() en el conjunto de la función. Esto asegura que Angulares serán conscientes de los cambios a service.foo. Si usted consigue ‘inprog de los errores de ver esta útil página de referencia, o si utiliza Angular >= 1.3 usted puede utilizar $rootScope.$applyAsync().

    También tener cuidado con esta si aService.foo se actualiza muy frecuentemente, ya que podría afectar significativamente el rendimiento. Si el rendimiento sería un problema, puede establecer un observador patrón similar a las otras respuestas aquí el uso de la incubadora.

    • Este es el correcto, y más fácil solución. Como @NanoWizard decir, $digerir relojes para services no para las propiedades que pertenecen a la propia prestación del servicio.
  7. 8

    Puede insertar el servicio en $rootScope y ver:

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

    En el controlador:

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

    Otra opción es el uso de una biblioteca externa como: https://github.com/melanke/Watch.JS

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

    Se puede observar los cambios de uno, varios o todos los atributos de objeto.

    Ejemplo:

    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");​
    • YO NO PUEDO CREER QUE ESTA RESPUESTA NO ES LA PARTE MÁS VOTADO!!! Esta es la solución más elegante (OMI), ya que reduce informativo-entropía & probablemente mitiga la necesidad de la Mediación de los manipuladores. Yo iba a votar este más si yo pudiera…
    • La adición de todos sus servicios a los $rootScope, sus beneficios y riesgos potenciales, se detallan un poco aquí: stackoverflow.com/questions/14573023/…
  8. 6

    Usted puede ver los cambios dentro de la misma fábrica y, a continuación, la emisión de un cambio

    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;
    });

    Luego en el controlador:

    angular.module('MyApp').controller('aController', ['$rootScope', function ($rootScope) {
    
        $rootScope.$on('aFactory:keyChanged', function currentCityChanged(event, value) {
            //do something
        });
    }]);

    De esta manera se ponen todos los relacionados con el código de fábrica dentro de su descripción, a continuación, sólo se puede confiar en la emisión de fuera de

    • maldita buena idea!
  9. 6

    ==ACTUALIZADO==

    Muy sencillo ahora en $watch.

    Pen aquí.

    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 + '!');
    });
    }]);
    • me gusta la PostmanService pero, ¿cómo debo cambiar $la función de reloj en el controlador de si tengo que escuchar más de una variable?
    • Hola jedi, gracias por el aviso! He actualizado el lápiz y la respuesta. Recomiendo agregar otra función de reloj para que. Así que, he añadido una nueva característica a PostmanService. Espero que esta ayuda 🙂
    • la forma normal 🙂
    • En realidad, sí lo es 🙂 Si compartes más detalles del problema tal vez yo pueda ayudarle.
  10. 4

    Edificio en dtheodor del respuesta podría utilizar algo similar a la siguiente para asegurarse de que usted no se olvide de cancelar el registro de la devolución de llamada… Algunos pueden oponerse a la aprobación de la $scope a un servicio, aunque.

    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();
    });
    });

    Matriz.remove es un método de extensión que tiene este aspecto:

    /**
    * 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;
    };
  11. 2

    Aquí está mi enfoque genérico.

    mainApp.service('aService',[function(){
    var self = this;
    var callbacks = {};
    this.foo = '';
    this.watch = function(variable, callback) {
    if (typeof(self[variable]) !== 'undefined') {
    if (!callbacks[variable]) {
    callbacks[variable] = [];
    }
    callbacks[variable].push(callback);
    }
    }
    this.notifyWatchersOn = function(variable) {
    if (!self[variable]) return;
    if (!callbacks[variable]) return;
    angular.forEach(callbacks[variable], function(callback, key){
    callback(self[variable]);
    });
    }
    this.changeFoo = function(newValue) {
    self.foo = newValue;
    self.notifyWatchersOn('foo');
    }
    }]);

    En El Controlador

    function FooCtrl($scope, aService) {
    $scope.foo;
    $scope._initWatchers = function() {
    aService.watch('foo', $scope._onFooChange);
    }
    $scope._onFooChange = function(newValue) {
    $scope.foo = newValue;
    }
    $scope._initWatchers();
    }
    FooCtrl.$inject = ['$scope', 'aService'];
  12. 2

    Para aquellos que, como yo buscando una solución simple, esto hace casi exactamente lo que usted espera de el uso normal de $ver en los controladores.
    La única diferencia es, que evalúa la cadena de javascript de contexto y no en un ámbito específico. Usted tendrá que inyectar $rootScope en su servicio, aunque sólo se utilizan para enlazar a los ciclos de digerir correctamente.

    function watch(target, callback, deep) {
    $rootScope.$watch(function () {return eval(target);}, callback, deep);
    };
  13. 2

    mientras se enfrenta a una muy similar problema observé una función en el ámbito y tenían la función de retorno de la variable del servicio. He creado un js violín. usted puede encontrar el código de abajo.

        var myApp = angular.module("myApp",[]);
    myApp.factory("randomService", function($timeout){
    var retValue = {};
    var data = 0;
    retValue.startService = function(){
    updateData();
    }
    retValue.getData = function(){
    return data;
    }
    function updateData(){
    $timeout(function(){
    data = Math.floor(Math.random() * 100);
    updateData()
    }, 500);
    }
    return retValue;
    });
    myApp.controller("myController", function($scope, randomService){
    $scope.data = 0;
    $scope.dataUpdated = 0;
    $scope.watchCalled = 0;
    randomService.startService();
    $scope.getRandomData = function(){
    return randomService.getData();    
    }
    $scope.$watch("getRandomData()", function(newValue, oldValue){
    if(oldValue != newValue){
    $scope.data = newValue;
    $scope.dataUpdated++;
    }
    $scope.watchCalled++;
    });
    });
  14. 2

    Llegué a esta pregunta, pero resultó que mi problema era que estaba usando setInterval, cuando debería haber estado usando el angulares $intervalo de proveedor. Este es también el caso de setTimeout (uso $de tiempo de espera en su lugar). Sé que no es la respuesta para el OP pregunta, pero podría ayudar a algunos, como me ayudó a mí.

    • Usted puede utilizar setTimeout, o cualquier otro no-Angular de la función, pero no te olvides de ajustar el código en la devolución de llamada con $scope.$apply().
  15. 2

    He encontrado una gran solución en el otro hilo con un problema similar, pero totalmente diferente enfoque. Fuente: AngularJS : $ver dentro de la directiva es que no funciona cuando se $rootScope se cambia el valor de

    Básicamente la solución no dice NO uso $watch como es muy pesado solución. Lugar se proponen utilizar $emit y $on.

    Mi problema era reloj una variable en mi servicio y reaccionar en directiva. Y con el método anterior, es muy fácil!

    Mi módulo de servicio/ejemplo:

    angular.module('xxx').factory('example', function ($rootScope) {
    var user;
    return {
    setUser: function (aUser) {
    user = aUser;
    $rootScope.$emit('user:change');
    },
    getUser: function () {
    return (user) ? user : false;
    },
    ...
    };
    });

    Así que básicamente me reloj mi user – cada vez que se establece el nuevo valor de I $emit un user:change estado.

    Ahora en mi caso, en el directiva he utilizado:

    angular.module('xxx').directive('directive', function (Auth, $rootScope) {
    return {
    ...
    link: function (scope, element, attrs) {
    ...
    $rootScope.$on('user:change', update);
    }
    };
    });

    Ahora en el directiva escucho en la $rootScope y en el cambio – reacciono respectivamente. Muy fácil y muy elegante!

  16. 1

    //servicio: (nada especial aquí)

    myApp.service('myService', function() {
    return { someVariable:'abc123' };
    });

    //ctrl:

    myApp.controller('MyCtrl', function($scope, myService) {
    $scope.someVariable = myService.someVariable;
    //watch the service and update this ctrl...
    $scope.$watch(function(){
    return myService.someVariable;
    }, function(newValue){
    $scope.someVariable = newValue;
    });
    });
  17. 1

    Un poquito feo, pero he añadido el registro de variables del ámbito a mi servicio para un toggle:

    myApp.service('myService', function() {
    var self = this;
    self.value = false;
    self.c2 = function(){};
    self.callback = function(){
    self.value = !self.value; 
    self.c2();
    };
    self.on = function(){
    return self.value;
    };
    self.register = function(obj, key){ 
    self.c2 = function(){
    obj[key] = self.value; 
    obj.$apply();
    } 
    };
    return this;
    });

    Y, a continuación, en el controlador:

    function MyCtrl($scope, myService) {
    $scope.name = 'Superhero';
    $scope.myVar = false;
    myService.register($scope, 'myVar');
    }
    • Gracias. Una pequeña pregunta: ¿por qué volver this de ese servicio en lugar de self?
    • Porque los errores que se hacen a veces. 😉
    • Una buena práctica return this; de sus constructores 😉
  18. 1

    Un vistazo a este plunker:: este es el ejemplo más sencillo de lo que podía pensar

    http://jsfiddle.net/HEdJF/

    <div ng-app="myApp">
    <div ng-controller="FirstCtrl">
    <input type="text" ng-model="Data.FirstName"><!-- Input entered here -->
    <br>Input is : <strong>{{Data.FirstName}}</strong><!-- Successfully updates here -->
    </div>
    <hr>
    <div ng-controller="SecondCtrl">
    Input should also be here: {{Data.FirstName}}<!-- How do I automatically updated it here? -->
    </div>
    </div>
    //declare the app with no dependencies
    var myApp = angular.module('myApp', []);
    myApp.factory('Data', function(){
    return { FirstName: '' };
    });
    myApp.controller('FirstCtrl', function( $scope, Data ){
    $scope.Data = Data;
    });
    myApp.controller('SecondCtrl', function( $scope, Data ){
    $scope.Data = Data;
    });
  19. 0

    He visto algunas de las terribles observador patrones de aquí que causa pérdidas de memoria en aplicaciones de gran tamaño.

    Yo podría ser un poco tarde, pero es tan simple como esto.

    La función de reloj de los relojes de los cambios de referencia (tipos primitivos) si quieres ver algo como matriz de empuje simplemente uso:

    someArray.push(someObj); someArray = someArray.splice(0);

    Actualizar la referencia y actualizar el reloj desde cualquier lugar. Incluyendo servicios de un método getter.
    Cualquier cosa que una primitiva se actualizará automáticamente.

  20. 0

    Llego tarde a la parte, pero he encontrado una mejor manera de hacerlo que de la respuesta publicada anteriormente. En lugar de asignar una variable para almacenar el valor de la variable del servicio, he creado una función de adjunto al ámbito de aplicación, que devuelve la variable del servicio.

    controlador de

    $scope.foo = function(){
    return aService.foo;
    }

    Creo que esto va a hacer lo que quiera. Mi controlador mantiene la comprobación de que el valor de mi servicio con esta aplicación. Honestamente, esta es mucho más sencilla que la respuesta seleccionada.

    • por qué fue votada abajo.. también he utilizado una técnica similar a muchas de las veces y no ha funcionado.
  21. 0

    He escrito dos simples utilidad de los servicios que me ayude pista de servicio de las propiedades de los cambios.

    Si desea omitir la larga explicación, usted puede ir estrecho de jsfiddle

    1. WatchObj

    JS:

    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;
    };
    }

    Este servicio es utilizado en el controlador de la siguiente manera:
    Supongamos que usted tiene un servicio de «testService» con las propiedades ‘prop1’, ‘prop2’, ‘prop3’. Quieres ver y asignar el alcance de ‘prop1’ y ‘prop2’. Con el servicio de vigilancia se verá como que:

    JS:

    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. aplicar
      Ver obj es grande, pero no es suficiente si usted tiene un código asíncrono en su servicio. Para ese caso, yo uso una segunda utilidad que se parece a:

    JS:

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

    Me iba a desencadenar en el final de mi async código para activar el $digerir bucle.
    Como que:

    JS:

    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));
    }

    Así que todo junto se verá así (se puede ejecutar o abrir el violín):

    JS:

    //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;
    };
    }

    HTML:

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
    <div class='test' ng-app="app" ng-controller="TestWatch">
    prop1: {{prop1}}
    <br>prop2: {{prop2}}
    <br>prop3 (unwatched): {{prop3}}
    <br>
    <button ng-click="test1()">
    Simple props change
    </button>
    <button ng-click="test2()">
    Async props change
    </button>
    <button ng-click="test3()">
    Async props change with apply
    </button>
    </div>

Dejar respuesta

Please enter your comment!
Please enter your name here