Zunächst, was Sie benötigen, um die Beziehungen zwischen Komponenten zu verstehen. Dann können Sie die richtige Kommunikationsmethode wählen. Ich werde versuchen, alle Methoden zu erklären, die ich kenne und in meiner Praxis für die Kommunikation zwischen Komponenten verwende.
Welche Arten von Beziehungen zwischen Komponenten kann es geben?
1. Eltern> Kind
Daten über Eingabe teilen
Dies ist wahrscheinlich die häufigste Methode zum Teilen von Daten. Mithilfe des @Input()
Dekorators können Daten über die Vorlage übertragen werden.
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() { }
}
Dies ist eine sehr einfache Methode. Es ist einfach zu bedienen. Mit ngOnChanges können wir auch Änderungen an den Daten in der untergeordneten Komponente abfangen .
Vergessen Sie jedoch nicht, dass sich der Verweis darauf nicht ändert, wenn wir ein Objekt als Daten verwenden und die Parameter dieses Objekts ändern. Wenn wir also ein geändertes Objekt in einer untergeordneten Komponente empfangen möchten, muss es unveränderlich sein.
2. Kind> Eltern
Daten über ViewChild teilen
Mit ViewChild kann eine Komponente in eine andere eingefügt werden, sodass der übergeordnete Benutzer Zugriff auf seine Attribute und Funktionen erhält . Eine Einschränkung ist jedoch, dass child
diese erst verfügbar ist, nachdem die Ansicht initialisiert wurde. Dies bedeutet, dass wir den AfterViewInit-Lebenszyklus-Hook implementieren müssen, um die Daten vom untergeordneten Element zu empfangen.
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() { }
}
Daten über Output () und EventEmitter austauschen
Eine andere Möglichkeit, Daten gemeinsam zu nutzen, besteht darin, Daten vom untergeordneten Element auszugeben, die vom übergeordneten Element aufgelistet werden können. Dieser Ansatz ist ideal, wenn Sie Datenänderungen freigeben möchten, die beispielsweise bei Schaltflächenklicks, Formulareinträgen und anderen Benutzerereignissen auftreten.
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. Geschwister
Kind> Eltern> Kind
Ich versuche unten andere Möglichkeiten der Kommunikation zwischen Geschwistern zu erklären. Sie können jedoch bereits eine der Möglichkeiten zum Verständnis der oben genannten Methoden verstehen.
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. Nicht verwandte Komponenten
Alle Methoden, die ich unten beschrieben habe, können für alle oben genannten Optionen für die Beziehung zwischen den Komponenten verwendet werden. Aber jeder hat seine eigenen Vor- und Nachteile.
Daten mit einem Dienst teilen
Wenn Sie Daten zwischen Komponenten übertragen, für die keine direkte Verbindung besteht, z. B. Geschwister, Enkelkinder usw., sollten Sie einen gemeinsam genutzten Dienst verwenden. Wenn Sie Daten haben, die immer synchron sein sollten, finde ich das RxJS BehaviorSubject in dieser Situation sehr nützlich.
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")
}
}
Daten mit einer Route teilen
Manchmal müssen Sie nicht nur einfache Daten zwischen Komponenten übertragen, sondern auch einen bestimmten Status der Seite speichern. Zum Beispiel möchten wir einige Filter im Online-Markt speichern und dann diesen Link kopieren und an einen Freund senden. Und wir erwarten, dass die Seite im selben Zustand wie wir geöffnet wird. Der erste und wahrscheinlich schnellste Weg, dies zu tun, wäre die Verwendung von Abfrageparametern .
Abfrageparameter sehen eher so aus, /people?id=
als id
ob alles gleich wäre, und Sie können so viele Parameter haben, wie Sie möchten. Die Abfrageparameter würden durch das kaufmännische Und-Zeichen getrennt.
Wenn Sie mit Abfrageparametern arbeiten, müssen Sie diese nicht in Ihrer Routendatei definieren, und sie können als Parameter bezeichnet werden. Nehmen Sie zum Beispiel den folgenden Code:
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);
}
}
Auf der Empfangsseite würden Sie diese Abfrageparameter wie folgt erhalten:
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
Der letzte Weg, der komplizierter, aber leistungsfähiger ist, ist die Verwendung von NgRx . Diese Bibliothek ist nicht für die gemeinsame Nutzung von Daten vorgesehen. Es ist eine leistungsstarke State-Management-Bibliothek. Ich kann nicht in einem kurzen Beispiel erklären, wie man es benutzt, aber Sie können auf die offizielle Seite gehen und die Dokumentation darüber lesen.
Für mich löst NgRx Store mehrere Probleme. Wenn Sie sich beispielsweise mit Observablen befassen müssen und die Verantwortung für einige beobachtbare Daten auf verschiedene Komponenten aufgeteilt ist, stellen die Speicheraktionen und der Reduzierer sicher, dass Datenänderungen immer "auf die richtige Weise" durchgeführt werden.
Es bietet auch eine zuverlässige Lösung für das Zwischenspeichern von HTTP-Anforderungen. Sie können die Anforderungen und ihre Antworten speichern, um zu überprüfen, ob für die von Ihnen gestellte Anforderung noch keine Antwort gespeichert ist.
Sie können über NgRx lesen und verstehen, ob Sie es in Ihrer App benötigen oder nicht:
Abschließend möchte ich sagen, dass Sie vor der Auswahl einiger Methoden zum Teilen von Daten verstehen müssen, wie diese Daten in Zukunft verwendet werden. Ich meine, vielleicht können Sie gerade jetzt nur einen @Input
Dekorateur verwenden, um einen Benutzernamen und einen Nachnamen zu teilen. Anschließend fügen Sie eine neue Komponente oder ein neues Modul hinzu (z. B. ein Admin-Bereich), das weitere Informationen zum Benutzer benötigt. Dies bedeutet, dass dies möglicherweise eine bessere Möglichkeit ist, einen Dienst für Benutzerdaten zu verwenden oder Daten auf andere Weise gemeinsam zu nutzen. Sie müssen mehr darüber nachdenken, bevor Sie mit der Implementierung der Datenfreigabe beginnen.