Angular 2: как взаимодействуют компоненты
Ранее в Angular 2: Как создать компонент контейнера я объяснил, как создавать структурные компоненты, которые отмечают способ визуализации представления, предоставляя директивы сетки с контейнером, заголовком, строками и элементами.
Но прежде чем я перейду к более сложным примерам; Я хочу объяснить, как компоненты взаимодействуют друг с другом в Angular 2.
Типы общения
Если у вас есть некоторый опыт работы с Angular 1, то вы знаете, что есть несколько способов связи между контроллерами с помощью $ rootScope, $ scope, $ emit, $ broadcast или сервисов. Но теперь, используя Angular 2, у нас есть несколько другие способы взаимодействия между компонентами.
Когда я впервые начал работать с Angular 2, я быстро узнал о декораторах @Input и @Output, я также смог выяснить, что службы продолжат выполнять часть работы в этой версии, но я также обнаружил, что есть и другие интересные способы общаться между компонентами, поэтому давайте рассмотрим их.
Услуги
Самый первый метод, о котором я хочу поговорить, - это использование сервисов. Почему? Ну, потому что я знаю, что многим людям это знакомо, и это хороший способ почувствовать себя как дома.
Следующий фрагмент является примером самой простой формы службы в Angular 2:
import {Injectable} from ‘@angular/core’; @Injectable() export class MyService { constructor() { } }
В основном, как вы можете видеть, это обычный класс, который будет экспортирован как модуль.
Единственная сомнительная особенность здесь - это декоратор @Injectable (), который, по сути, является способом сказать angular, что эта служба хороша для использования в Dependency Injection (DI).
Ниже приведен пример вызова этой службы из любого другого компонента.
import {Component, OnInit} from ‘@angular/core’; import {MyService} from ‘./shared/my-service.ts’; // see angular.io/styleguide @Component({ selector: ‘<my-component></my-component>’, templateUrl: ‘app/name.component.html’, providers: [MyService] }) export class MyComponent implements OnInit { constructor(private ms: MyService) {} ngOnInit() { this.ms.doSomething(); } }
Использовать нашу службу в любом компоненте очень просто, мы импортируем модуль из любого каталога, в котором можем его хранить, затем мы говорим Angular 2, что мы хотим использовать MyService в качестве поставщика, таким образом Angular может создать экземпляр для использования и внедрить его. в нашем конструкторе.
Затем мы можем использовать наш экземпляр службы, используя this.ms в любом месте нашего компонента.
Итак, мы знаем, как создать сервис, и что? Что ж, мы можем создать сеттеры и геттеры для поддержки некоторых состояний приложения или даже некоторой информации, например, когда пользователь вошел в систему, вы можете получить зарегистрированного пользователя в любом месте компонентов вашего приложения.
Службы, генерирующие события с использованием RXJS
Это может быть более продвинутый способ использования сервисов, позвольте мне продолжить с зарегистрированного примера.
В вашем приложении у вас есть один из многих компонентов, работающих одновременно, один из этих компонентов отвечает за то, чтобы либо сообщить, что пользователь вошел в систему с другими компонентами или вышел из системы.
Поскольку стандартные средства получения и установки не будут работать, поскольку изменения могут произойти после того, как все компоненты уже загружены, нам нужно установить прослушиватель, который следит за изменениями, но как?
Тема RJXS
import {Injectable} from ‘@angular/core’; import {Subject} from ‘rxjs’; import {Logged} from ‘./definitions/logged.d.ts’; @Injectable() export class LoggedService { private logged: Logged; private subject: Subject<Logged> = new Subject<Logged>(); setLogged(logged: Logged): void { this.logged = logged; this.subject.next(logged); } getLogged(): Subject<Logged> { return this.subject; } }
Этот пример кажется немного более сложным, но это не что иное, как способ генерировать и прослушивать события, поэтому у нас есть не только сеттер и получатель, которые хранят локальную ссылку зарегистрированного, но и отправляют сообщения любому компонент, который регистрируется и отслеживает изменения.
Теперь мы будем прислушиваться к следующим изменениям:
import {Component, OnInit} from ‘@angular/core’; import {LoggedService} from ‘./shared/logged-service.ts’; @Component({ selector: ‘<my-component></my-component>’, templateUrl: ‘app/name.component.html’, providers: [LoggedService] }) export class MyComponent implements OnInit { constructor(private ls: LoggedService) {} ngOnInit() { // Will fire everytime other component use the setter this.ls.setLogged() this.ls.getLogged().subscribe((logged: Logged) => { console.log(‘Welcome %s’, logged.displayname); }); } }
Если мы получим нашу службу Injectable, а затем подпишемся на метод getLogged в любом из наших компонентов, находящихся в нашем приложении, мы сможем узнать, когда пользователь вошел в систему, когда мы могли бы, например, изменить представление NavigationComponent и отобразить пользователя отображаемое имя вместо кнопки входа.
Я знаю, что этот последний пример немного сложнее ... Поэтому, пожалуйста, оставьте комментарий ниже обо всем, что вам нужно прояснить.
Свойства ввода (Родитель - ›Дочерний)
Самый первый классный способ общения между родителями и детьми - использовать @Inputproperties, который по сути является типом общения Родитель - ›Ребенок.
В следующем фрагменте объясняется, как установить свойство ввода в дочернем компоненте, который будет получать сообщения от родительского.
import {Component, Input, OnInit} from ‘@angular/core’; @Component({ selector: ‘child-component’, template: `I’m {{ name }}` }) export class ChildComponent implements OnInit { @Input() name: string; constructor() { } ngOnInit() { } }
Если мы рассмотрим следующий пример, мы увидим, как реализовать ChildComponent и как взаимодействовать с помощью свойства @Input.
import {Component, OnInit} from ‘@angular/core’; import {ChildComponent} from ‘./child-component.ts’; @Component({ selector: ‘parent-component’, template: `<child-component [name]=”childName”></child-component>`, directives: [ChildComponent] }) export class ParentComponent implements OnInit { private childName: string; constructor() { } ngOnInit() { this.childName = ‘Boo’; } }
При реализации ChildComponent в ParentComponent и установке thechildName в качестве имени с помощью имени входного свойства мы сообщаем сверху вниз сообщение о том, что результатом должен быть ChildComponent, говорящий что-то вроде: I’m Boo.
В этом порядке идей мы также можем узнать, когда по какой-то причине ParentComponent изменяет имя ChildComponent, мы можем отслеживать эти изменения, используя hookngOnChanges жизненного цикла компонента.
import {Component, Input, SimpleChange} from ‘@angular/core’; @Component({ selector: ‘child-component’, template: `I’m {{ name }}` }) export class ChildComponent { @Input() name: string; ngOnChanges(changes: SimpleChange) { } }
Свойства вывода (Дочерний - ›Родительский)
Следующий метод - это то, как ChildComponent взаимодействует с ParentComponent, создавая события.
@Component({ selector: ‘child-component’, template: `I’m {{ name }}` }) export class ChildComponent { @Input() name: string; @Output() cry: EventEmitter<boolean> = new EventEmitter<boolean>(); ngOnChanges(changes: SimpleChange) { } isHungry() { this.cry.next(true); } }
Поскольку ChildComponent теперь может генерировать событие cry, когда голоден, теперь asParentComponent нам нужно установить прослушиватель для этого события.
import {Component, OnInit} from ‘@angular/core’; import {ChildComponent} from ‘./child-component.ts’; @Component({ selector: ‘parent-component’, template: `<child-component [name]=”childName” (cry)=”isChildCrying($event)”></child-component>`, directives: [ChildComponent] }) export class ParentComponent implements OnInit { private childName: string; constructor() { } ngOnInit() { this.childName = ‘Boo’; } isChildCrying(isIt: boolean) { console.log(‘The child %s crying’, isIt ? ‘is’ : ‘is not’); } }
Теперь каждый раз, когда ChildComponent испытывает голод и генерирует событие «cry», ParentComponentnow может прослушивать эти события.
Конечно, мы могли бы создать больше методов для кормления ChildComponent и прослушивания, когда он перестанет плакать.
Справочник по шаблону компонентов
Другой способ обмена информацией между ParentComponent и ChildComponent - использование ссылки на компонент и последующий доступ к свойствам или методам этого компонента.
import {Component} from ‘@angular/core’; @Component({ selector: ‘child-component’, template: `I’m {{ name }}` }) export class ChildComponent { public name: string; }
Теперь, если мы реализуем этот ChildComponent в ParentCompnent и получим ссылку, мы сможем получить доступ к свойствам и методам из родительского шаблона.
import {Component, OnInit} from ‘@angular/core’; import {ChildComponent} from ‘./child-component.ts’; @Component({ selector: ‘parent-component’, template: ` <child-component #child></child-component> <button (click)=”child.name = childName”></button> `, directives: [ChildComponent] }) export class ParentComponent implements OnInit { private childName: string; constructor() { } ngOnInit() { this.childName = ‘Boo’; } }
Внедрение ChildView
Я знаю, что это может показаться пугающим, но правда в том, что если вы уже поняли связь с помощью Ссылки на шаблоны компонентов, то вы можете обнаружить, что вам может потребоваться доступ из родительского компонента, а не из родительского шаблона компонента.
import {Component, AfterViewInit, ViewChild} from ‘@angular/core’; import {ChildComponent} from ‘./child-component.ts’; @Component({ selector: ‘parent-component’, template: `<child-component></child-component>`, directives: [ChildComponent] }) export class ParentComponent implements AfterViewInit { @ViewChild(ChildComponent) private cc: ChildComponent; constructor() { } ngAfterViewInit() { this.cc.name = ‘Boo’; } }
На самом деле, я думаю, что это довольно изящный способ общения, потому что он действительно чистый, но я бы использовал этот метод, когда ChildComponent является частью того же модуля, иначе я бы выбрал между @ Input @Outputor Services, потому что вы действительно не хотите для внутренней работы / возни со сторонними библиотеками.
Что следующее?
В своих следующих сообщениях в блоге я расскажу о новых горячих темах, используя только что запущенный Horizon.io, и, конечно же, я расскажу о том, как создать собственное приложение реального времени с использованием Angular 2 и NativeScript 2.
Если вам нравится эта серия и вы хотите быть в курсе следующих релизов и новых пакетов, подпишитесь на меня в Twitter @johncasarrubias и, если вы чувствуете это, оставьте здесь комментарий.
Спасибо за чтение.