Escribir componentes sin observadores
Esta respuesta describe cinco técnicas que se pueden usar para escribir componentes de AngularJS 1.5 sin usar observadores.
Utilice la ng-change
directiva
¿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-change
directiva 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 AngularJS de usar $watch
no es escalable porque es una estrategia de sondeo. Cuando el número de $watch
oyentes alcanza alrededor de 2000, la interfaz de usuario se vuelve lenta. La estrategia en Angular 2 es hacer que la colocación del marco más reactivo y evitar $watch
en $scope
.
Utilice el $onChanges
gancho de ciclo de vida
Con la versión 1.5.3 , AngularJS agregó el enlace del $onChanges
ciclo de vida al $compile
servicio.
De los Docs:
El controlador puede proporcionar los siguientes métodos que actúan como ganchos del ciclo de vida:
- $ onChanges (changesObj): se llama siempre que se actualizan los enlaces unidireccionales (
<
) o interpolación ( @
). El changesObj
es un hash cuyas claves son los nombres de las propiedades enlazadas que han cambiado y los valores son un objeto del formulario { currentValue: ..., previousValue: ... }
. Utilice este enlace para activar actualizaciones dentro de un componente, como la clonación del valor vinculado para evitar la mutación accidental del valor externo.
- Referencia de API de directiva integral de AngularJS - Ganchos de ciclo de vida
El $onChanges
gancho se utiliza para reaccionar a cambios externos en el componente con <
enlaces unidireccionales. La ng-change
directiva se utiliza para propagar cambios desde el ng-model
controlador fuera del componente con &
enlaces.
Utilice el $doCheck
gancho de ciclo de vida
Con la versión 1.5.8 , AngularJS agregó el enlace$doCheck
del ciclo de vida al $compile
servicio.
De los Docs:
El controlador puede proporcionar los siguientes métodos que actúan como ganchos del ciclo de vida:
$doCheck()
- Llamado en cada turno del ciclo de digestión. Brinda la oportunidad de detectar cambios y actuar sobre ellos. Cualquier acción que desee realizar en respuesta a los cambios que detecte debe invocarse desde este gancho; implementar esto no tiene ningún efecto sobre cuándo $onChanges
se llama. Por ejemplo, este gancho podría ser útil si desea realizar una verificación de igualdad profunda, o para verificar un objeto Date, 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 de API de directiva integral de AngularJS - Ganchos de ciclo de vida
Comunicación entre componentes con require
Las directivas pueden requerir que los controladores de otras directivas permitan la comunicación entre ellos. Esto se puede lograr en un componente proporcionando un mapeo de objetos para la propiedad require . Las claves de objeto especifican los nombres de propiedad bajo los cuales los controladores requeridos (valores de objeto) estarán vinculados 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
Enviar valores desde un servicio con RxJS
¿Qué pasa en una situación en la que tiene un Servicio que tiene un estado, por ejemplo? ¿Cómo podría impulsar cambios en ese Servicio y otros componentes aleatorios en la página estar al tanto de dicho cambio? Últimamente he estado luchando para abordar este problema
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íbase 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 con DataService.subscribe
y los productores pueden impulsar cambios con DataService.set
.
La DEMO en PLNKR .
myBox.game
variable?Service
estado de espera, por ejemplo? ¿Cómo podría impulsar cambios en ese Servicio y otros componentes aleatorios en la página estar al tanto de tal cambio? Últimamente$watch
El objeto está disponible dentro del$scope
objeto, por lo que debe agregar$scope
la función de fábrica de su controlador y luego colocar el observador sobre la variable.$scope.$watch(function(){ return myBox.game; }, function(newVal){ alert('Value changed to '+ newVal) });
Demo aquí
Actualizar
Básicamente, el
.component
método angular 1.5 solo diferencia dos funcionalidades diferentes. Likecomponent
.stands para realizar la adición de comportamientos particularesselector
, mientras que asdirective
son para añadir comportamientos específicos a DOM. La directiva es solo un método de envoltura en.directive
DDO (objeto de definición de directiva). Solo lo que puede ver es que tenían lalink/compile
función de eliminación mientras usaban el.component
método en el que tenía la capacidad de obtener DOM compilado angular.Hacer uso
$onChanges
/$doCheck
gancho del ciclo de vida del ciclo de vida del componente angular de gancho, los estará disponible después angular versión 1.5.3+.Al usar la misma función dentro del componente, garantizará que su código sea compatible para pasar a Angular 2.
fuente
observable
deRxJS
, eso sería bueno compatible conAngular2
Para cualquiera que esté interesado en mi solución, termino recurriendo a RXJS Observables, que lo 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); } } }])
fuente
Un pequeño aviso sobre el uso de
ng-change
, como se recomienda con la respuesta aceptada, junto con un componente angular de 1,5.En caso de que necesite vigilar un componente que
ng-model
yng-change
no funcione, puede pasar los parámetros como:Marcado en el que se utiliza el componente:
<my-component on-change="$ctrl.doSth()" field-value="$ctrl.valueToWatch"> </my-component>
Componente js:
angular .module('myComponent') .component('myComponent', { bindings: { onChange: '&', fieldValue: '=' } });
Marcado de componentes:
<select ng-model="$ctrl.fieldValue" ng-change="$ctrl.onChange()"> </select>
fuente
Disponible en IE11, MutationObserver https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver . Debe inyectar el servicio $ element en el controlador que semi-rompe la separación DOM / controlador, pero creo que esta es una excepción fundamental (es decir, falla) en angularjs. Dado que hide / show es asincrónico, necesitamos una devolución de llamada en el show, que angularjs y angular-bootstrap-tab no proporcionan. También requiere que sepa qué elemento DOM específico desea observar. Utilicé el siguiente código para el controlador angularjs para activar el reflujo del gráfico Highcharts en el programa.
const myObserver = new MutationObserver(function (mutations) { const isVisible = $element.is(':visible') // Requires jquery if (!_.isEqual(isVisible, $element._prevIsVisible)) { // Lodash if (isVisible) { $scope.$broadcast('onReflowChart') } $element._prevIsVisible = isVisible } }) myObserver.observe($element[0], { attributes: true, attributeFilter: ['class'] })
fuente
$scope
y$rootScope
. Considere usar RxJS en su lugar para emisores y suscriptores de eventos.Realmente buena respuesta aceptada, pero debo agregar que también puede usar el poder de los eventos (un poco como en la señal / ranuras Qt si lo desea).
Un evento es transmitido:
$rootScope.$broadcast("clickRow", rowId)
por cualquier padre (o incluso controlador secundario). Luego, en su controlador puede manejar el evento de esta manera:$scope.$on("clickRow", function(event, data){ // do a refresh of the view with data == rowId });
También puede agregar algunos inicios de sesión como este (tomado de aquí: https://stackoverflow.com/a/34903433/3147071 )
var withLogEvent = true; // set to false to avoid events logs app.config(function($provide) { if (withLogEvent) { $provide.decorator("$rootScope", function($delegate) { var Scope = $delegate.constructor; var origBroadcast = Scope.prototype.$broadcast; var origEmit = Scope.prototype.$emit; Scope.prototype.$broadcast = function() { console.log("$broadcast was called on $scope " + this.$id + " with arguments:", arguments); return origBroadcast.apply(this, arguments); }; Scope.prototype.$emit = function() { console.log("$emit was called on $scope " + this.$id + " with arguments:", arguments); return origEmit.apply(this, arguments); }; return $delegate; }); } });
fuente
$scope
y$rootScope
. Considere usar RxJS en su lugar para emisores de eventos y suscriptores.Voy tarde. Pero puede ayudar a otras personas.
app.component("headerComponent", { templateUrl: "templates/header/view.html", controller: ["$rootScope", function ($rootScope) { let $ctrl = this; $rootScope.$watch(() => { return $ctrl.val; }, function (newVal, oldVal) { // do something }); }] });
fuente