[Angular, TypeScript] Обмен данными между компонентами Angular
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
ПроблемаВ результате работы с фреймворком Angular, мы декомпозируем наше web-приложение. И по этому у нас возникает ситуация, когда нам нужно передавать данные между компонентами.@Input()Что бы передать данные в дочерний компонент, мы можем использовать декоратор @Input(). Он позволит нам передать данные из родительского компонента в дочерний. Рассмотрим простой пример:
import { Input, Component} from '@angular/core';
@Component({
selector: 'app-child',
template: `<h1>Title: {{ title }}</h1>`
})
export class ChildComponent {
@Input() title: string;
}
В дочернем компоненте мы мы "задекорировали" нужное нам свойство title. Не забываем импортировать декоратор:
import { Input} from '@angular/core';
Осталось только передать параметр title в дочерний компонент из родительского:
import { Component } from '@angular/core';
@Component({
selector: 'app-component',
template: `<app-child [title]="title" [userAge]="age"></app-child>`
})
export class AppComponent {
public title = 'Hello world!';
}
Параметры из класса мы передаем с помощью квадратных скобок [title]="title", простую строку мы можем передать и без использования квадратных скобок title="Hello world". Мы научились передавать параметры из родительского в дочерний, но что если нам надо сделать все наоборот?@Output()Благодаря директиве @Output() мы можем привязаться к событиям дочернего компонента. На первый взгляд не очень понятно, так что давайте рассмотрим пример:
import { Component } from '@angular/core';
@Component({
selector: 'app-counter',
template: `<h1>Count: {{ count }}</h1>
<app-add (buttonClick)="onAdd()"></app-add>`
})
export class AppCounter {
public count = 0;
public onAdd(): void {
this.count++;
}
}
import { Component, EventEmitter, Output } from '@angular/core';
@Component({
selector: 'app-add',
template: `<button (click)="add()"></button>`;
})
export class AppAdd {
@Output() buttonClick = new EventEmitter();
public add(): void {
this.buttonClick.emit();
}
}
Думаю данный код требует некоторых объяснений. При клике на кнопку в компоненте AppAdd срабатывает событие click, которое вызывает функцию add(). Код this.buttonClick.emit() вызовет событие buttonClick в компоненте AppCounter. Очень важно правильно импортировать EventEmitter:
import { EventEmitter } from '@angular/core';
Но есть одно "но", мы не передали никакую информацию в родительский компонент. Рассмотрим уже другой вариант в котором мы будем передавать информацию в родительский компонент:
import { Component } from '@angular/core';
@Component({
selector: 'app-better-counter',
template: `<h1>Count: {{ count }}</h1>
<app-buttons (buttonClick)="onChange($event)"></app-buttons>`
})
export class BetterCounterComponent {
public count = 0;
public onChange(isAdd: boolean): void {
if (isAdd) {
this.count++;
} else {
this.count--;
}
}
}
import { Component, EventEmitter, Output } from '@angular/core';
@Component({
selector: 'app-buttons',
template: `<button (click)="change(true)"></button>
<button (click)="change(false)"></button>`
})
export class ButtonsComponent {
@Output() buttonClick = new EventEmitter<boolean>();
public change(change: boolean): void {
this.buttonClick.emit(change);
}
}
Давайте рассмотрим список внесенных изменений:
- Добавили тип передаваемых данных new EventEmitter<boolean>()
- В метод emit передали нужную информацию this.buttonClick.emit(change)
- Принимаем данные как $event в родительском компоненте (buttonClick)="onChange($event)"
@Input() и @Output() достаточно удобно, но не в ситуации, когда на надо передать данные в дочерний компонент, дочернего компонента и т.д., или же компоненты находятся в разных частях приложения.Сервисы и RxJsОдними из лучших вариантов обмена данных остаются сервисы. Создадим простой сервис который бы мог оповещать компоненты про изменение данных, а так же передавать значения:
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class SimpleService {
public count$ = new Subject<number>();
public changeCount(count: number) {
this.count$.next(count);
}
}
Наш сервис готов. В нём мы создадим переменную count$. Знак доллара - это договорённость между программистами в обозначениях потоков. Теперь простыми словами про Subject. Subject - это труба, по которой мы можем передавать данные. Данные получают компоненты, которые оформили подписку на Subject. Давайте посмотрим, как изменять count из компонента:
import { SimpleService } from './services/simple.service.ts';
@Component({
selector: 'app-any',
template: ``
})
export class AnyComponentComponent {
constructor(
private readonly simpleService: SimpleService
) {}
public setAnyCount(): void {
this.simpleService.changeCount(Math.random());
}
}
Мы передали результат Math.random() и пустили его по всем подписчикам. Теперь посмотрим как следить за этими изменениями:
import { Component, OnInit } from '@angular/core';
import { SimpleService } from './services/simple.service.ts';
@Component({
selector: 'app-other',
template: ``
})
export class OtherComponentComponent implements OnInit {
constructor(
private readonly simpleService: SimpleService
) {}
ngOnInit(): void {
this.simpleService.count$.subscribe((count) => this.log(count));
}
private log(data: number): void {
console.log(data);
}
}
На инициализации мы подписываемся на изменения count, и при каждом вызове count$.next(...) где-либо сработает функция которую мы передали в subscribe. Единственная проблема которая осталась в коде - утечка памяти. При переходе между страницами нашего приложения, компонент будет дестроится, а когда он нам снова понадобится произойдёт повторная инициализация. Старая подписка не пропала, а новые с каждым разом будут только добавляться. Функция log() будет запускаться столько раз, сколько у нас есть подписок. Если бы мы имели там какой-нибудь сложный функционал, то пользовать приложения заметил бы снижение производительности. Этого можно избежать, отписавшись от count$ на OnDestroy. Для этого вынесем подписку в переменную и вызовем у неё метод unsubscribe():
import { Component, OnInit, OnDestroy } from '@angular/core';
import { SimpleService } from './services/simple.service.ts';
import { Subsription } from 'rxjs';
@Component({
selector: 'app-other',
template: ``
})
export class OtherComponentComponent implements OnInit, OnDestroy {
private subs: Subsription;
constructor(
private readonly simpleService: SimpleService
) {}
ngOnInit(): void {
this.subs = this.simpleService.count$.subscribe((count) => this.log(count));
}
ngOnDestroy(): void {
this.subs.unsubscribe();
}
private log(data: number): void {
console.log(data);
}
}
Мы можем подписаться на множество Subject из компонента, подписаться на один и тот же Subject из разных компонентов.ИтогМы можем обмениваться данными между компонентов с помощью @Input(), @Output(), а также RxJs. В данной статье я опустил store, так как статья рассчитана на новичков. Советую попрактиковаться в данной теме, что бы улучшить свои навыки.
===========
Источник:
habr.com
===========
Похожие новости:
- [Управление разработкой, Управление проектами] Как работать в команде, которая пишет на 5 языках
- [JavaScript, Проектирование и рефакторинг, ReactJS] Как мы сетапили монорепозиторий с SSR и SPA для Otus.ru
- [JavaScript, API, TypeScript] TypedAPI: клиент-сервер для TypeScript
- [Angular] Отслеживание состояния компонентов в Angular c помощью ng-set-state (перевод)
- [JavaScript, TypeScript] Пишем юнит тесты на TypeScript'е (на примере котиков)
- [Программирование, ReactJS, Управление разработкой, TypeScript] Wrike переходит с Dart на новый стек. Какой?
- [JavaScript, Node.JS, TypeScript] FSTB – работа с файлами в Node.js без боли
- [JavaScript, ООП] Типобезопасность в JavaScript: Flow и TypeScript
- [Программирование, ReactJS, TypeScript] Чего мне не хватало в функциональных компонентах React.js
- [JavaScript, Разработка под iOS, Разработка мобильных приложений, Разработка под Android, ERP-системы] Cordova. Опыт Enterprise-проекта
Теги для поиска: #_angular, #_typescript, #_angular, #_typescript, #_rxjs, #_vzaimodejstvie_mezhdu_komponentami (взаимодействие между компонентами), #_@input(), #_@output(), #_angular, #_typescript
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 15:03
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
ПроблемаВ результате работы с фреймворком Angular, мы декомпозируем наше web-приложение. И по этому у нас возникает ситуация, когда нам нужно передавать данные между компонентами.@Input()Что бы передать данные в дочерний компонент, мы можем использовать декоратор @Input(). Он позволит нам передать данные из родительского компонента в дочерний. Рассмотрим простой пример: import { Input, Component} from '@angular/core';
@Component({ selector: 'app-child', template: `<h1>Title: {{ title }}</h1>` }) export class ChildComponent { @Input() title: string; } import { Input} from '@angular/core';
import { Component } from '@angular/core';
@Component({ selector: 'app-component', template: `<app-child [title]="title" [userAge]="age"></app-child>` }) export class AppComponent { public title = 'Hello world!'; } import { Component } from '@angular/core';
@Component({ selector: 'app-counter', template: `<h1>Count: {{ count }}</h1> <app-add (buttonClick)="onAdd()"></app-add>` }) export class AppCounter { public count = 0; public onAdd(): void { this.count++; } } import { Component, EventEmitter, Output } from '@angular/core';
@Component({ selector: 'app-add', template: `<button (click)="add()"></button>`; }) export class AppAdd { @Output() buttonClick = new EventEmitter(); public add(): void { this.buttonClick.emit(); } } import { EventEmitter } from '@angular/core';
import { Component } from '@angular/core';
@Component({ selector: 'app-better-counter', template: `<h1>Count: {{ count }}</h1> <app-buttons (buttonClick)="onChange($event)"></app-buttons>` }) export class BetterCounterComponent { public count = 0; public onChange(isAdd: boolean): void { if (isAdd) { this.count++; } else { this.count--; } } } import { Component, EventEmitter, Output } from '@angular/core';
@Component({ selector: 'app-buttons', template: `<button (click)="change(true)"></button> <button (click)="change(false)"></button>` }) export class ButtonsComponent { @Output() buttonClick = new EventEmitter<boolean>(); public change(change: boolean): void { this.buttonClick.emit(change); } }
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs'; @Injectable({ providedIn: 'root', }) export class SimpleService { public count$ = new Subject<number>(); public changeCount(count: number) { this.count$.next(count); } } import { SimpleService } from './services/simple.service.ts';
@Component({ selector: 'app-any', template: `` }) export class AnyComponentComponent { constructor( private readonly simpleService: SimpleService ) {} public setAnyCount(): void { this.simpleService.changeCount(Math.random()); } } import { Component, OnInit } from '@angular/core';
import { SimpleService } from './services/simple.service.ts'; @Component({ selector: 'app-other', template: `` }) export class OtherComponentComponent implements OnInit { constructor( private readonly simpleService: SimpleService ) {} ngOnInit(): void { this.simpleService.count$.subscribe((count) => this.log(count)); } private log(data: number): void { console.log(data); } } import { Component, OnInit, OnDestroy } from '@angular/core';
import { SimpleService } from './services/simple.service.ts'; import { Subsription } from 'rxjs'; @Component({ selector: 'app-other', template: `` }) export class OtherComponentComponent implements OnInit, OnDestroy { private subs: Subsription; constructor( private readonly simpleService: SimpleService ) {} ngOnInit(): void { this.subs = this.simpleService.count$.subscribe((count) => this.log(count)); } ngOnDestroy(): void { this.subs.unsubscribe(); } private log(data: number): void { console.log(data); } } =========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 15:03
Часовой пояс: UTC + 5