angular - spa - ¿Por qué necesitamos `ngDoCheck`
formularios en angular 5 (3)
// se requiere lo siguiente, de lo contrario la vista no se actualizará
this.ref.markForCheck (); ^^^^^^^^^^^^^^^^^^^^^^^^
Hola, Maxim @ AngularInDepth.com La vista se actualiza sin llamar a this.ref.markForCheck () . He probado en consturctor y ngOnInit. Mira esto
Parece que no puedo entender por qué necesito el gancho del ciclo de vida de ngDoCheck
que no sea una simple notificación, en particular, la forma en que escribir un código en el interior hace una diferencia en lo que respecta a la detección de cambios. La mayoría de los ejemplos que he encontrado muestran ejemplos inútiles, como este , con un montón de funciones de registro.
Además, en las clases generadas no veo que se use para otra cosa que no sea una simple notificación:
conmponent / wrapper.ngfactory.js
Wrapper_AppComponent.prototype.ngDoCheck = function(view,el,throwOnChange) {
var self = this;
var changed = self._changed;
self._changed = false;
if (!throwOnChange) {
if (changed) {
jit_setBindingDebugInfoForChanges1(view.renderer,el,self._changes);
self._changes = {};
}
self.context.ngDoCheck(); <----------- this calls ngDoCheck on the component
but the result is not used
anywhere and no params are passed
}
return changed;
};
Este excelente artículo Si cree que ngDoCheck
significa que su componente está siendo revisado, lea este artículo que explica el error en profundidad.
El contenido de esta respuesta se basa en la versión angular 2.xx. Para la versión más reciente 4.xx vea esta publicación .
No hay nada en Internet sobre el funcionamiento interno de la detección de cambios, por lo que tuve que dedicar aproximadamente una semana a la depuración de fuentes, por lo que esta respuesta será bastante técnica en los detalles.
Una aplicación angular es un árbol de views (clase AppView
que se extiende por la clase específica del componente generada por el compilador). Cada vista tiene un modo de detección de cambios que vive en la propiedad cdMode
. El valor predeterminado para cdMode
es ChangeDetectorStatus.CheckAlways
, que es cdMode = 2
.
Cuando se ejecuta un ciclo de detección de cambios, cada vista principal comprueba si debe realizar la detección de cambios en la vista secundaria here :
detectChanges(throwOnChange: boolean): void {
const s = _scope_check(this.clazz);
if (this.cdMode === ChangeDetectorStatus.Checked ||
this.cdMode === ChangeDetectorStatus.Errored)
return;
if (this.cdMode === ChangeDetectorStatus.Destroyed) {
this.throwDestroyedError(''detectChanges'');
}
this.detectChangesInternal(throwOnChange); <---- performs CD on child view
donde this
apunta a la vista child
. Entonces, si cdMode
es ChangeDetectorStatus.Checked=1
, la detección de cambio se omite para el hijo inmediato y todos sus descendientes debido a esta línea.
if (this.cdMode === ChangeDetectorStatus.Checked ||
this.cdMode === ChangeDetectorStatus.Errored)
return;
Lo que hace changeDetection: ChangeDetectionStrategy.OnPush
es simplemente establece cdMode
en ChangeDetectorStatus.CheckOnce = 0
, por lo que después de la primera ejecución de detección de cambios, la vista secundaria tendrá su cdMode
establecido en ChangeDetectorStatus.Checked = 1
debido a este código :
if (this.cdMode === ChangeDetectorStatus.CheckOnce)
this.cdMode = ChangeDetectorStatus.Checked;
Lo que significa que la próxima vez que comience un ciclo de detección de cambios no se realizará ninguna detección de cambios para la vista secundaria.
Hay pocas opciones de cómo ejecutar la detección de cambios para dicha vista. Lo primero es cambiar el cdMode
la vista cdMode
a ChangeDetectorStatus.CheckOnce
, lo que se puede hacer usando this._changeRef.markForCheck()
en el gancho del ciclo de vida de ngDoCheck
:
constructor(private _changeRef: ChangeDetectorRef) { }
ngDoCheck() {
this._changeRef.markForCheck();
}
Esto simplemente cambia cdMode
de la vista actual y sus padres a ChangeDetectorStatus.CheckOnce
, por lo que la próxima vez que se realice la detección de cambios, se verifica la vista actual.
Verifique un ejemplo completo aquí en las fuentes , pero aquí está la esencia de esto:
constructor(ref: ChangeDetectorRef) {
setInterval(() => {
this.numberOfTicks ++
// the following is required, otherwise the view will not be updated
this.ref.markForCheck();
^^^^^^^^^^^^^^^^^^^^^^^^
}, 1000);
}
La segunda opción es llamar a detectChanges
en la vista misma, que here en la vista actual si cdMode
no es ChangeDetectorStatus.Checked
o ChangeDetectorStatus.Errored
. Dado que con onPush
angular establece cdMode
en ChangeDetectorStatus.CheckOnce
, angular ejecutará la detección de cambios.
Por ngDoCheck
tanto, ngDoCheck
no anula el cambio de detección, simplemente se ngDoCheck
en cada ciclo de detección cambiado y el único trabajo es configurar el modo de visualización de cdMode
actual como checkOnce
, de modo que durante el próximo ciclo de detección de cambios se verifique los cambios. Vea esta respuesta para más detalles. Si el modo de detección de cambio de la vista actual es checkAlways
(establecido de forma predeterminada si no se usa la estrategia onPush), ngDocCheck
parece no ser de utilidad.
La interfaz DoCheck
se utiliza para detectar cambios manualmente que la detección de cambios angulares ha pasado por alto. Un uso podría ser cuando cambia la ChangeDetectionStrategy
de cambio de ChangeDetectionStrategy
de su componente, pero sabe que una propiedad de un objeto cambiará.
Es más eficiente verificar este cambio, que dejar que el detector de cambios funcione a través de todo su componente
let obj = {
iChange: ''hiii''
}
Si usa obj.iChange
dentro de su plantilla, angular no lo detectará si este valor cambia, porque la referencia de obj
sí no cambia. ngDoCheck
implementar un ngDoCheck
para verificar si el valor ha cambiado y llamar a detectChanges
en el detectChanges
de su componente.
De la documentación angular sobre DoCheck
Mientras que el gancho
ngDoCheck
puede detectar cuando el nombre del héroe ha cambiado, tiene un costo espantoso. Este gancho se llama con una frecuencia enorme, después de cada ciclo de detección de cambios, sin importar dónde ocurrió el cambio. Se llama más de veinte veces en este ejemplo antes de que el usuario pueda hacer algo.La mayoría de estas comprobaciones iniciales son activadas por la primera representación de Angular de datos no relacionados en otra parte de la página. El mero hacer clic en otro cuadro de entrada activa una llamada. Relativamente pocas llamadas revelan cambios reales a los datos pertinentes. Claramente, nuestra implementación debe ser muy liviana o la experiencia del usuario se verá afectada.
ejemplo probado
@Component({
selector: ''test-do-check'',
template: `
<div [innerHtml]="obj.changer"></div>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class TestDoCheckComponent implements DoCheck, OnInit {
public obj: any = {
changer: 1
};
private _oldValue: number = 1;
constructor(private _changeRef: ChangeDetectorRef){}
ngOnInit() {
setInterval(() => {
this.obj.changer += 1;
}, 1000);
}
ngDoCheck() {
if(this._oldValue !== this.obj.changer) {
this._oldValue = this.obj.changer;
//disable this line to see the counter not moving
this._changeRef.detectChanges();
}
}
}