¿Por qué necesitamos `ngDoCheck`
Parece que no puedo entender por qué necesito ngDoCheck
gancho de ciclo de vida que no sea para una simple notificación, particularmente cómo escribir código dentro de él hace una diferencia con respecto 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 funcionalidad de registro.
Además, en las clases generadas no veo que se use para otra cosa que no sea 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;
};
3 answers
Este gran artículo Si cree que ngDoCheck
significa que su componente está siendo revisado, lea este artículo explica el error en profundidad.
El contenido de esta respuesta se basa en la versión angular 2.x. x. Para la versión más reciente 4.x. x ver este post.
No hay nada en Internet sobre el funcionamiento interno de la detección de cambios, así que tuve que pasar aproximadamente una semana depurando fuentes, por lo que esta respuesta será bastante técnica en detalles.
An la aplicación angular es un árbol de vistas (AppView
clase extendida 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 aquí :
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
. Así que 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 establecer cdMode
a ChangeDetectorStatus.CheckOnce = 0
, por lo que después de la primera ejecución de detección de cambios, la vista hija 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 se inicie 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 detección de cambio para dicha vista. Primero es cambiar la vista secundaria cdMode
a ChangeDetectorStatus.CheckOnce
, lo que se puede hacer usando this._changeRef.markForCheck()
en ngDoCheck
gancho de ciclo de vida:
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 comprobará la vista actual.
Mira un ejemplo completo aquí en las fuentes , pero aquí está la esencia:
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 propia vista que se ejecutará cambie la detección en la vista actual si cdMode
no es ChangeDetectorStatus.Checked
o ChangeDetectorStatus.Errored
. Dado que con onPush
angular establece cdMode
a ChangeDetectorStatus.CheckOnce
, angular ejecutará la detección de cambios.
Así que ngDoCheck
no anula la detección cambiada, simplemente se llama en cada ciclo de detección cambiado y su único trabajo es establecer la vista actual cdMode
como checkOnce
, de modo que durante el siguiente ciclo de detección de cambios se verifique los cambios. Ver esta respuesta para más detalles. Si el modo de detección de cambio de la vista actual es checkAlways
(establecido por defecto si no se usa la estrategia OnPush), ngDocCheck
parece no ser de utilidad.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-09 10:48:03
La interfaz DoCheck
se utiliza para detectar manualmente los cambios que la detección de cambio angular ha pasado por alto. Un uso podría ser cuando cambias el ChangeDetectionStrategy
de tu componente, pero sabes que una propiedad de un objeto cambiará.
Es más eficiente verificar este cambio que dejar que el changeDetector se ejecute a través de todo el componente
let obj = {
iChange: 'hiii'
}
Si utiliza obj.iChange
dentro de su plantilla, angular no lo detectará si este valor cambia, porque el la referencia de obj
no cambia. Necesitas implementar un ngDoCheck
para comprobar si el valor ha cambiado, y llamar a un detectChanges
en el changeDetector de tu componente.
De la documentación de angular acerca de 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 se produjo el cambio. Se llama más de veinte veces en este ejemplo antes de que el usuario pueda hacer nada.La mayoría de estas comprobaciones iniciales se activan por la primera representación de Angular de datos no relacionados en otra parte de la página. El mero mousing en otro cuadro de entrada desencadena una llamada. Relativamente pocas llamadas revelan cambios reales en los datos pertinentes. Claramente nuestra implementación debe ser muy ligera 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();
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-07 09:20:21
/ / se requiere lo siguiente, de lo contrario la vista no se actualizará
Esto.ref.markForCheck(); ^^^^^^^^^^^^^^^^^^^^^^^^
Hola, Maxim @AngularInDepth.com View se actualiza sin llamar a esto.ref.markForCheck () . He probado en consturctor y ngOnInit. Comprueba esto
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-03 05:12:33