Qual é o equivalente Angular a um relógio AngularJS $?

213

No AngularJS, você foi capaz de especificar observadores para observar alterações nas variáveis ​​de escopo usando a $watchfunção de $scope. Qual é o equivalente a observar alterações de variáveis ​​(por exemplo, variáveis ​​de componentes) no Angular?

Erwin
fonte
Use get accessor em texto datilografado.
jmvtrinidad
verificar este artigo que explica a diferença
Max Koretskyi

Respostas:

268

No Angular 2, a detecção de alterações é automática ... $scope.$watch()e o $scope.$digest()RIP

Infelizmente, a seção Detecção de alterações do guia do desenvolvedor ainda não foi gravada (existe um espaço reservado na parte inferior da página Visão geral da arquitetura , na seção "O outro material").

Aqui está meu entendimento de como a detecção de alterações funciona:

  • O Zone.js "corrige o mundo" - intercepta todas as APIs assíncronas no navegador (quando o Angular é executado). É por isso que podemos usar setTimeout()dentro de nossos componentes, em vez de algo como $timeout... porque setTimeout()é o patch do macaco.
  • Angular cria e mantém uma árvore de "detectores de alterações". Existe um desses detectores de alterações (classe) por componente / diretiva. (Você pode obter acesso a esse objeto injetando ChangeDetectorRef.) Esses detectores de alterações são criados quando o Angular cria componentes. Eles controlam o estado de todas as suas ligações, para verificação suja. Eles são, de certa forma, semelhantes ao automático $watches()que o Angular 1 configuraria para {{}}ligações de modelo.
    Diferentemente do Angular 1, o gráfico de detecção de alterações é uma árvore direcionada e não pode ter ciclos (isso torna o Angular 2 muito mais eficiente, como veremos abaixo).
  • Quando um evento é acionado (dentro da zona Angular), o código que escrevemos (o retorno de chamada do manipulador de eventos) é executado. Ele pode atualizar os dados que desejar - o modelo / estado do aplicativo compartilhado e / ou o estado de exibição do componente.
  • Depois disso, devido aos ganchos Zone.js adicionados, ele executa o algoritmo de detecção de alterações do Angular. Por padrão (ou seja, se você não estiver usando a onPushestratégia de detecção de alterações em nenhum de seus componentes), todos os componentes da árvore serão examinados uma vez (TTL = 1) ... a partir do topo, em ordem de profundidade. (Bem, se você estiver no modo dev, a detecção de alterações será executada duas vezes (TTL = 2). Consulte ApplicationRef.tick () para obter mais informações sobre isso.) Realiza verificação suja em todas as suas ligações, usando esses objetos detectores de alterações.
    • Os ganchos do ciclo de vida são chamados como parte da detecção de alterações.
      Se os dados do componente que você deseja assistir forem uma propriedade de entrada primitiva (String, boolean, number), você poderá implementar ngOnChanges()para ser notificado sobre alterações.
      Se a propriedade input for um tipo de referência (objeto, matriz, etc.), mas a referência não foi alterada (por exemplo, você adicionou um item a uma matriz existente), será necessário implementar ngDoCheck()(consulte esta resposta do SO para obter mais informações). nisto).
      Você deve alterar apenas as propriedades do componente e / ou propriedades dos componentes descendentes (devido à implementação de uma caminhada em árvore única - ou seja, fluxo de dados unidirecional). Aqui está um desentupidor que viola isso. Tubos com estado também podem levá-lo até aqui.
  • Para quaisquer alterações de ligação encontradas, os componentes são atualizados e o DOM é atualizado. A detecção de alterações agora está concluída.
  • O navegador percebe que o DOM altera e atualiza a tela.

Outras referências para saber mais:

Mark Rajcok
fonte
window.addEventListener () não aciona a detecção quando variáveis ​​são alteradas ... me deixa louco, não há nada disso em lugar algum.
Albert James Teddy
@AlbertJamesTeddy, consulte a hostdocumentação "Host Listeners" no documento API da DirectivaMetadata . Ele explica como ouvir eventos globais de dentro da zona Angular (para que a detecção de alterações seja acionada conforme desejado). Esta resposta tem um afunilador de trabalho.
Mark Rajcok
esse link seria útil ..
refatorar
@ MarkRajcok, tomei a liberdade de adicionar referências ao meu artigo sobre detecção de alterações. Espero que você não se importe. Explica em grandes detalhes o que acontece sob o capô.
precisa saber é o seguinte
Em relação ao plunkr que viola a regra de fluxo de dados unidirecional, gostaria de acrescentar que, se você executar o plunkr com enableProdMode (), não verá nenhuma atualização na visualização pai, porque o detector de alterações é executado apenas uma vez.
Mister_L 26/10/17
93

Esse comportamento agora faz parte do ciclo de vida do componente.

Um componente pode implementar o método ngOnChanges na interface OnChanges para obter acesso às alterações de entrada.

Exemplo:

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
fonte
77
isso só é verdade para @Input (). se você deseja rastrear alterações nos dados do seu componente, isso não funcionará
LanderV 26/02/16
4
Não consegui alterações de variáveis ​​simples (booleanas, por exemplo). Somente alterações nos objetos são detectadas.
mtoloo
por que você precisa adicionar uma matriz "inputs" no decorador do componente? a detecção de alterações também funcionará sem isso.
Gil Epshtain 31/01/19
68

Se, além da ligação bidirecional automática, você desejar chamar uma função quando um valor for alterado, poderá interromper a sintaxe do atalho de ligação bidirecional para a versão mais detalhada.

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

é uma abreviação de

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

(veja, por exemplo, http://victorsavkin.com/post/119943127151/angular-2-template-syntax )

Você poderia fazer algo assim:

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

supermikko
fonte
No último exemplo, você pretendia remover [] o ngModel?
Eugene Kulabuhov
16

Você pode usar getter functionou get accessoratuar como relógio no angular 2.

Veja a demonstração aqui .

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
fonte
12

Aqui está outra abordagem usando as funções getter e setter para o 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;
  }
}
selecionar
fonte
4
Este assunto é insano. Eu tenho um objeto com muitas propriedades vinculadas a um formulário complexo. Não quero adicionar (change)manipulador (es) em cada um deles; Não quero adicionar get|setss a todas as propriedades do meu modelo; não ajudará a adicionar um get|setpara this.object; ngOnChanges() detecta apenas alterações em @Inputs . Santo maná! O que eles fizeram conosco ??? Devolva-nos algum tipo de observação profunda!
Cody
6

Se você deseja torná-lo vinculativo de duas vias, pode usar [(yourVar)], mas é necessário implementar o yourVarChangeevento e chamá-lo sempre que sua variável for alterada.

Algo assim para acompanhar a mudança do herói

@Output() heroChange = new EventEmitter();

e quando seu herói mudar, ligue this.heroChange.emit(this.hero);

a [(hero)]ligação fará o resto por você

veja o exemplo aqui:

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

Mohy Eldeen
fonte
2

Isso não responde diretamente à pergunta, mas em várias ocasiões cheguei a essa questão do Stack Overflow para resolver algo que eu usaria $ watch nos angularJs. Acabei usando outra abordagem além da descrita nas respostas atuais e quero compartilhá-la caso alguém ache útil.

A técnica que eu uso para obter algo semelhante $watché usar um BehaviorSubject( mais sobre o tópico aqui ) em um serviço Angular e permitir que meus componentes se inscrevam nele para obter (observar) as alterações. Isso é semelhante ao a $watchem angularJs, mas requer um pouco mais de configuração e entendimento.

No meu 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;
    });
  }
}

No meu serviço

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});
  }
}

Aqui está uma demonstração no Stackblitz

John
fonte