首先,您需要了解组件之间的关系。然后,您可以选择正确的通信方式。我将尝试解释我在实践中用于组件之间通信的所有已知方法。
组件之间可以建立什么样的关系?
1.父母>子女
通过输入共享数据
这可能是共享数据的最常用方法。它通过使用@Input()
装饰器来以允许通过模板传递数据。
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() { }
}
这是一个非常简单的方法。它很容易使用。我们还可以使用ngOnChanges捕获子组件中数据的更改。
但是请不要忘记,如果我们使用一个对象作为数据并更改该对象的参数,则对该对象的引用将不会更改。因此,如果我们想在子组件中接收修改后的对象,则该对象必须是不可变的。
2.孩子>父母
通过ViewChild共享数据
ViewChild允许将一个组件注入另一个组件,从而使父级可以访问其属性和功能。然而,一个警告是child
只有在视图初始化之后才能使用。这意味着我们需要实现AfterViewInit生命周期挂钩,以从子级接收数据。
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() { }
}
通过Output()和EventEmitter共享数据
共享数据的另一种方法是从子级发出数据,该数据可以由父级列出。当您希望共享在单击按钮,表单条目和其他用户事件等事件上发生的数据更改时,此方法非常理想。
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.兄弟姐妹
子>父>子
我尝试在下面解释兄弟姐妹之间进行交流的其他方式。但是您已经可以理解其中一种方法。
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)
}
}
子组件Two.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.不相关的组件
我下面介绍的所有方法都可以用于上述所有组件之间关系的选项。但是每个都有自己的优点和缺点。
与服务共享数据
在缺少直接连接的组件(例如兄弟姐妹,孙子等)之间传递数据时,应使用共享服务。当您拥有应该始终同步的数据时,我发现RxJS BehaviorSubject在这种情况下非常有用。
数据服务
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")
}
}
与路线共享数据
有时,您不仅需要在组件之间传递简单数据,还需要保存页面的某些状态。例如,我们想在在线市场上保存一些过滤器,然后复制此链接并发送给朋友。我们希望它以与我们相同的状态打开页面。执行此操作的第一种方法(可能也是最快的方法)是使用查询参数。
查询参数看起来更像/people?id=
哪里id
可以等于任何东西,您可以根据需要拥有任意数量的参数。查询参数将以&字符分隔。
使用查询参数时,无需在路由文件中定义它们,可以将它们命名为参数。例如,使用以下代码:
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);
}
}
在接收页面中,您将收到以下查询参数:
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
最后一种更复杂但功能更强大的方法是使用NgRx。该库不用于数据共享。它是一个功能强大的状态管理库。在简短的示例中,我无法解释如何使用它,但是您可以转到官方网站并阅读有关它的文档。
对我来说,NgRx Store解决了多个问题。例如,当您必须处理可观察对象并且在不同组件之间共享某些可观察数据的责任时,存储操作和简化程序可确保始终以“正确的方式”执行数据修改。
它还为HTTP请求缓存提供了可靠的解决方案。您将能够存储请求及其响应,以便可以验证所发出的请求尚未存储响应。
您可以阅读有关NgRx的信息,并了解是否需要在应用程序中使用它:
最后,我想说的是,在选择一些共享数据的方法之前,您需要了解将来如何使用这些数据。我的意思是说,也许您现在只能使用@Input
修饰符来共享用户名和姓氏。然后,您将添加一个需要有关用户更多信息的新组件或新模块(例如,管理面板)。这意味着将服务用于用户数据或将数据共享的其他方式可能是更好的方法。在开始实施数据共享之前,您需要更多考虑。