2016-07-25 3 views
155

У меня есть родительский компонент (КатегорияКомпонент), дочерний компонент (videoListComponent) и ApiService.Как определить, когда значение @Input() изменяется в Угловом?

У меня есть большая часть этой работы, то есть каждый компонент может получить доступ к json api и получить соответствующие данные через наблюдаемые.

В настоящее время компонент видео-списка просто получает все видео, я хотел бы отфильтровать это только на видео в определенной категории, я достиг этого, передав categoryId ребенку через @Input().

CategoryComponent.html

<video-list *ngIf="category" [categoryId]="category.id"></video-list> 

Это работает и при изменении категории родительского CategoryComponent то значение CategoryId пропускается через через @Input() но я тогда нужно определить это в VideoListComponent и повторный запрос массив видео через APIService (с новой категорией).

В AngularJS я бы сделал $watch по переменной. Каков наилучший способ справиться с этим?

+0

https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#parent-to-child-on-changes –

+0

изменения входных данных: - http: //www.angulartutorial. net/2017/12/watch-input-changes-angular-4.html – Prashobh

ответ

182

На самом деле, есть два способа обнаружения и капризничать, когда входные изменения в компоненте ребенка в angular2 +:

  1. Вы можете использовать ngOnChanges() метода жизненного цикла как упоминалось в более старых ответах:

    @Input() categoryId: string; 
    
    ngOnChanges(changes: SimpleChanges) { 
    
        this.doSomething(changes.categoryId.currentValue); 
        // You can also use categoryId.previousValue and 
        // categoryId.firstChange for comparing old and new values 
    
    } 
    

    Документация Ссылки: ngOnChanges,SimpleChanges,SimpleChange
    Демо Пример: Посмотрите на this plunker

  2. Альтернативно, вы можете также использовать ввода свойства сеттер следующим образом:

    private _categoryId: string; 
    
    @Input() set categoryId(value: string) { 
    
        this._categoryId = value; 
        this.doSomething(this._categoryId); 
    
    } 
    
    get categoryId(): string { 
    
        return this._categoryId; 
    
    } 
    

    Документация Ссылка: Посмотрите here.

    Демо-пример: Посмотрите на this plunker.

Какой подход следует использовать?

Если ваш компонент имеет несколько входов, то, если вы используете ngOnChanges(), вы получите все изменения для всех вводов одновременно в ngOnChanges(). Используя этот подход, вы также можете сравнить текущие и предыдущие значения ввода, который изменился, и принять соответствующие меры.

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

EDIT 2017-07-25: Угловая ОБНАРУЖЕНИЕ ИЗМЕНЕНИЯ МОЖЕТ ЕЩЕ НЕ пожары НЕКОТОРЫХ ОБСТОЯТЕЛЬСТВ

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

  1. Если вы используете вложенный объект или массив (вместо примитивного типа данных JS), чтобы передать данные от родителей к детям, обнаружению изменений (с использованием либо сеттеров или ngchanges) могут не сработать, а также упоминаются в ответ пользователя: muetzerich. Для решений смотрите here.

  2. Если вы мутируете данные вне углового контекста (то есть внешне), то угловые не будут знать об изменениях. Возможно, вам придется использовать ChangeDetectorRef или NgZone в своем компоненте для того, чтобы сделать угловые сведения о внешних изменениях и тем самым инициировать обнаружение изменений. См. this.

+2

Очень хорошая точка относительно использования сеттеров с входами, я обновлю это, чтобы принять принятый ответ, поскольку он более всеобъемлющий. Благодарю. –

+0

Нормально ли, что сеттер работает только один раз? – trichetriche

+0

@trichetriche, setter (set method) будет вызван, когда родитель изменяет ввод. То же самое верно и для ngOnChanges(). –

83

Используйте жизненный цикл ngOnChanges() в вашем компоненте.

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

Вот Docs.

+3

Это работает, когда переменная установлена ​​на новое значение, например 'MyComponent.myArray = []', но если изменяется входное значение, например. 'MyComponent.myArray.push (newValue)', 'ngOnChanges()' не запускается. Любые идеи о том, как зафиксировать это изменение? –

+3

'ngOnChanges()' не вызывается, когда вложенный объект изменился. Возможно, вы найдете решение [здесь] (https://stackoverflow.com/questions/34796901/angular2-change-detection-ngonchanges-not-firing-for-nested-object) – muetzerich

19

Я получал ошибки в консоли, а также компилятор и IDE при использовании типа SimpleChanges в сигнатуре функции. Чтобы предотвратить ошибки, используйте вместо этого ключевое слово any.

ngOnChanges(changes: any) { 
    console.log(changes.myInput.currentValue); 
} 

EDIT:

Как Джон указал ниже, вы можете использовать SimpleChanges подпись при использовании брекет обозначения, а не точечную нотацию.

ngOnChanges(changes: SimpleChanges) { 
    console.log(changes['myInput'].currentValue); 
} 
+0

Вы импортировали интерфейс SimpleChanges в наверху вашего файла? –

+0

@Jon - Да. Проблема заключалась не в самой сигнатуре, а в доступе к параметрам, которые назначены во время выполнения объекту SimpleChanges. Например, в моем компоненте я привязал свой пользовательский класс к входу (т. Е. ''). Это свойство доступно из класса SimpleChanges, вызывая 'changes.user.currentValue'. Уведомление 'user' связано во время выполнения, а не в части объекта SimpleChanges – Darcy

+0

Я вижу, что вы имеете в виду хорошую точку. –

1

Я просто хочу добавить, что есть еще один Lifecycle крючок называется DoCheck, что полезно, если значение @Input не является примитивным значением.

У меня есть массив как Input так это не сгореть OnChanges события, когда изменяется содержание (потому что проверка, что Угловой делают «простая» и не глубоко, так что массив еще массив, даже если содержание на массив изменился).

Затем я реализую некоторый пользовательский проверочный код, чтобы решить, хочу ли я обновить свое представление с помощью измененного массива.

0

Вы также можете иметь наблюдаемое, которое вызывает изменения в родительском компоненте (CategoryComponent) и выполняет то, что вы хотите сделать в подписке в дочернем компоненте. (VideoListComponent)

service.ts 
public categoryChange$ : ReplaySubject<any> = new ReplaySubject(1); 

----------------- 
CategoryComponent.ts 
public onCategoryChange(): void { 
    service.categoryChange$.next(); 
} 

----------------- 
videoListComponent.ts 
public ngOnInit(): void { 
    service.categoryChange$.subscribe(() => { 
    // do your logic 
}); 
} 
3

Самый безопасный выбор должен пойти с общим service instead параметра @Input. Кроме того, параметр @Input не обнаруживает изменения в сложном вложенном типе объекта.

Простой пример службы выглядит следующим образом:

Service.ts

import { Injectable } from '@angular/core'; 
import { Subject } from 'rxjs/Subject'; 

@Injectable() 
export class SyncService { 

    private thread_id = new Subject<number>(); 
    thread_id$ = this.thread_id.asObservable(); 

    set_thread_id(thread_id: number) { 
     this.thread_id.next(thread_id); 
    } 

} 

Component.ts

export class ConsumerComponent implements OnInit { 

    constructor(
    public sync: SyncService 
) { 
    this.sync.thread_id$.subscribe(thread_id => { 
      **Process Value Updates Here** 
     } 
    } 

    selectChat(thread_id: number) { <--- How to update values 
    this.sync.set_thread_id(thread_id); 
    } 
} 

Вы можете использовать аналогичную реализацию в других компонентах, и все ваши компоненты будут совместно использовать одни и те же общие значения.

+1

Thankyou Sanket. Это очень хороший момент, чтобы сделать и, при необходимости, лучший способ сделать это. Я оставлю принятый ответ, поскольку он отвечает на мои конкретные вопросы относительно обнаружения изменений @Input. –

+0

Параметр @Input не обнаруживает изменения _inside_ сложного типа вложенных объектов, но если сам объект изменяется, он загорается, верно? например У меня есть входной параметр «parent», поэтому, если я изменяю родительский объект в целом, будет обнаружено изменение обнаружения, но если я изменю parent.name, это не будет? – yogibimbi

Смежные вопросы