Взаимодействие компонентов


Для передачи данных из одного Angular компонента в другой существует несколько способов:

@Input() свойства;
@Output() свойства;
@ViewChild() свойства;
Сервис.
Первые три случая были рассмотрены ранее в предыдущих главах. Тем не менее в этой главе приведены примеры всех способов взаимодействия.

Пример Angular компонента с @Input(), @Output() и @ViewChild() приведен ниже.

parent-example.component.ts


@Component({
selector: 'parent-example',
template: `
<child-example
[title]="'Title'"
(dataChanged)="dataChangeHandler($event)"
></child-example>
`,
})
export class ParentExampleComponent
implements AfterViewInit {
@ViewChild(ChildExampleComponent)
viewChild: ChildExampleComponent

constructor() {}

ngAfterViewInit() {
console.log(this.viewChild)
}

dataChangeHandler(data) {
console.log(data)
}
}
child-example.component.ts


@Component({
selector: 'child-example',
template: `
<div>
<h1 [textContent]="title"></h1>
<a
href="#"
(click)="sendMessage()"
[textContent]="label"
></a>
</div>
`,
})
export class ChildExampleComponent {
@Input() title: string
@Output() dataChanged: EventEmitter<
any
> = new EventEmitter<any>()

label: string = 'Send message'

constructor() {}

sendMessage(data) {
this.dataChanged.emit({
msg: 'Message from ChildExample',
})
}
}
Декоратор @Input() позволяет передавать значения дочерним компонентам, но только на один уровень иерархии.

С помощью @Output() имитируют возникновение события и передают данные родительскому компоненту.

Использование @ViewChild() в родительском Angular component позволяет получить все свойства указанного дочернего компонента.

Полное описание указанных декораторов находится в главах "Компоненты", "Обработка событий" и "Жизненный цикл компонента" соответственно.

Еще один способ передавать данные между компонентами - использование сервисов. Сервис — одна из сущностей Angular, которая реализует паттерн проектирования Singleton и служит хранилищем данных. Другое частое его использование — хранение методов, осуществляющих HTTP запросы к серверу.

Обычно сервис определяется на уровне всего приложения в целом. Поэтому все Angular компоненты обращаются к одному и тому же его экземпляру. Более подробно здесь.

Перед использованием сервисы необходимо импортировать в корневой модуль.


import {SomeDataService} from './services/some.service';

// ...
providers: [
SomeDataService,
// ...
],
// ...
Пример Angular компонента с сервисом.

some-data.service.ts


@Injectable({ providedIn: 'root' })
export class SomeDataService {
data: number = 1
}
first.component.ts


@Component({
selector: 'the-first',
template: ` <p>First</p> `,
})
export class FirstComponent {
constructor(private someSrv: SomeDataService) {
console.log(someSrv.data)
someSrv.data = 3
}
}
second.component.ts


@Component({
selector: 'the-second',
template: ` <p>Second</p> `,
})
export class SecondComponent {
constructor(private someSrv: SomeDataService) {
console.log(someSrv.data)
}
}
В сервисе SomeDataService определено свойство data со значением 1. В FirstComponent в консоль выводится значение data и затем ему же присваивается новое значение — 3.

Далее уже в конструкторе SecondComponent выводится свойство data сервиса SomeDataService. Как можно убедиться, в консоль будет выведено 3. Все потому что оба Angular компонента обращаются к одному и тому же экземпляру класса сервиса.