javascript - que - observables angular 5
ChangeDetectionStrategy.OnPush y Observable.suscribirse en Angular 2 (3)
Estoy tratando de ChangeDetectionStrategy.OnPush
mejores prácticas cuando uso Observables junto con ChangeDetectionStrategy.OnPush
.
El ejemplo demuestra el escenario común de querer mostrar algún tipo de mensaje de carga (o una animación de hilandero simple, tal vez):
@Component({
selector: ''my-app'',
template: `Are we loading?: {{loadingMessage}}`,
// Obviously "Default" will notice the change in `loadingMessage`...
// changeDetection: ChangeDetectionStrategy.Default
// But what is best practice when using OnPush?
changeDetection: ChangeDetectionStrategy.OnPush
})
export class App implements OnInit {
private loadingMessage = "Wait for it...";
constructor() {
}
ngOnInit() {
// Pretend we''re loading data from a service.
// This component is only interested in the call''s success
Observable.of(true)
.delay(2000)
.subscribe(success => {
if(success){
console.log(''Pretend loading: success!'');
// OnPush won''t detect this change
this.loadingMessage = ''Success!'';
}
});
}
}
Comprendo más o menos el requisito de inmutabilidad con OnPush
y, al menos para mí, tiene sentido cuando se habla de datos reales del modelo (probablemente guardados en algún tipo de tienda).
Entonces, tengo dos preguntas:
- ¿Por qué la asignación del nuevo valor de cadena
''Success!''
activar el detector de cambio? En lo que respecta a la inmutabilidad, el valor ha cambiado, ¿verdad? - ¿Cómo debe implementarse el componente interno liviano (es decir,
loadingMessage
) al usarChangeDetectionStrategy.OnPush
? Si hay varias prácticas recomendadas, indíqueme la dirección correcta.
AFAIK, OnPush
se usa cuando se trabaja directamente con observables :
//our root app component
import {Component, OnInit, ChangeDetectionStrategy} from ''angular2/core''
import {Observable} from ''rxjs/Observable'';
import ''rxjs/Rx'';
@Component({
selector: ''my-app'',
template: `Are we loading?: {{ loadingMessage |async }}`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class App implements OnInit {
private loadingMessage;
constructor() { }
ngOnInit() {
this.loadingMessage = Observable.of(true)
.delay(2000)
}
}
Buena pregunta. Tengo dos citas de Savkin sobre onPush
(ya que los documentos de Angular.io todavía no parecen tener información sobre este tema):
El marco verificará los componentes de
OnPush
solo cuando cambien sus entradas o las plantillas de los componentes emitan eventos. - refAl usar
OnPush
, Angular solo verificará el componente cuando cualquiera de sus propiedades de entrada cambie, cuando se active un evento o cuando un observable active un evento. - ref (en un comentario, respuesta a @vivainio)
La segunda cita parece más completa. (Lástima que fue enterrado en un comentario!)
¿Por qué la asignación del nuevo valor de cadena no es
Success!
activar el detector de cambio? En lo que respecta a la inmutabilidad, el valor ha cambiado, ¿verdad?
OnPush
inmutabilidad de OnPush
se refiere a propiedades de entrada, no propiedades de instancia normales. Si loadingMessage
fuera una propiedad de entrada y el valor cambiara, la detección de cambios se ejecutaría en el componente. (Ver la respuesta de @Vlado para Plunker).
¿Cómo debe implementarse el componente interno liviano (es decir,
loadingMessage
) al usarChangeDetectionStrategy.OnPush
? Si hay varias prácticas recomendadas, indíqueme la dirección correcta.
Esto es lo que sé hasta ahora:
- Si cambiamos el estado interno como parte del manejo de un evento, o como parte de un disparo observable, la detección de cambios se ejecuta en el componente
OnPush
(es decir, la vista se actualizará). En su caso particular, me sorprendió que la vista no se actualizara. Aquí hay dos conjeturas sobre por qué no:- Agregar
delay()
hace que Angular lo vea más como unsetTimeout()
lugar de un cambio observable.setTimeout
s no da como resultado la ejecución de detección de cambios en un componenteOnPush
. En su ejemplo, Change Detector ha completado su trabajo 2 segundos antes de que seloadingMessage
el valor deloadingMessage
. - Al igual que @Sasxa muestra en su respuesta, debe tener un enlace en la plantilla para el
Observable
. Es decir, tal vez es algo más que "un incendio observable" ... tal vez tiene que ser un observable atado que dispare. En ese caso, la creación deloadingMessage
(o incluso una propiedad destate
más genérica) como unObservable
mismo le permitirá vincular su plantilla a su valor (o múltiples valores asíncronos), vea este ejemplo plnkr .
Actualización 2016-04-28: parece que el enlace debe incluir| async
| async
, como se muestra en el plnkr, y en este plnkr .
- Agregar
- Si cambiamos el estado interno y no es parte del manejo de eventos o de un disparo observable, podemos inyectar
ChangeDetectorRef
en nuestro componente y llamar al métodomarkForCheck()
para que la detección de cambios se ejecute en el componenteOnPush
y todos los componentes antecesores hasta la raíz componente. Si solo se cambia el estado de la vista (es decir, el estado que es local para el componente y tal vez sus descendientes), en sudetectChanges()
puede usarsedetectChanges()
, que no marcará todos los componentes ancestrales para la detección de cambios. Plunker
Con esta ChangeDetectionStrategy.OnPush
le está diciendo a su componente que solo escuche los cambios en sus propiedades de entrada.
Agregué el componente de carga a su ejemplo solo para mostrarle cómo funciona: http://plnkr.co/edit/k5tkmcLlzkwz4t5ifqFD?p=preview