Los componentes de AngularJS 1.5+ no son compatibles con Watchers, ¿cuál es la solución?

Resuelto Ka Tech asked hace 8 años • 7 respuestas

He estado actualizando mis directivas personalizadas a la nueva arquitectura de componentes . He leído que los componentes no admiten observadores. ¿Es esto correcto? Si es así, ¿cómo se detectan cambios en un objeto? Para un ejemplo básico, tengo un componente personalizado myBoxque tiene un componente secundario de juego con un enlace al juego. Si hay un cambio de juego dentro del componente del juego, ¿cómo muestro un mensaje de alerta dentro de myBox? Entiendo que existe el método rxJS. ¿Es posible hacer esto exclusivamente en angular? Mi JSFiddle

javascript

var app = angular.module('myApp', []);
app.controller('mainCtrl', function($scope) {

   $scope.name = "Tony Danza";

});

app.component("myBox",  {
      bindings: {},
      controller: function($element) {
        var myBox = this;
        myBox.game = 'World Of warcraft';
        //IF myBox.game changes, show alert message 'NAME CHANGE'
      },
      controllerAs: 'myBox',
      templateUrl: "/template",
      transclude: true
})
app.component("game",  {
      bindings: {game:'='},
      controller: function($element) {
        var game = this;


      },
      controllerAs: 'game',
      templateUrl: "/template2"
})

HTML

<div ng-app="myApp" ng-controller="mainCtrl">
  <script type="text/ng-template" id="/template">
    <div style='width:40%;border:2px solid black;background-color:yellow'>
      Your Favourite game is: {{myBox.game}}
      <game game='myBox.game'></game>
    </div>
  </script>

 <script type="text/ng-template" id="/template2">
    <div>
    </br>
        Change Game
      <textarea ng-model='game.game'></textarea>
    </div>
  </script>

  Hi {{name}}
  <my-box>

  </my-box>

</div><!--end app-->
Ka Tech avatar Feb 21 '16 16:02 Ka Tech
Aceptado

Componentes de escritura sin observadores

Esta respuesta describe cinco técnicas que se pueden utilizar para escribir componentes de AngularJS 1.5 sin utilizar observadores.

  • Utilice la ng-changedirectiva
  • Utilice el $onChangesgancho del ciclo de vida
  • Utilice el $doCheckgancho del ciclo de vida
  • Comunicación intercomponente con require
  • Insertar valores desde un servicio con RxJS

Utilice la ng-changedirectiva

¿Qué métodos alternativos están disponibles para observar los cambios de estado de obj sin usar watch en preparación para AngularJs2?

Puede utilizar la ng-changedirectiva para reaccionar a los cambios de entrada.

<textarea ng-model='game.game' 
          ng-change="game.textChange(game.game)">
</textarea>

Y para propagar el evento a un componente principal, el controlador de eventos debe agregarse como un atributo del componente secundario.

<game game='myBox.game' game-change='myBox.gameChange($value)'></game>

js

app.component("game",  {
      bindings: {game:'=',
                 gameChange: '&'},
      controller: function() {
        var game = this;
        game.textChange = function (value) {
            game.gameChange({$value: value});
        });

      },
      controllerAs: 'game',
      templateUrl: "/template2"
});

Y en el componente principal:

myBox.gameChange = function(newValue) {
    console.log(newValue);
});

Este es el método preferido en el futuro. La estrategia de uso de AngularJS $watchno es escalable porque es una estrategia de sondeo. Cuando el número de $watchoyentes llega a alrededor de 2000, la interfaz de usuario se vuelve lenta. La estrategia en Angular 2 es hacer que el marco sea más reactivo y evitar $watchcolocarlo $scope.


Utilice el $onChangesgancho del ciclo de vida

Con la versión 1.5.3 , AngularJS agregó el $onChangesenlace de ciclo de vida al $compileservicio.

De los documentos:

El controlador puede proporcionar los siguientes métodos que actúan como enlaces del ciclo de vida:

  • $onChanges(changesObj): se llama cada vez que se actualizan enlaces unidireccionales ( <) o de interpolación ( ). @Es changesObjun hash cuyas claves son los nombres de las propiedades vinculadas que han cambiado y los valores son un objeto de la forma { currentValue: ..., previousValue: ... }. Utilice este enlace para activar actualizaciones dentro de un componente, como clonar el valor vinculado para evitar la mutación accidental del valor externo.

— Referencia API de directiva integral de AngularJS: enlaces de ciclo de vida

El $onChangesgancho se utiliza para reaccionar a cambios externos en el componente con <fijaciones unidireccionales. La ng-changedirectiva se utiliza para propagar cambios desde el ng-modelcontrolador fuera del componente con &enlaces.


Utilice el $doCheckgancho del ciclo de vida

Con la versión 1.5.8 , AngularJS agregó el $doCheckenlace de ciclo de vida al $compileservicio.

De los documentos:

El controlador puede proporcionar los siguientes métodos que actúan como enlaces del ciclo de vida:

  • $doCheck()- Se llama en cada turno del ciclo de resumen. Brinda la oportunidad de detectar y actuar sobre los cambios. Cualquier acción que desee realizar en respuesta a los cambios que detecte debe invocarse desde este enlace; implementar esto no tiene ningún efecto sobre cuándo $onChangesse llama. Por ejemplo, este gancho podría ser útil si desea realizar una verificación de igualdad profunda o verificar un objeto Fecha cuyos cambios no serían detectados por el detector de cambios de Angular y, por lo tanto, no se activarían $onChanges. Este gancho se invoca sin argumentos; Si detecta cambios, debe almacenar los valores anteriores para compararlos con los valores actuales.

— Referencia API de directiva integral de AngularJS: enlaces de ciclo de vida


Comunicación intercomponente conrequire

Las directivas pueden exigir que los controladores de otras directivas permitan la comunicación entre sí. Esto se puede lograr en un componente proporcionando una asignación de objetos para la propiedad requerida . Las claves de objeto especifican los nombres de propiedad bajo los cuales los controladores requeridos (valores de objeto) se vincularán al controlador del componente requerido.

app.component('myPane', {
  transclude: true,
  require: {
    tabsCtrl: '^myTabs'
  },
  bindings: {
    title: '@'
  },
  controller: function() {
    this.$onInit = function() {
      this.tabsCtrl.addPane(this);
      console.log(this);
    };
  },
  templateUrl: 'my-pane.html'
});

Para obtener más información, consulte la Guía para desarrolladores de AngularJS: comunicación entre componentes


Insertar valores desde un servicio con RxJS

¿Qué pasa en una situación en la que tienes un Servicio que mantiene el estado, por ejemplo? ¿Cómo podría impulsar cambios en ese Servicio y que otros componentes aleatorios de la página estén al tanto de dicho cambio? He estado luchando para abordar este problema últimamente.

Cree un servicio con extensiones RxJS para Angular .

<script src="//unpkg.com/angular/angular.js"></script>
<script src="//unpkg.com/rx/dist/rx.all.js"></script>
<script src="//unpkg.com/rx-angular/dist/rx.angular.js"></script>
var app = angular.module('myApp', ['rx']);

app.factory("DataService", function(rx) {
  var subject = new rx.Subject(); 
  var data = "Initial";

  return {
      set: function set(d){
        data = d;
        subject.onNext(d);
      },
      get: function get() {
        return data;
      },
      subscribe: function (o) {
         return subject.subscribe(o);
      }
  };
});

Entonces simplemente suscríbete a los cambios.

app.controller('displayCtrl', function(DataService) {
  var $ctrl = this;

  $ctrl.data = DataService.get();
  var subscription = DataService.subscribe(function onNext(d) {
      $ctrl.data = d;
  });

  this.$onDestroy = function() {
      subscription.dispose();
  };
});

Los clientes pueden suscribirse a los cambios DataService.subscribey los productores pueden impulsar los cambios con DataService.set.

La DEMOSTRACIÓN de PLNKR .

georgeawg avatar Feb 21 '2016 11:02 georgeawg

$watchEl objeto está disponible dentro $scopedel objeto, por lo que debe agregar $scopela función de fábrica dentro de su controlador y luego colocar el observador sobre la variable.

$scope.$watch(function(){
    return myBox.game;
}, function(newVal){
   alert('Value changed to '+ newVal)
});

Demostración aquí

Nota: Sé que se había convertido directivea component, para eliminar la dependencia $scopede modo que pueda acercarse un paso más a Angular2. Pero parece que no se eliminó en este caso.

Actualizar

Básicamente, angular 1.5 agrega .componentun método para diferenciar dos funciones diferentes. Like component.significa realizar un comportamiento particular agregando selector, donde as directivesignifica agregar un comportamiento específico a DOM. La directiva es solo un método contenedor en .directiveDDO (objeto de definición de directiva). Lo único que puede ver es que tenían link/compilela función de eliminación mientras usaban .componentun método en el que tenían la capacidad de obtener DOM compilado angular.

Utilice el gancho $onChanges/ $doCheckciclo de vida del gancho del ciclo de vida del componente Angular, estarán disponibles después de la versión Angular 1.5.3+.

$onChanges(changesObj) : se llama cada vez que se actualizan los enlaces. El changeObj es un hash cuyas claves son los nombres de las propiedades vinculadas.

$doCheck() : se llama en cada turno del ciclo de resumen cuando cambia el enlace. Brinda la oportunidad de detectar y actuar sobre los cambios.

El uso de la misma función dentro del componente garantizará que su código sea compatible para pasar a Angular 2.

Pankaj Parkar avatar Feb 21 '2016 10:02 Pankaj Parkar

Para cualquiera interesado en mi solución, termino recurriendo a RXJS Observables, que tendrás que usar cuando llegues a Angular 2. Aquí hay un violín que funciona para las comunicaciones entre componentes, me da más control sobre qué mirar.

JS FIDDLE RXJS Observables

class BoxCtrl {
    constructor(msgService) {
    this.msgService = msgService
    this.msg = ''

    this.subscription = msgService.subscribe((obj) => {
      console.log('Subscribed')
      this.msg = obj
    })
    }

  unsubscribe() {
    console.log('Unsubscribed')
    msgService.usubscribe(this.subscription)
  }
}

var app = angular
  .module('app', ['ngMaterial'])
  .controller('MainCtrl', ($scope, msgService) => {
    $scope.name = "Observer App Example";
    $scope.msg = 'Message';
    $scope.broadcast = function() {
      msgService.broadcast($scope.msg);
    }
  })
  .component("box", {
    bindings: {},
    controller: 'BoxCtrl',
    template: `Listener: </br>
    <strong>{{$ctrl.msg}}</strong></br>
    <md-button ng-click='$ctrl.unsubscribe()' class='md-warn'>Unsubscribe A</md-button>`
  })
  .factory('msgService', ['$http', function($http) {
    var subject$ = new Rx.ReplaySubject();
    return {
      subscribe: function(subscription) {
        return subject$.subscribe(subscription);
      },
      usubscribe: function(subscription) {
        subscription.dispose();
      },
      broadcast: function(msg) {
        console.log('success');
        subject$.onNext(msg);
      }
    }
  }])
Ka Tech avatar Jul 06 '2016 06:07 Ka Tech