Що таке кутовий еквівалент годиннику AngularJS $?


213

У AngularJS ви змогли вказати спостерігачам спостерігати за змінами змінних областей за допомогою $watchфункції $scope. Який еквівалент перегляду змін змінних (наприклад, змінних компонентів) у Angular?


Використовуйте get accessor в typecript.
jmvtrinidad

Відповіді:


268

У куті 2 виявлення змін є автоматичним ... $scope.$watch()та $scope.$digest()RIP

На жаль, розділ Виявлення змін у посібнику розробників ще не написаний (у нижній частині сторінки " Огляд архітектури " в розділі "Інші речі" є заповнювач .

Ось моє розуміння того, як працює виявлення змін:

  • Zone.js "мавпа виправляє світ" - він перехоплює всі асинхронні API в браузері (коли запущено Angular). Ось чому ми можемо використовувати setTimeout()всередині наших компонентів, а не щось на зразок $timeout... тому що setTimeout()це мавпа.
  • Кутовий будує і підтримує дерево "детекторів змін". Існує один такий детектор змін (клас) на компонент / директиву. (Ви можете отримати доступ до цього об'єкта шляхом ін'єкції ChangeDetectorRef.) Ці детектори змін створюються, коли Angular створює компоненти. Вони відслідковують стан усіх ваших прив’язок, для брудної перевірки. Вони в певному сенсі схожі на автомати, $watches()які Angular 1 встановив би для {{}}прив'язки шаблонів.
    На відміну від Angular 1, графік виявлення змін є направленим деревом і не може мати циклів (це робить Angular 2 набагато ефективнішим, як ми побачимо нижче).
  • Коли подія запускається (всередині кутової зони), код, який ми написали (зворотний виклик обробника події), запускається. Він може оновлювати будь-які дані, які хоче - модель спільної програми / стан та / або стан перегляду компонента.
  • Після цього через додані гачки Zone.js він запускає алгоритм виявлення змін Angular. За замовчуванням (тобто, якщо ви не використовуєте onPushстратегію виявлення змін на жодному зі своїх компонентів), кожен компонент у дереві перевіряється один раз (TTL = 1) ... зверху, в глибині першого порядку. (Добре, якщо ви перебуваєте в режимі розробки, виявлення змін запускається двічі (TTL = 2). Докладніше про це див. У ApplicationRef.tick () .) Він здійснює брудну перевірку всіх ваших прив'язок, використовуючи ці об'єкти детектора змін.
    • Гачки життєвого циклу називаються частиною виявлення змін.
      Якщо дані компонента, які ви хочете переглянути, є примітивними властивостями введення (String, boolean, number), ви можете реалізувати, ngOnChanges()щоб отримувати повідомлення про зміни.
      Якщо властивість введення є типом посилання (об'єкт, масив тощо), але посилання не змінилося (наприклад, ви додали елемент до існуючого масиву), вам потрібно буде реалізувати ngDoCheck()(див. Цю відповідь ТА для отримання додаткової інформації на цьому).
      Вам слід змінити лише властивості компонента та / або властивості компонентів-нащадків (через реалізацію єдиного дерева - тобто однонаправленого потоку даних). Ось викрадач, який порушує це. Видатні труби також можуть вас перевезти сюди.
  • Для будь-яких змін, які знайдуться, Компоненти оновлюються, а потім DOM оновлюється. Виявлення змін завершено.
  • Браузер помічає зміни DOM і оновлює екран.

Інші посилання, щоб дізнатися більше:


window.addEventListener () не запускає виявлення при зміні змінної ... це зводить мене з розуму, ніде нічого в цьому немає.
Альберт Джеймс Тедді

@AlbertJamesTeddy, див. hostДокументацію "Слухачі хостів" в API DocumMetadata API, док . Він пояснює, як слухати глобальні події зсередини кутової зони (тому виявлення змін буде запускатися за бажанням). У цій відповіді працює планкер.
Марк Райкок

це посилання буде корисним ..
refactor

@MarkRajcok, я сміливо додав посилання на свою статтю про виявлення змін. Сподіваюся, ви не заперечуєте. Він докладно пояснює, що відбувається під капотом.
Макс Корецький

Що стосується plunkr, який порушує правило однонаправленого потоку даних, я хочу додати, що якщо ви запустите plunkr з EnaProdMode (), ви не побачите жодного оновлення в батьківському поданні, оскільки детектор змін працює лише один раз.
Mister_L

93

Така поведінка зараз є частиною життєвого циклу компонентів.

Компонент може реалізувати метод 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);
  }
}

77
це справедливо лише для @Input (). якщо ви хочете відслідковувати зміни власних даних свого компонента, це не вийде
LanderV

4
Я не міг отримати зміни простих змінних (наприклад, булева). Виявляються лише зміни об’єктів.
mtoloo

чому потрібно додати масив "inputs" в декоратор компонента? виявлення змін буде працювати і без цього.
Гіл Епштейн

68

Якщо, крім автоматичного двостороннього прив'язки, ви хочете викликати функцію, коли значення змінюється, ви можете розбити синтаксис ярлика двостороннього зв’язування на більш багатослівну версію.

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


В останньому прикладі ви мали намір видалити [] навколо ngModel?
Євген Кулабухов

16

Ви можете використовувати getter functionабо 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);
    }
}

12

Ось ще один підхід із використанням функцій 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;
  }
}

4
Ця тема божевільна. У мене об’єкт з багатьма властивостями прив’язаний до складної форми. Я не хочу додавати (change)обробники на кожну з них; Я не хочу додавати get|setss до кожної властивості моєї моделі; це не допоможе додати get|setдля this.object; ngOnChanges() виявляє зміни лише до @Inputs . Свята манна! Що вони з нами зробили ??? Поверніть нам якийсь глибокий годинник!
Коді

6

Якщо ви хочете зробити це двостороннім обов'язковим, ви можете використовувати [(yourVar)], але ви повинні реалізувати yourVarChangeподію та викликати її щоразу, коли зміна вашої змінної.

Щось подібне для відстеження змін героя

@Output() heroChange = new EventEmitter();

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

[(hero)]зв'язуванні інше зробить за вас

див. приклад тут:

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



2

Це не відповідає на питання безпосередньо, але я неодноразово опинявся на цьому запитанні переповнення стека, щоб вирішити щось, для чого я би використовував $ watch у angularJs. Я в кінцевому підсумку використовував інший підхід, ніж описано в поточних відповідях, і хочу поділитися ним у випадку, якщо хтось вважає це корисним.

Метод, який я використовую для досягнення чогось подібного, $watchполягає у використанні BehaviorSubject( докладніше про тему тут ) у службі Angular, і дозволити моїм компонентам підписатися на нього, щоб отримати (переглянути) зміни. Це схоже на $watchкутовий J, але вимагає додаткової настройки та розуміння.

У моєму компоненті:

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.