Что такое угловой эквивалент часов AngularJS $?

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

Ответ 1

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

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

Здесь мое понимание того, как работает обнаружение изменений:

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

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

Ответ 2

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

Компонент может реализовать метод ngOnChanges в интерфейсе OnChanges, чтобы получить доступ к изменениям ввода.

Пример:

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

Ответ 3

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

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

является сокращением для

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

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

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

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

Ответ 4

Вы можете использовать getter function get accessor или get accessor чтобы действовать как часы на угловой 2.

Смотрите демо здесь.

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

Ответ 5

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

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

Ответ 6

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

Что-то вроде этого, чтобы отслеживать изменение героя

@Output() heroChange = new EventEmitter();

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

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

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

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

Ответ 8

Это не дает прямого ответа на вопрос, но я несколько раз сталкивался с вопросом о переполнении стека, чтобы решить что-то, для чего я бы использовал $ 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