switch - Los componentes de AngularJS 1.5+ no son compatibles con Watchers, ¿en qué consiste el trabajo?
ng-value (7)
He estado actualizando mis directivas personalizadas a la nueva
arquitectura de componentes
.
He leído que los componentes no son compatibles con los observadores.
¿Es esto correcto?
Si es así, ¿cómo detecta cambios en un objeto?
Para un ejemplo básico, tengo un componente personalizado
myBox
que tiene un juego de componentes secundarios con un enlace en el juego.
Si hay un cambio de juego dentro del componente del juego, ¿cómo puedo mostrar un mensaje de alerta dentro de myBox?
Entiendo que hay un método rxJS, ¿es posible hacer esto puramente en angular?
My
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-->
Escribir componentes sin Watchers
Esta respuesta describe cinco técnicas para usar para escribir componentes de AngularJS 1.5 sin usar Watchers
-
Utilice la
directiva
ng-change
-
Use el
$onChanges
de ciclo de vida$onChanges
-
Use el
$doCheck
de ciclo de vida$doCheck
- Comunicación intercomponente con require
- Valores de inserción de un servicio con RxJS
Utilice la directiva
ng-change
¿Qué métodos alternativos están disponibles para observar los cambios de estado de obj sin usar watch en preparación para AngularJs2?
Puede usar la directiva
ng-change
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 primario, 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 padre:
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 oyentes de
$watch
alcanza 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 colocar
$watch
en
$scope
.
Use el
$onChanges
de ciclo de vida
$onChanges
Con la
versión 1.5.3
, AngularJS agregó el
$onChanges
de ciclo de vida
$onChanges
al servicio de
$compile
.
De los documentos:
El controlador puede proporcionar los siguientes métodos que actúan como ganchos de ciclo de vida:
- $ onChanges (changesObj): se llama cada vez que se actualizan los enlaces unidireccionales (
<
) o de interpolación (@
).changesObj
es un hash cuyas claves son los nombres de las propiedades enlazadas que han cambiado, y los valores son un objeto de la forma{ currentValue: ..., previousValue: ... }
. Use este enlace para activar actualizaciones dentro de un componente, como clonar el valor enlazado para evitar la mutación accidental del valor externo.- Referencia de API de directiva integral AngularJS - Ganchos de ciclo de vida
El
$onChanges
se usa para reaccionar a cambios externos en el componente con enlaces
<
unidireccionales.
La directiva
ng-change
se usa para proponer cambios desde el controlador
ng-model
fuera del componente con
&
bindings.
Use el
$doCheck
de ciclo de vida
$doCheck
Con la
versión 1.5.8
, AngularJS agregó el
$doCheck
de ciclo de vida
$doCheck
al servicio de
$compile
.
De los documentos:
El controlador puede proporcionar los siguientes métodos que actúan como ganchos de 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 efecto cuando se llama$onChanges
. Por ejemplo, este enlace podría ser útil si desea realizar una verificación de igualdad profunda o verificar un objeto Date, cuyos cambios no serían detectados por el detector de cambios de Angular y, por lo tanto, no$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 AngularJS - Ganchos de ciclo de vida
Comunicación intercomponente con
require
Las directivas pueden require los controladores de otras directivas permitan la comunicación entre ellas. Esto se puede lograr en un componente al proporcionar una asignación 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 del desarrollador de AngularJS: comunicación intercomponente
Valores de inserción de un servicio con RxJS
¿Qué pasa en una situación en la que tiene un Servicio que tiene el estado en espera, por ejemplo? ¿Cómo podría enviar cambios a ese Servicio, y otros componentes aleatorios en la página estar al tanto de tal cambio? He estado luchando para abordar este problema últimamente
Cree un servicio con RxJS .
<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);
}
};
});
Luego 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 los cambios con
DataService.set
.
La DEMO en PLNKR .
Disponible en IE11, MutationObserver https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver . Necesitas 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 asíncrono, necesitamos 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. Usé el siguiente código para el controlador angularjs para activar el reflujo de gráficos 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'']
})
Muy buena respuesta aceptada, pero podría agregar que también puede usar el poder de los eventos (un poco como en la señal Qt / ranuras si lo desea).
Se transmite un evento:
$rootScope.$broadcast("clickRow", rowId)
por cualquier padre (o incluso controlador
$rootScope.$broadcast("clickRow", rowId)
).
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 algo de inicio de sesión como este (tomado de aquí: https://.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;
});
}
});
Para cualquier persona interesada en mi solución, termino recurriendo a RXJS Observables, que tendrá que usar cuando llegue a Angular 2. Aquí hay un violín que funciona para las comunicaciones entre componentes, me da más control sobre qué mirar.
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);
}
}
}])
Un pequeño aviso sobre el uso de
ng-change
, como se recomienda con la respuesta aceptada, junto con un componente angular 1.5.
En caso de que necesite ver un componente que
ng-model
y
ng-change
no funcionan, puede pasar los parámetros como:
Marcado en qué componente se utiliza:
<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>
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
});
}]
});
$watch
objeto
$watch
está disponible dentro del objeto
$scope
, por lo que debe agregar
$scope
dentro de la función de fábrica de su controlador y luego colocar watcher sobre la variable.
$scope.$watch(function(){
return myBox.game;
}, function(newVal){
alert(''Value changed to ''+ newVal)
});
Nota: Sé que habías convertido la
directive
acomponent
, para eliminar la dependencia de$scope
para que te acerques un paso más a Angular2. Pero parece que no se eliminó para este caso.
Actualizar
Básicamente, angular 1.5 agrega
.component
método jus diferenciar dos funcionalidades diferentes.
Al igual que el
component
realiza un
selector
adición de comportamiento particular, donde, como
directive
se agrega un comportamiento específico al DOM.
La directiva es solo un método envoltorio en
.directive
DDO (objeto de definición de directiva).
Solo lo que puede ver es que eliminaron la función de
link/compile
mientras usaban el método
.component
donde tenían la capacidad de obtener DOM compilado angular.
Utilice el gancho de ciclo de vida
$onChanges
/
$doCheck
gancho de ciclo de vida del componente angular, estos estarán disponibles después de la versión Angular 1.5.3+.
$ onChanges (changesObj) : se llama cada vez que se actualizan los enlaces. ChangeObj es un hash cuyas claves son los nombres de las propiedades enlazadas.
$ doCheck () : se llama en cada turno del ciclo de resumen cuando los cambios de enlace. Brinda la oportunidad de detectar y actuar sobre los cambios.
Al usar la misma función dentro del componente se asegurará de que su código sea compatible para moverse a Angular 2.