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 и, если вы чувствуете это, оставьте здесь комментарий.

Спасибо за чтение.