Angular – komunikacja między komponentami

Ze względu na rozdzielną naturę Angulara, wymiana danych pomiędzy poszczególnymi komponentami nie jest wcale taka oczywista. Może się ona odbywać na kilka sposobów, zależnie od relacji między danymi komponentami. Inaczej będzie to wyglądać dla relacji rodzic -> dziecko, inaczej w drugą stronę, a jeszcze inaczej gdy komponenty są rodzeństwem. W tym artykule postaram się wyjaśnić z punktu widzenia osoby dopiero zaczynającej przygodę z Angularem w jaki sposób można taką wymianę przeprowadzić.

@Input (parent -> child)

Sytuacja, w której użyjemy operatora @Input, ma miejsce gdy zagnieżdżamy jeden element w drugim, np.


<!-- app.component.html -->
<!-- * zagnieżdżamy app-child w app-component, który jest rodzicem -->

<app-child></app-child>

Aby móc zaimportować dane z komponentu nadrzędnego, musimy najpierw zaimportować komponent Input z @angular/core w pliku typescript komponentu podrzędnego. Następnie używamy dekoratora @Input i zapisujemy nazwę zmiennej, która ma przechowywać tę wartość, którą chcemy przesłać z elementu nadrzędnego. Przykładowy plik .ts mógłby wyglądać tak:


/* child.component.ts */
import { Component, OnInit, Input } from '@angular/core'

@Component({
	selector: 'app-child',
	templateUrl: './child.component.html',
	styleUrls: ['./child.component.css']
})

export class ChildComponent implements OnInit {
	@Input() importowanaZmienna;

	ngOnInit() {}
}

Teraz, w pliku app.component.ts tworzymy zmienną, którą chcemy eksportować do elementu podrzędnego, np.


/* app.component.ts */
import { Component, OnInit, Input } from '@angular/core'

@Component({
	selector: 'app-root',
	templateUrl: './app.component.html',
	styleUrls: ['./app.component.css']
})

export class AppComponent {
	eksportowanaZmienna = [ 'wartość 1', 'wartość 2'];
}

Następnie, musimy zmodyfikować plik html elementu podrzędnego, przesyłając w znaczniku wywołującym go zmienną, która ma zostać zaimportowana. Wygląda to tak: [importowanaZmienna]="eksportowanaZmienna". Przykład:


<!-- app.component.html -->

	<app-child [importowanaZmienna]="eksportowanaZmienna"></app-child>

<!-- poprzez importowanaZmienna rozumiemy tę zmienną, -->
<!-- do której będzie przypisana wartość eksportowana -->

Dzięki takiemu podpięciu otrzymujemy możliwość wymiany zmiennych pomiędzy dwoma elementami w relacji parent -> child.

@Output (child -> parent)

Wysyłanie informacji w drugą stronę jest odrobinę bardziej skomplikowane, a to ze względu na konieczność stworzenia eventEmittera. Żeby było przejrzyściej, będziemy wykorzystywać pliki i zmienne, które utworzyliśmy w poprzedniej części. Najpierw musimy zaimportować komponent Output oraz EventEmitter z @angular/core w pliku .ts komponentu podrzędnego. Następnie tworzymy dekorator @Output i zapisujemy w nim deklarację o dowolnej nazwie, do której musi być przypisany specjalny obiekt klasy EventEmitter. Taka deklaracja wygląda następująco: zmiennaEskportujaca = new EventEmitter();. Opcjonalnie, pomiedzy EventEmitter a () możemy dodać w nawiasach klamrowych typ zmiennej, który będzie przesyłany, np. <string>. Przykład:


/* child.component.ts */
import { Component, OnInit, Input, Output } from '@angular/core'

@Component({
	selector: 'app-child',
	templateUrl: './child.component.html',
	styleUrls: ['./child.component.css']
})

export class ChildComponent implements OnInit {
	@Input() importowanaZmienna;

	@Output() zmiennaEksportujaca = new EventEmitter();

	ngOnInit() {}
}

Aby wyeksportować daną wartość, musimy użyć metody klasy eventEmitter: emit(). Jako argument metody emit() wrzucamy zmienną, którą chcemy wysłać, np. this.zmiennaEksportujaca.emit(this.istniejacaZmienna). Wyglądałoby to tak:


/* child.component.ts */
import { Component, OnInit, Input, Output } from '@angular/core'

@Component({
	selector: 'app-child',
	templateUrl: './child.component.html',
	styleUrls: ['./child.component.css']
})

export class ChildComponent implements OnInit {
	@Input() importowanaZmienna;

	@Output() zmiennaEksportujaca = new EventEmitter();

	ngOnInit() {
		this.istniejacaZmienna = 'asdf';
		
		this.eksport();
	}

	eksport() {
		this.zmiennaEskportujaca.emit(this.istniejacaZmienna);
	}
}

Aby wyeksportować tę zmienną do nadrzędnego komponentu, musimy dodać wiązanie danych do znacznika komponentu podrzędnego (oczywiście przekazanie zmiennej nastąpi przy wywołaniu metody eksport()). W nawiasach okrągłych wrzucamy nazwę naszego emitera, a po znaku równości w cudzysłowie podajemy np. nazwę metody komponentu odbierającego (nadrzędnego), a w nawiasie koniecznie zawieramy $event. Przykład wywołania w znaczniku elementu podrzędnego: (zmiennaEksportujaca)="import($event)". Przykład pliku .ts komponentu nadrzędnego, który zawiera metodę odbierającą wartość przekazaną przez element podrzędny:


/* app.component.ts */
import { Component, OnInit, Input } from '@angular/core'

@Component({
	selector: 'app-root',
	templateUrl: './app.component.html',
	styleUrls: ['./app.component.css']
})

export class AppComponent {
	eksportowanaZmienna = [ 'wartość 1', 'wartość 2'];

	import(odebranaZmienna) {
		console.log(odebranaZmienna);
	}
}

Oraz przykład wiązania zawartego w znaczniku elementu podrzędnego:


<!-- app.component.html -->

<app-child 
	[importowanaZmienna]="eksportowanaZmienna" 
	(zmiennaEksportujaca)="import($event)">
</app-child>

<!-- poprzez importowanaZmienna rozumiemy tę zmienną, -->
<!-- do której będzie przypisana wartość eksportowana -->

Komentarze

Dodaj komentarz