В AngularJS вы могли указать наблюдателей для наблюдения за изменениями в переменных области видимости, используя функцию $ watch из $ scope. Что эквивалентно наблюдению за изменениями переменных (например, в переменных компонента) в Angular?

Ответы (8)

В Angular 2 обнаружение изменений происходит автоматически ... $ scope. $ Watch () и $ scope. $ Digest () R.I.P.

К сожалению, раздел «Обнаружение изменений» в руководстве разработчика еще не написан (в нижней части страницы Обзор архитектуры, в разделе «Прочие материалы» есть заполнитель).

Вот мое понимание того, как работает обнаружение изменений:

  • Zone.js «обезьяна патчит мир» - он перехватывает все асинхронные API в браузере (при запуске Angular). Вот почему мы можем использовать setTimeout () внутри наших компонентов, а не что-то вроде $ timeout... потому что setTimeout () исправлен как обезьяна.
  • Angular строит и поддерживает дерево «детекторов изменений». Для каждого компонента / директивы существует один такой детектор (класс) изменений. (Вы можете получить доступ к этому объекту, введя ChangeDetectorRef.) Эти детекторы изменений создаются, когда Angular создает компоненты. Они отслеживают состояние всех ваших привязок для грязной проверки. В некотором смысле они похожи на автоматические $ watch (), которые Angular 1 настраивает для {{}} привязок шаблонов.
    В отличие от Angular 1, изменение граф обнаружения является ориентированным деревом и не может иметь циклов (это делает Angular 2 намного более производительным, как мы увидим ниже).
  • Когда возникает событие (внутри зоны Angular), запускается написанный нами код (обратный вызов обработчика событий). Он может обновлять любые данные, которые он хочет - общую модель / состояние приложения и / или состояние представления компонента.
  • После этого, из-за добавленных хуков Zone.js, он запускает алгоритм обнаружения изменений Angular. По умолчанию (т. Е. Если вы не используете стратегию обнаружения изменений onPush для любого из ваших компонентов), каждый компонент в дереве проверяется один раз (TTL = 1) ... сверху, подробно -Первый заказ. (Ну, если вы находитесь в режиме разработки, обнаружение изменений запускается дважды (TTL = 2). См. ApplicationRef.tick ()Подробнее об этом.) Он выполняет грязную проверку всех ваших привязок, используя эти объекты детектора изменений.
    • Перехватчики жизненного цикла вызываются как часть обнаружения изменений.
      Если данные компонента, которые вы хотите отслеживать, являются примитивным входным свойством (String, boolean, number), вы можете реализовать ngOnChanges (), чтобы получать уведомления об изменениях.
      Если свойство input является ссылочным типом (объект, массив и т. Д.), Но ссылка не изменилась (например, вы добавили элемент в существующий массив), вам необходимо реализовать ngDoCheck () (подробнее см. этот SO-ответ).
      Вам следует изменять только свойства компонента и / или свойства дочерних компонентов (из-за реализации единого обхода дерева, т. Е. Однонаправленного потока данных). Вот плункер, который это нарушает. Каналы с отслеживанием состояния также могут сбить вас с пути.
  • Для любых обнаруженных изменений привязки обновляются компоненты, а затем обновляется DOM. Обнаружение изменений завершено.
  • Браузер замечает изменения DOM и обновляет экран.

Другие ссылки, чтобы узнать больше:

Попробуйте это, когда ваше приложение все еще требует $ parse, $ eval, $ watch как поведение в Angular

https://github.com/vinayk406/angular-expression-parser

Это не отвечает на вопрос напрямую, но я в разных случаях сталкивался с этим вопросом о переполнении стека, чтобы решить то, для чего я бы использовал $ watch для angularJs. В итоге я использовал другой подход, чем описано в текущих ответах, и хочу поделиться им, если кто-то сочтет это полезным.

Техника, которую я использую для достижения чего-то похожего $ watch, заключается в использовании BehaviorSubject (подробнее по теме здесь) в сервисе Angular, и пусть мои компоненты подписываются на него, чтобы получать (наблюдать) изменения. Это похоже на $ watch в angularJs, но требует дополнительной настройки и понимания.

В моем компоненте:

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

В моем сервисе

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

Вот демонстрация Stackblitz

Если, помимо автоматической двусторонней привязки, вы хотите вызвать функцию при изменении значения, вы можете разбить синтаксис ярлыка двусторонней привязки на более подробную версию.

- это сокращение от

(см., Например, http://victorsavkin.com/post/119943127151/angular-2-template-syntax)

Вы можете сделать что-то вроде этого:

Вот еще один подход с использованием функций получения и установки для модели.

@Component({
  selector: 'input-language',
  template: `
  …
  
  `,
})
export class InputLanguageComponent {

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

  get query() {
    return this._query;
  }
}

Вы можете использовать функцию получения или средство доступа, чтобы действовать как часы на angular 2.

См. Демонстрацию здесь.

импорт {Component} из 'angular2 / core';

@Компонент({
  // Объявляем имя тега в index.html, куда прикрепляется компонент
  селектор: 'привет-мир',

  // Расположение шаблона для этого компонента
  шаблон: `
  
  
Я массив 1 {{array1 | json}}
Я массив 2 {{array2 | json}}
Я конкатенирован {{concatenatedArray | json}}
Я длина двух массивов {{arrayLength | json}}
` }) экспортный класс HelloWorld { массив1: любой [] = []; массив2: любой [] = []; get concatenatedArray (): any [] { вернуть this.array1.concat (this.array2); } получить arrayLength (): number { вернуть this.concatenatedArray.length; } OnPushArray1 () { this.array1.push (this.array1.length); } OnPushArray2 () { this.array2.push (this.array2.length); } }

Это поведение теперь является частью жизненного цикла компонента.

A component can implement the ngOnChanges method in the OnChanges interface to get access to input changes.

Пример:

импорт {Component, Input, OnChanges} из 'angular2 / core';


@Компонент({
  селектор: 'hero-comp',
  templateUrl: 'app / components / hero-comp / hero-comp.html',
  styleUrls: ['app / components / hero-comp / hero-comp.css'],
  провайдеры: [],
  директивы: [],

  трубы: [],
  входы: ['герой', 'настоящий']
})
класс экспорта HeroComp реализует OnChanges {
  @Input () герой: Герой;
  @Input () реальный: строка;
  constructor () {
  }
  ngOnChanges (changes) {
      console.log (изменения);
  }
}

Если вы хотите сделать двустороннюю привязку, вы можете использовать [(yourVar)], но вам нужно реализовать событие yourVarChange и вызывать его каждый раз при изменении вашей переменной.

Примерно так для отслеживания смены героя

@Output() heroChange = new EventEmitter();

, а затем, когда ваш герой изменится, вызовите this.heroChange.emit (this.hero);

привязка [(hero)] сделает все остальное за вас

см. Пример здесь:

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

2022 WebDevInsider