Русский
Русский
English
Статистика
Реклама

Обмен данными между компонентами Angular

Проблема

В результате работы с фреймворком 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
К списку статей
Опубликовано: 24.04.2021 16:07:19
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Angular

Typescript

Rxjs

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

Input()

Output()

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru