Solución:
Primero, lo que necesita para comprender las relaciones entre los componentes. Entonces puede elegir el método de comunicación correcto. Intentaré explicar todos los métodos que conozco y utilizo en mi práctica para la comunicación entre componentes.
¿Qué tipo de relaciones entre componentes puede haber?
1. Padre> Hijo
Compartir datos a través de la entrada
Este es probablemente el método más común para compartir datos. Funciona usando el @Input()
decorador para permitir que los datos se pasen a través de la plantilla.
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 es un método muy simple. Es fácil de usar. También podemos detectar cambios en los datos del componente secundario usando ngOnChanges.
Pero no olvide que si usamos un objeto como datos y cambiamos los parámetros de este objeto, la referencia al mismo no cambiará. Por tanto, si queremos recibir un objeto modificado en un componente hijo, debe ser inmutable.
2. Hijo> Padre
Compartir datos a través de ViewChild
ViewChild permite inyectar un componente en otro, dando al padre acceso a sus atributos y funciones. Sin embargo, una advertencia es que child
no estará disponible hasta que se haya inicializado la vista. Esto significa que debemos implementar el enlace del ciclo de vida AfterViewInit para recibir los datos del niño.
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() { }
}
Compartir datos a través de Output () y EventEmitter
Otra forma de compartir datos es emitir datos del niño, que el padre puede enumerar. Este enfoque es ideal cuando desea compartir cambios de datos que ocurren en cosas como clics en botones, entradas de formularios y otros eventos de usuario.
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. Hermanos
Hijo> Padre> Hijo
Intento explicar otras formas de comunicarse entre hermanos a continuación. Pero ya podría comprender una de las formas de comprender los métodos anteriores.
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 no relacionados
Todos los métodos que he descrito a continuación se pueden utilizar para todas las opciones anteriores para la relación entre los componentes. Pero cada uno tiene sus propias ventajas y desventajas.
Compartir datos con un servicio
Al pasar datos entre componentes que carecen de una conexión directa, como hermanos, nietos, etc., debe utilizar un servicio compartido. Cuando tiene datos que siempre deben estar sincronizados, encuentro que RxJS BehaviorSubject es muy útil en esta situación.
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")
}
}
Compartir datos con una ruta
A veces, no solo necesita pasar datos simples entre componentes, sino también guardar algún estado de la página. Por ejemplo, queremos guardar algún filtro en el mercado online y luego copiar este enlace y enviarlo a un amigo. Y esperamos que abra la página en el mismo estado que nosotros. La primera forma, y probablemente la más rápida, de hacer esto sería utilizar parámetros de consulta.
Los parámetros de consulta se ven más en la línea de /people?id=
dónde id
puede igualar cualquier cosa y puede tener tantos parámetros como desee. Los parámetros de consulta estarían separados por el carácter comercial.
Cuando trabaje con parámetros de consulta, no es necesario que los defina en su archivo de rutas y se pueden nombrar parámetros. Por ejemplo, tome el siguiente 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);
}
}
En la página de recepción, recibiría estos parámetros de consulta como los siguientes:
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
La última forma, que es más complicada pero más poderosa, es usar NgRx. Esta biblioteca no es para compartir datos; es una poderosa biblioteca de gestión estatal. No puedo en un breve ejemplo explicar cómo usarlo, pero puedes ir al sitio oficial y leer la documentación al respecto.
Para mí, NgRx Store resuelve varios problemas. Por ejemplo, cuando tiene que lidiar con observables y cuando la responsabilidad de algunos datos observables se comparte entre diferentes componentes, las acciones de la tienda y el reductor aseguran que las modificaciones de datos siempre se realizarán “de la manera correcta”.
También proporciona una solución confiable para el almacenamiento en caché de solicitudes HTTP. Podrá almacenar las solicitudes y sus respuestas para que pueda verificar que la solicitud que está realizando aún no tiene una respuesta almacenada.
Puede leer sobre NgRx y comprender si lo necesita en su aplicación o no:
- Capas de servicios angulares: Redux, RxJs y Ngrx Store: ¿cuándo usar una tienda y por qué?
- Tienda Ngrx: una guía de arquitectura
Finalmente, quiero decir que antes de elegir algunos de los métodos para compartir datos, debe comprender cómo se utilizarán estos datos en el futuro. Quiero decir, tal vez ahora puedas usar solo un @Input
decorador por compartir un nombre de usuario y apellido. Luego, agregará un nuevo componente o un nuevo módulo (por ejemplo, un panel de administración) que necesita más información sobre el usuario. Esto significa que puede ser una mejor forma de utilizar un servicio para los datos del usuario o de alguna otra forma de compartir datos. Debe pensarlo más antes de comenzar a implementar el intercambio de datos.
Si com1 y com2 son hermanos, puede usar
@component({
selector:'com1',
})
export class com1{
function1(){...}
}
com2 emite un evento usando un EventEmitter
@component({
selector:'com2',
template: `<button (click)="function2()">click</button>`
)
export class com2{
@Output() myEvent = new EventEmitter();
function2(){...
this.myEvent.emit(null)
}
}
Aquí, el componente principal agrega un enlace de eventos para escuchar myEvent
eventos y luego llamadas com1.function1()
cuando ocurre tal evento.
#com1
es una variable de plantilla que permite hacer referencia a este elemento desde otra parte de la plantilla. Usamos esto para hacer function1()
el controlador de eventos para myEvent
de com2
:
@component({
selector:'parent',
template: `<com1 #com1></com1><com2 (myEvent)="com1.function1()"></com2>`
)
export class com2{
}
Para conocer otras opciones para comunicarse entre componentes, consulte también interacción de componentes
Puede acceder al método del componente uno desde el componente dos.
componenteOne
ngOnInit() {}
public testCall(){
alert("I am here..");
}
componenteDos
import { oneComponent } from '../one.component';
@Component({
providers:[oneComponent ],
selector: 'app-two',
templateUrl: ...
}
constructor(private comp: oneComponent ) { }
public callMe(): void {
this.comp.testCall();
}
componentTwo archivo html
<button (click)="callMe()">click</button>