Đầu tiên, những gì bạn cần để hiểu mối quan hệ giữa các thành phần. Sau đó, bạn có thể chọn phương pháp giao tiếp phù hợp. Tôi sẽ cố gắng giải thích tất cả các phương pháp mà tôi biết và sử dụng trong thực tế để giao tiếp giữa các thành phần.
Những loại mối quan hệ giữa các thành phần có thể có?
1. Cha mẹ> Con
Chia sẻ dữ liệu qua đầu vào
Đây có lẽ là phương pháp chia sẻ dữ liệu phổ biến nhất. Nó hoạt động bằng cách sử dụng trình @Input()
trang trí để cho phép dữ liệu được truyền qua mẫu.
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() { }
}
Đây là một phương pháp rất đơn giản. Nó rất dễ dàng để sử dụng. Chúng tôi cũng có thể bắt các thay đổi đối với dữ liệu trong thành phần con bằng ngOnChanges .
Nhưng đừng quên rằng nếu chúng ta sử dụng một đối tượng làm dữ liệu và thay đổi các tham số của đối tượng này, thì tham chiếu đến nó sẽ không thay đổi. Do đó, nếu chúng ta muốn nhận một đối tượng đã sửa đổi trong một thành phần con, thì nó phải là bất biến.
2. Con> Cha mẹ
Chia sẻ dữ liệu qua ViewChild
ViewChild cho phép một thành phần được đưa vào một thành phần khác, cho phép cha mẹ truy cập vào các thuộc tính và chức năng của nó. Tuy nhiên, một cảnh báo là child
sẽ không khả dụng cho đến khi chế độ xem được khởi tạo. Điều này có nghĩa là chúng ta cần triển khai móc vòng đời AfterViewInit để nhận dữ liệu từ đứa trẻ.
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() { }
}
Chia sẻ dữ liệu qua đầu ra () và EventEuctor
Một cách khác để chia sẻ dữ liệu là phát ra dữ liệu từ trẻ, có thể được liệt kê bởi cha mẹ. Cách tiếp cận này lý tưởng khi bạn muốn chia sẻ các thay đổi dữ liệu xảy ra trên những thứ như nhấp vào nút, mục nhập biểu mẫu và các sự kiện người dùng khác.
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. Anh chị em
Con> Cha mẹ> Con
Tôi cố gắng giải thích những cách khác để giao tiếp giữa anh chị em dưới đây. Nhưng bạn đã có thể hiểu một trong những cách hiểu về các phương pháp trên.
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
}
}
con-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)
}
}
con-hai.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. Thành phần không liên quan
Tất cả các phương pháp mà tôi đã mô tả dưới đây có thể được sử dụng cho tất cả các tùy chọn ở trên cho mối quan hệ giữa các thành phần. Nhưng mỗi cái đều có ưu điểm và nhược điểm riêng.
Chia sẻ dữ liệu với dịch vụ
Khi truyền dữ liệu giữa các thành phần thiếu kết nối trực tiếp, chẳng hạn như anh chị em, cháu, v.v., bạn nên sử dụng một dịch vụ chia sẻ. Khi bạn có dữ liệu phải luôn đồng bộ, tôi thấy RxJS BehaviorSubject rất hữu ích trong tình huống này.
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")
}
}
Chia sẻ dữ liệu với lộ trình
Đôi khi bạn không chỉ cần chuyển dữ liệu đơn giản giữa các thành phần mà còn lưu một số trạng thái của trang. Ví dụ: chúng tôi muốn lưu một số bộ lọc trong thị trường trực tuyến và sau đó sao chép liên kết này và gửi cho bạn bè. Và chúng tôi hy vọng nó sẽ mở trang ở trạng thái giống như chúng tôi. Cách đầu tiên và có lẽ là nhanh nhất để làm điều này là sử dụng các tham số truy vấn .
Tham số truy vấn tìm kiếm hơn dọc theo dòng của /people?id=
nơi id
có thể bằng bất cứ điều gì và bạn có thể có nhiều thông số như bạn muốn. Các tham số truy vấn sẽ được phân tách bằng ký tự dấu và.
Khi làm việc với các tham số truy vấn, bạn không cần xác định chúng trong tệp tuyến đường của mình và chúng có thể được đặt tên là tham số. Ví dụ: lấy mã sau:
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);
}
}
Trong trang nhận, bạn sẽ nhận được các tham số truy vấn như sau:
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
Cách cuối cùng, phức tạp hơn nhưng mạnh hơn, là sử dụng NgRx . Thư viện này không phải để chia sẻ dữ liệu; nó là một thư viện quản lý nhà nước mạnh mẽ Tôi không thể trong một ví dụ ngắn giải thích cách sử dụng nó, nhưng bạn có thể truy cập trang web chính thức và đọc tài liệu về nó.
Đối với tôi, NgRx Store giải quyết nhiều vấn đề. Ví dụ: khi bạn phải xử lý các vật quan sát và khi trách nhiệm đối với một số dữ liệu quan sát được chia sẻ giữa các thành phần khác nhau, các hành động lưu trữ và bộ giảm tốc đảm bảo rằng việc sửa đổi dữ liệu sẽ luôn được thực hiện "đúng cách".
Nó cũng cung cấp một giải pháp đáng tin cậy cho bộ nhớ đệm yêu cầu HTTP. Bạn sẽ có thể lưu trữ các yêu cầu và phản hồi của họ để bạn có thể xác minh rằng yêu cầu bạn đang thực hiện chưa có phản hồi được lưu trữ.
Bạn có thể đọc về NgRx và hiểu xem bạn có cần nó trong ứng dụng của mình hay không:
Cuối cùng, tôi muốn nói rằng trước khi chọn một số phương pháp chia sẻ dữ liệu, bạn cần hiểu cách sử dụng dữ liệu này trong tương lai. Ý tôi là có lẽ bây giờ bạn chỉ có thể sử dụng một @Input
trang trí để chia sẻ tên người dùng và họ. Sau đó, bạn sẽ thêm một thành phần mới hoặc mô-đun mới (ví dụ: bảng quản trị) cần thêm thông tin về người dùng. Điều này có nghĩa là có thể là một cách tốt hơn để sử dụng dịch vụ cho dữ liệu người dùng hoặc một số cách khác để chia sẻ dữ liệu. Bạn cần suy nghĩ về nó nhiều hơn trước khi bắt đầu thực hiện chia sẻ dữ liệu.