¿Cuál es la diferencia entre markForCheck () y detectChanges ()

¿Cuál es la diferencia entre ChangeDetectorRef.markForCheck() y ChangeDetectorRef.detectChanges() ?

Solo encontré información sobre SO en cuanto a la diferencia entre NgZone.run() , pero no entre estas dos funciones.

Para respuestas con solo una referencia al documento, por favor ilustra algunos escenarios prácticos para elegir uno sobre el otro.

De documentos:

detectChanges (): vacío

Verifica el detector de cambio y sus hijos.

Esto significa que, si hay un caso en el que algo dentro de su modelo (su clase) ha cambiado pero no ha reflejado la vista, es posible que necesite notificar a Angular para detectar esos cambios (detectar cambios locales) y actualizar la vista.

Posibles escenarios podrían ser:

1- El detector de cambio está separado de la vista (ver separación )

2- Ha ocurrido una actualización pero no ha estado dentro de la Zona Angular, por lo tanto, Angular no lo sabe.

Como cuando una función de un tercero ha actualizado su modelo y desea actualizar la vista después de eso.

  someFunctionThatIsRunByAThirdPartyCode(){ yourModel.text = "new text"; } 

Debido a que este código se encuentra fuera de la zona de Angular (probablemente), lo más probable es que necesite asegurarse de detectar los cambios y actualizar la vista, por lo tanto:

  myFunction(){ someFunctionThatIsRunByAThirdPartyCode(); // Let's detect the changes that above function made to the model which Angular is not aware of. this.cd.detectChanges(); } 

NOTA :

Hay otras maneras de hacer el trabajo anterior, en otras palabras, hay otras formas de llevar ese cambio al ciclo de cambio angular.

** Podría ajustar esa función de terceros dentro de una zona.run:

  myFunction(){ this.zone.run(this.someFunctionThatIsRunByAThirdPartyCode); } 

** Puedes ajustar la función dentro de un setTimeout:

 myFunction(){ setTimeout(this.someFunctionThatIsRunByAThirdPartyCode,0); } 

3- También hay casos en los que actualiza el modelo una vez que finaliza el change detection cycle , donde en esos casos aparece este temido error:

“La expresión ha cambiado después de haber sido revisada”;

Esto generalmente significa (del lenguaje Angular2):

Vi un cambio en su modelo que fue causado por una de mis formas aceptadas (eventos, solicitudes XHR, setTimeout y …) y luego ejecuté mi detección de cambios para actualizar su vista y la terminé, pero luego hubo otra función en su código que actualizó el modelo de nuevo y no quiero volver a ejecutar mi detección de cambios porque ya no hay verificación sucia como AngularJS: D y deberíamos usar un flujo de datos de una sola dirección.

Definitivamente encontrarás este error: P.

Un par de formas de arreglarlo:

1- Manera correcta : asegúrese de que la actualización esté dentro del ciclo de detección de cambios (las actualizaciones Angular2 son de un solo sentido que ocurren una vez, no actualice el modelo después de eso y mueva su código a un lugar / hora mejor).

2- Camino flojo : ejecuta detectChanges () después de esa actualización para hacer que angular2 sea feliz, definitivamente esta no es la mejor manera, pero cuando preguntas cuáles son los escenarios posibles, este es uno de ellos.

De esta forma estás diciendo: sé sinceramente que ejecutaste la detección de cambios, pero quiero que lo hagas de nuevo porque tuve que actualizar algo sobre la marcha después de que terminaste la verificación.

3- Ponga el código dentro de un setTimeout , porque setTimeout está parchado por zona y ejecutará detectChanges después de que haya finalizado.


De los documentos

 markForCheck() : void 

Marca todos los antecesores de ChangeDetectionStrategy para ser revisados.

Esto es más necesario cuando ChangeDetectionStrategy de su componente es OnPush .

OnPush significa que solo ejecuta la detección de cambios si alguna de estas cosas sucedió:

1- Una de las @inputs del componente ha sido completamente reemplazada por un nuevo valor, o simplemente put, si la referencia de la propiedad @Input ha cambiado por completo.

Entonces, si ChangeDetectionStrategy de su componente es OnPush, entonces tiene:

  var obj = { name:'Milad' }; 

Y luego lo actualiza / muta como:

  obj.name = "a new name"; 

Esto no actualizará la referencia obj , por lo tanto, la detección de cambio no se ejecutará, por lo tanto, la vista no refleja la actualización / mutación.

En este caso, debe decirle manualmente a Angular que verifique y actualice la vista (markForCheck);

Entonces si hiciste esto:

  obj.name = "a new name"; 

Usted necesita hacer ésto:

  this.cd.markForCheck(); 

Por el contrario, abajo podría provocar que se ejecute una detección de cambio:

  obj = { name:"a new name" }; 

Que reemplazó por completo el obj anterior con un {} nuevo;

2- Un evento se ha disparado, como un clic o algo así o cualquiera de los componentes secundarios ha emitido un evento.

Eventos como:

  • Hacer clic
  • Tecla Arriba
  • Eventos de suscripción
  • etc.

En resumen:

  • Use detectChanges() cuando haya actualizado el modelo después de que angular haya ejecutado su detección de cambios, o si la actualización no ha estado en absoluto en el mundo angular.

  • Use markForCheck() si está usando OnPush y está pasando por alto ChangeDetectionStrategy al ChangeDetectionStrategy algunos datos o ha actualizado el modelo dentro de un setTimeout ;

La mayor diferencia entre los dos es que detectChanges() realmente activa la detección de cambios, mientras que markForCheck() no activa la detección de cambios.

detectar Cambios

Este se usa para ejecutar detección de cambios para el árbol de componentes que comienza con el componente en el que desencadena detectChanges() . Entonces la detección de cambio se ejecutará para el componente actual y todos sus elementos secundarios. Angular contiene referencias al árbol de componentes raíz en ApplicationRef y, cuando ocurre una operación asíncrona, desencadena la detección de cambios en este componente raíz a través de un método de contenedor tick() :

 @Injectable() export class ApplicationRef_ extends ApplicationRef { ... tick(): void { if (this._runningTick) { throw new Error('ApplicationRef.tick is called recursively'); } const scope = ApplicationRef_._tickScope(); try { this._runningTick = true; this._views.forEach((view) => view.detectChanges()); <------------------ 

view aquí es la vista del componente raíz. Puede haber muchos componentes raíz como describí en el capítulo ¿Cuáles son las implicaciones de arrancar múltiples componentes ?

@milad describió las razones por las que potencialmente podría necesitar activar la detección de cambios manualmente.

markForCheck

Como dije, este tipo no dispara la detección de cambios en absoluto. Simplemente sube desde el componente actual al componente raíz y actualiza su estado de vista a ChecksEnabled . Aquí está el código fuente:

 export function markParentViewsForCheck(view: ViewData) { let currView: ViewData|null = view; while (currView) { if (currView.def.flags & ViewFlags.OnPush) { currView.state |= ViewState.ChecksEnabled; <----------------- } currView = currView.viewContainerParent || currView.parent; } } 

La detección de cambio real para el componente no está progtwigda, pero cuando ocurra en el futuro (ya sea como parte del ciclo de CD actual o siguiente) se verificará la vista de componente principal incluso si se han desconectado los detectores de cambio. Los detectores de cambio se pueden separar utilizando cd.detach() o especificando la estrategia de detección de cambio de OnPush . Todos los controladores de eventos nativos marcan todas las vistas de componentes principales para su verificación.

Este enfoque se usa a menudo en el gancho de ciclo de vida ngDoCheck . Puedes leer más en el Si crees que ngDoCheck significa que tu componente está siendo revisado, lee este artículo .

Consulte también Todo lo que necesita saber sobre la detección de cambios en Angular para obtener más detalles.