¿Cuál es el equivalente angular de un reloj AngularJS $?

213

En AngularJS pudo especificar observadores para observar cambios en las variables de alcance utilizando la $watchfunción de $scope. ¿Cuál es el equivalente de observar los cambios de las variables (en, por ejemplo, las variables componentes) en Angular?

Erwin
fuente
Utilice get accessor en mecanografiado.
jmvtrinidad
mira

Respuestas:

268

En Angular 2, la detección de cambios es automática ... $scope.$watch()y$scope.$digest() RIP

Desafortunadamente, la sección Detección de cambios de la guía de desarrollo aún no está escrita (hay un marcador de posición cerca de la parte inferior de la Descripción general de la arquitectura página , en la sección "Las otras cosas").

Aquí está mi comprensión de cómo funciona la detección de cambios:

  • Zone.js "monkey parches the world": intercepta todas las API asincrónicas en el navegador (cuando se ejecuta Angular). Es por eso que podemos usar setTimeout()dentro de nuestros componentes en lugar de algo como $timeout... porquesetTimeout() es mono parcheado.
  • Angular construye y mantiene un árbol de "detectores de cambio". Hay uno de estos detectores de cambio (clase) por componente / directiva. (Puede obtener acceso a este objeto inyectando ChangeDetectorRef). Estos detectores de cambio se crean cuando Angular crea componentes. Realizan un seguimiento del estado de todas sus fijaciones, para realizar comprobaciones sucias. Estos son, en cierto sentido, similares a los automáticos $watches()que Angular 1 configuraría para los {{}}enlaces de plantillas.
    A diferencia de Angular 1, el gráfico de detección de cambios es un árbol dirigido y no puede tener ciclos (esto hace que Angular 2 sea mucho más eficiente, como veremos a continuación).
  • Cuando se dispara un evento (dentro de la zona angular), se ejecuta el código que escribimos (la devolución de llamada del controlador de eventos). Puede actualizar los datos que desee: el modelo / estado de la aplicación compartida y / o el estado de vista del componente.
  • Después de eso, debido a los ganchos que Zone.js agregó, luego ejecuta el algoritmo de detección de cambios de Angular. De forma predeterminada (es decir, si no está utilizando la onPushestrategia de detección de cambios en ninguno de sus componentes), cada componente del árbol se examina una vez (TTL = 1) ... desde arriba, en primer orden de profundidad. (Bueno, si está en modo dev, la detección de cambios se ejecuta dos veces (TTL = 2). Consulte ApplicationRef.tick () para obtener más información al respecto). Realiza una comprobación sucia de todos sus enlaces, utilizando esos objetos detectores de cambio.
    • Los ganchos de ciclo de vida se llaman como parte de la detección de cambios.
      Si los datos del componente que desea ver son una propiedad de entrada primitiva (String, boolean, number), puede implementar ngOnChanges()para recibir notificaciones de cambios.
      Si la propiedad de entrada es un tipo de referencia (objeto, matriz, etc.), pero la referencia no cambió (por ejemplo, agregó un elemento a una matriz existente), deberá implementarla ngDoCheck()(consulte esta respuesta SO para obtener más información). en este).
      Solo debe cambiar las propiedades del componente y / o las propiedades de los componentes descendientes (debido a la implementación de un solo recorrido del árbol, es decir, el flujo de datos unidireccional). Aquí hay un saqueador que viola eso. Las tuberías con estado también pueden hacerte tropezar aquí.
  • Para cualquier cambio de enlace que se encuentre, los componentes se actualizan y luego se actualiza el DOM. La detección de cambios ha finalizado.
  • El navegador nota que el DOM cambia y actualiza la pantalla.

Otras referencias para aprender más:

Mark Rajcok
fuente
window.addEventListener () no activa la detección cuando se cambian las variables ... me vuelve loco, no hay nada de eso en ningún lado.
Albert James Teddy
@AlbertJamesTeddy, consulte la documentación de host"Host Listeners" en el documento de la API de GuidelinesMetadata . Explica cómo escuchar eventos globales desde el interior de la zona angular (por lo que la detección de cambios se activará como se desee). Esta respuesta tiene un golpeador que funciona.
Mark Rajcok
este enlace sería útil ..
refactorizar
@ MarkRajcok, me tomé la libertad de agregar referencias a mi artículo sobre detección de cambios. Espero que no te importe. Explica con gran detalle lo que sucede debajo del capó.
Max Koretskyi
Con respecto al plunkr que viola la regla de flujo de datos unidireccional, me gustaría agregar que si ejecuta plunkr con enableProdMode (), no verá ninguna actualización en la vista principal, porque el detector de cambios se ejecuta solo una vez.
Mister_L
93

Este comportamiento ahora es parte del ciclo de vida del componente.

Un componente puede implementar el método ngOnChanges en la interfaz OnChanges para obtener acceso a los cambios de entrada.

Ejemplo:

import {Component, Input, OnChanges} from 'angular2/core';


@Component({
  selector: 'hero-comp',
  templateUrl: 'app/components/hero-comp/hero-comp.html',
  styleUrls: ['app/components/hero-comp/hero-comp.css'],
  providers: [],
  directives: [],

  pipes: [],
  inputs:['hero', 'real']
})
export class HeroComp implements OnChanges{
  @Input() hero:Hero;
  @Input() real:string;
  constructor() {
  }
  ngOnChanges(changes) {
      console.log(changes);
  }
}
toskv
fuente
77
Esto solo es cierto para @Input (). si desea realizar un seguimiento de los cambios de los datos de su componente, esto no funcionará
LanderV
44
No pude obtener cambios de variables simples (booleanas, por ejemplo). Solo se detectan cambios en los objetos.
mtoloo
¿Por qué es necesario agregar una matriz de "entradas" en el decorador del componente? la detección de cambios funcionará sin esto también.
Gil Epshtain
68

Si, además del enlace bidireccional automático, desea llamar a una función cuando cambia un valor, puede romper la sintaxis de acceso directo del enlace bidireccional a la versión más detallada.

<input [(ngModel)]="yourVar"></input>

es una abreviatura de

<input [ngModel]="yourVar" (ngModelChange)="yourVar=$event"></input>

(véase, por ejemplo, http://victorsavkin.com/post/119943127151/angular-2-template-syntax )

Podrías hacer algo como esto:

<input [(ngModel)]="yourVar" (ngModelChange)="changedExtraHandler($event)"></input>

supermikko
fuente
En el último ejemplo, ¿quiso eliminar [] alrededor de ngModel?
Eugene Kulabuhov
16

Puede usar getter functiono get accessorpara actuar como reloj en angular 2.

Ver demo aquí .

import {Component} from 'angular2/core';

@Component({
  // Declare the tag name in index.html to where the component attaches
  selector: 'hello-world',

  // Location of the template for this component
  template: `
  <button (click)="OnPushArray1()">Push 1</button>
  <div>
    I'm array 1 {{ array1 | json }}
  </div>
  <button (click)="OnPushArray2()">Push 2</button>
  <div>
    I'm array 2 {{ array2 | json }}
  </div>
  I'm concatenated {{ concatenatedArray | json }}
  <div>
    I'm length of two arrays {{ arrayLength | json }}
  </div>`
})
export class HelloWorld {
    array1: any[] = [];
    array2: any[] = [];

    get concatenatedArray(): any[] {
      return this.array1.concat(this.array2);
    }

    get arrayLength(): number {
      return this.concatenatedArray.length;
    }

    OnPushArray1() {
        this.array1.push(this.array1.length);
    }

    OnPushArray2() {
        this.array2.push(this.array2.length);
    }
}
jmvtrinidad
fuente
12

Aquí hay otro enfoque que utiliza las funciones getter y setter para el modelo.

@Component({
  selector: 'input-language',
  template: `
  …
  <input 
    type="text" 
    placeholder="Language" 
    [(ngModel)]="query" 
  />
  `,
})
export class InputLanguageComponent {

  set query(value) {
    this._query = value;
    console.log('query set to :', value)
  }

  get query() {
    return this._query;
  }
}
Seleccione
fuente
44
Este tema es una locura. Tengo un objeto con muchas propiedades vinculadas a una forma compleja. No quiero agregar (change)controladores en cada uno de ellos; No quiero agregar get|setss a cada propiedad en mi modelo; no ayudará agregar un get|setpara this.object; ngOnChanges() solo detecta cambios en @Inputs . Santo maná! ¿Qué nos hicieron? ¡Devuélvanos una observación profunda de algún tipo!
Cody
6

Si desea hacer un enlace bidireccional, puede usarlo [(yourVar)], pero debe implementar el yourVarChangeevento y llamarlo cada vez que cambie su variable.

Algo como esto para rastrear el cambio de héroe

@Output() heroChange = new EventEmitter();

y luego cuando cambies tu héroe, llama this.heroChange.emit(this.hero);

la [(hero)]encuadernación hará el resto por ti

ver ejemplo aquí:

http://plnkr.co/edit/efOGIJ0POh1XQeRZctSx?p=preview

Mohy Eldeen
fuente
2

Esto no responde la pregunta directamente, pero en diferentes ocasiones he llegado a esta pregunta de desbordamiento de pila para resolver algo que usaría $ watch en angularJs. Terminé usando un enfoque diferente al descrito en las respuestas actuales, y quiero compartirlo en caso de que alguien lo encuentre útil.

La técnica que uso para lograr algo similar $watches usar un BehaviorSubject( más sobre el tema aquí ) en un servicio angular y dejar que mis componentes se suscriban para obtener (ver) los cambios. Esto es similar a un $watchen angularJs, pero requiere un poco más de configuración y comprensión.

En mi componente:

export class HelloComponent {
  name: string;
  // inject our service, which holds the object we want to watch.
  constructor(private helloService: HelloService){
    // Here I am "watching" for changes by subscribing
    this.helloService.getGreeting().subscribe( greeting => {
      this.name = greeting.value;
    });
  }
}

En mi servicio

export class HelloService {
  private helloSubject = new BehaviorSubject<{value: string}>({value: 'hello'});
  constructor(){}
  // similar to using $watch, in order to get updates of our object 
  getGreeting(): Observable<{value:string}> {
    return this.helloSubject;
  }
  // Each time this method is called, each subscriber will receive the updated greeting.
  setGreeting(greeting: string) {
    this.helloSubject.next({value: greeting});
  }
}

Aquí hay una demostración en Stackblitz

Juan
fuente