Primeiro, o que você precisa para entender os relacionamentos entre os componentes. Então você pode escolher o método certo de comunicação. Vou tentar explicar todos os métodos que eu conheço e uso na minha prática para a comunicação entre componentes.
Que tipos de relacionamentos entre componentes podem existir?
1. Pai> Filho
Compartilhando dados via entrada
Este é provavelmente o método mais comum de compartilhamento de dados. Ele funciona usando o @Input()
decorador para permitir que os dados sejam transmitidos pelo modelo.
parent.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'parent-component',
template: `
<child-component [childProperty]="parentProperty"></child-component>
`,
styleUrls: ['./parent.component.css']
})
export class ParentComponent{
parentProperty = "I come from parent"
constructor() { }
}
child.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'child-component',
template: `
Hi {{ childProperty }}
`,
styleUrls: ['./child.component.css']
})
export class ChildComponent {
@Input() childProperty: string;
constructor() { }
}
Este é um método muito simples. É fácil de usar. Também podemos capturar alterações nos dados no componente filho usando ngOnChanges .
Mas não esqueça que se usarmos um objeto como dados e alterarmos os parâmetros desse objeto, a referência a ele não será alterada. Portanto, se queremos receber um objeto modificado em um componente filho, ele deve ser imutável.
2. Criança> Pai
Compartilhando dados via ViewChild
O ViewChild permite que um componente seja injetado em outro, dando ao pai acesso a seus atributos e funções. Uma ressalva, no entanto, é que child
não estará disponível até que a visualização tenha sido inicializada. Isso significa que precisamos implementar o gancho do ciclo de vida AfterViewInit para receber os dados do filho.
parent.component.ts
import { Component, ViewChild, AfterViewInit } from '@angular/core';
import { ChildComponent } from "../child/child.component";
@Component({
selector: 'parent-component',
template: `
Message: {{ message }}
<child-compnent></child-compnent>
`,
styleUrls: ['./parent.component.css']
})
export class ParentComponent implements AfterViewInit {
@ViewChild(ChildComponent) child;
constructor() { }
message:string;
ngAfterViewInit() {
this.message = this.child.message
}
}
child.component.ts
import { Component} from '@angular/core';
@Component({
selector: 'child-component',
template: `
`,
styleUrls: ['./child.component.css']
})
export class ChildComponent {
message = 'Hello!';
constructor() { }
}
Compartilhando dados via Output () e EventEmitter
Outra maneira de compartilhar dados é emitir dados do filho, que podem ser listados pelos pais. Essa abordagem é ideal quando você deseja compartilhar alterações de dados que ocorram em itens como cliques em botões, entradas de formulário e outros eventos do usuário.
parent.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'parent-component',
template: `
Message: {{message}}
<child-component (messageEvent)="receiveMessage($event)"></child-component>
`,
styleUrls: ['./parent.component.css']
})
export class ParentComponent {
constructor() { }
message:string;
receiveMessage($event) {
this.message = $event
}
}
child.component.ts
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'child-component',
template: `
<button (click)="sendMessage()">Send Message</button>
`,
styleUrls: ['./child.component.css']
})
export class ChildComponent {
message: string = "Hello!"
@Output() messageEvent = new EventEmitter<string>();
constructor() { }
sendMessage() {
this.messageEvent.emit(this.message)
}
}
3. irmãos
Criança> Pai> Criança
Eu tento explicar outras maneiras de se comunicar entre irmãos abaixo. Mas você já pode entender uma das maneiras de entender os métodos acima.
parent.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'parent-component',
template: `
Message: {{message}}
<child-one-component (messageEvent)="receiveMessage($event)"></child1-component>
<child-two-component [childMessage]="message"></child2-component>
`,
styleUrls: ['./parent.component.css']
})
export class ParentComponent {
constructor() { }
message: string;
receiveMessage($event) {
this.message = $event
}
}
child-one.component.ts
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'child-one-component',
template: `
<button (click)="sendMessage()">Send Message</button>
`,
styleUrls: ['./child-one.component.css']
})
export class ChildOneComponent {
message: string = "Hello!"
@Output() messageEvent = new EventEmitter<string>();
constructor() { }
sendMessage() {
this.messageEvent.emit(this.message)
}
}
child-two.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'child-two-component',
template: `
{{ message }}
`,
styleUrls: ['./child-two.component.css']
})
export class ChildTwoComponent {
@Input() childMessage: string;
constructor() { }
}
4. Componentes não relacionados
Todos os métodos que descrevi abaixo podem ser usados para todas as opções acima para o relacionamento entre os componentes. Mas cada um tem suas próprias vantagens e desvantagens.
Compartilhando dados com um serviço
Ao transmitir dados entre componentes que não possuem conexão direta, como irmãos, netos, etc., você deve usar um serviço compartilhado. Quando você tem dados que sempre devem estar sincronizados, acho o RxJS BehaviorSubject muito útil nessa situação.
data.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable()
export class DataService {
private messageSource = new BehaviorSubject('default message');
currentMessage = this.messageSource.asObservable();
constructor() { }
changeMessage(message: string) {
this.messageSource.next(message)
}
}
first.component.ts
import { Component, OnInit } from '@angular/core';
import { DataService } from "../data.service";
@Component({
selector: 'first-componennt',
template: `
{{message}}
`,
styleUrls: ['./first.component.css']
})
export class FirstComponent implements OnInit {
message:string;
constructor(private data: DataService) {
// The approach in Angular 6 is to declare in constructor
this.data.currentMessage.subscribe(message => this.message = message);
}
ngOnInit() {
this.data.currentMessage.subscribe(message => this.message = message)
}
}
second.component.ts
import { Component, OnInit } from '@angular/core';
import { DataService } from "../data.service";
@Component({
selector: 'second-component',
template: `
{{message}}
<button (click)="newMessage()">New Message</button>
`,
styleUrls: ['./second.component.css']
})
export class SecondComponent implements OnInit {
message:string;
constructor(private data: DataService) { }
ngOnInit() {
this.data.currentMessage.subscribe(message => this.message = message)
}
newMessage() {
this.data.changeMessage("Hello from Second Component")
}
}
Compartilhando dados com uma rota
Às vezes, você precisa não apenas passar dados simples entre os componentes, mas salvar algum estado da página. Por exemplo, queremos salvar algum filtro no mercado on-line, copiar esse link e enviar para um amigo. E esperamos que ela abra a página no mesmo estado que nós. A primeira, e provavelmente a mais rápida, maneira de fazer isso seria usar parâmetros de consulta .
Os parâmetros de consulta são mais parecidos com /people?id=
onde id
pode ser qualquer coisa e você pode ter quantos parâmetros desejar. Os parâmetros da consulta seriam separados pelo caractere e comercial.
Ao trabalhar com parâmetros de consulta, você não precisa defini-los no arquivo de rotas e eles podem ser nomeados parâmetros. Por exemplo, pegue o seguinte código:
page1.component.ts
import {Component} from "@angular/core";
import {Router, NavigationExtras} from "@angular/router";
@Component({
selector: "page1",
template: `
<button (click)="onTap()">Navigate to page2</button>
`,
})
export class Page1Component {
public constructor(private router: Router) { }
public onTap() {
let navigationExtras: NavigationExtras = {
queryParams: {
"firstname": "Nic",
"lastname": "Raboy"
}
};
this.router.navigate(["page2"], navigationExtras);
}
}
Na página de recebimento, você receberá esses parâmetros de consulta como os seguintes:
page2.component.ts
import {Component} from "@angular/core";
import {ActivatedRoute} from "@angular/router";
@Component({
selector: "page2",
template: `
<span>{{firstname}}</span>
<span>{{lastname}}</span>
`,
})
export class Page2Component {
firstname: string;
lastname: string;
public constructor(private route: ActivatedRoute) {
this.route.queryParams.subscribe(params => {
this.firstname = params["firstname"];
this.lastname = params["lastname"];
});
}
}
NgRx
A última maneira, que é mais complicada, mas mais poderosa, é usar o NgRx . Esta biblioteca não é para compartilhamento de dados; é uma poderosa biblioteca de gerenciamento de estado. Em um breve exemplo, não posso explicar como usá-lo, mas você pode ir ao site oficial e ler a documentação sobre ele.
Para mim, a NgRx Store resolve vários problemas. Por exemplo, quando você precisa lidar com observáveis e quando a responsabilidade por alguns dados observáveis é compartilhada entre diferentes componentes, as ações da loja e o redutor garantem que as modificações dos dados sempre sejam executadas "da maneira certa".
Ele também fornece uma solução confiável para o cache de solicitações HTTP. Você poderá armazenar os pedidos e suas respostas, para poder verificar se o pedido que você está fazendo ainda não tem uma resposta armazenada.
Você pode ler sobre o NgRx e entender se precisa ou não no seu aplicativo:
Finalmente, quero dizer que, antes de escolher alguns dos métodos para compartilhar dados, você precisa entender como esses dados serão usados no futuro. Quero dizer, talvez agora você possa usar apenas um @Input
decorador para compartilhar um nome de usuário e sobrenome. Em seguida, você adicionará um novo componente ou novo módulo (por exemplo, um painel de administração) que precisa de mais informações sobre o usuário. Isso significa que pode ser uma maneira melhor de usar um serviço para dados do usuário ou alguma outra maneira de compartilhar dados. Você precisa pensar mais sobre isso antes de começar a implementar o compartilhamento de dados.