官網(wǎng):https://angular.cn/guide/component-interaction
1、父組件 向 子組件 傳遞數(shù)據(jù):Input
父組件中改變某變量X(任何數(shù)據(jù)類型), 子組件用 @Input 接收該變量
父組件:
<!-- parentComponent -->
<app-child [name]="'childName'"></app-child>
子組件:
@Input() public name:string = '';
如果傳入子組件的數(shù)據(jù),僅僅在子組件中用來顯示,是不需考慮輸入變化的(雙向綁定就幫我們自動實現(xiàn)了),而往往還要在input變化時做一些邏輯處理,此時就需要對輸入屬性的變化進行監(jiān)聽,通常有兩種方式:
方法1: 通過 setter 截聽輸入屬性值的變化
針對一個輸入屬性,設(shè)置setter,以攔截父組件中值的變化,并采取行動。
更多知識:https://my.oschina.net/sunlightday/blog/3118148
子組件:
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-name-child',
template: '<h3>"{{name}}"</h3>'
})
export class NameChildComponent {
@Input()
get name(): string { return this._name; }
set name(name: string) {
// 當(dāng)父組件輸入的name變化的時候,set方法自動截聽,然后在這里做相應(yīng)的處理
// 入?yún)ame就是最新的值,而當(dāng)前舊值就是本地私有變量_name this._name = (name && name.trim()) || '<no name set>';
}
private _name = '';
}</pre>
父組件:
import { Component } from '@angular/core';
@Component({
selector: 'app-name-parent',
template: `
<h2>Master controls {{names.length}} names</h2>
<app-name-child *ngFor="let name of names" [name]="name"></app-name-child>
`
})
export class NameParentComponent {
// Displays 'Dr IQ', '<no name set>', 'Bombasto'
names = ['Dr IQ', ' ', ' Bombasto '];
}
方法2: 通過ngOnChanges()來截聽輸入屬性值的變化
使用
OnChanges
生命周期鉤子接口的ngOnChanges()
方法來監(jiān)測所有輸入屬性值的變化并做出回應(yīng)。當(dāng)需要監(jiān)視多個、交互式輸入屬性的時候,本方法比用屬性的 setter 更合適。
生命周期鉤子:https://angular.cn/guide/lifecycle-hooks
**setter vs ngOnChanges
如果要對某個輸入變量進行變化監(jiān)聽,setter好用
如果多個輸入變量都需要進行變化監(jiān)聽,并且在監(jiān)聽后的邏輯處理涉及到多個輸入屬性,ngOnChanges好用 -- 可以在這個方法中統(tǒng)一處理
2、子組件 向 父組件 傳遞數(shù)據(jù): Output EventEmitter
首先子組件暴露一個 EventEmitter 屬性,當(dāng)子組件中改變某變量X(任何數(shù)據(jù)類型),或者想要傳遞消息,子組件就利用EventEmitter屬性的emit方法,將事件發(fā)射出去, 父組綁定該事件,并定義相應(yīng)的方法做出響應(yīng)
子組件:
Output() childEmit: EventEmitter<T> = new EventEmitter<T>();
// 對某變量data一頓操作后,發(fā)射出去
this.childEmit.emit(data);</pre>
父組件:
在子組件引用上面綁定:(eventEmitter)="模板表達式",就像響應(yīng)(click)事件一樣。
<app-child (childEmit)="getData($event)"></app-child></pre>
3、父組件 訪問 子組件: 本地變量
在父組件模板里,新建一個本地變量來代表子組件(其實就是子組件的引用),然后利用這個變量來讀取子組件的屬性和調(diào)用子組件的方法, 比較方便實用
父組件:
<app-base-grid #grid></app-base-grid>
<span>{{ grid.name }}</span> // 直接獲取子組件變量
<button (click)="grid.func()">直接調(diào)用子組件方法</button></pre>
這個本地變量方法簡單便利,但是它也有局限性:父組件-子組件的連接必須全部在父組件的模板中進行。父組件本身的代碼對子組件沒有訪問權(quán)。即,僅限于在html代碼中操作
4、父組件 訪問 子組件: ViewChild 方法
如果父組件的類 需要讀取子組件的屬性值或調(diào)用子組件的方法,就不能使用本地變量方法。當(dāng)父組件類 需要這種訪問時,可以把子組件作為 ViewChild,注入到父組件里面。
父組件:
<app-base-grid #grid></app-base-grid>
<span>{{ grid.name }}</span> // 直接獲取子組件變量
<button (click)="grid.func()">直接調(diào)用子組件方法</button></pre>
@ViewChild('grid') grid: BaseGridComponent; // 表格
// 我還可以干些別的
this.grid 巴拉巴拉</pre>
5、非父子組件通信: service 實例共享
組件之間共享同一個服務(wù)實例,利用該服務(wù)在組件之間實現(xiàn)雙向通訊。其實就是服務(wù)實例的一個變量,在引用服務(wù)的各個組件中都可以被改變和讀取
服務(wù):
import { Component, Injectable, EventEmitter } from '@angular/core';
@Injectable()
export class myService {
public info: string = '';
}
組件 1 向 service 傳遞信息
import { Service1 } from '../../service/service1.service';
public constructor(
public service: Service1, // 引用服務(wù)
) { }
public changeInfo():void {
this.service.info = this.service.info + '1234'; // 寫數(shù)據(jù)
}
組件 2 從 service 獲取信息
import { Service2 } from '../../service/service2.service';
public constructor(
public service: Service2, // 引用服務(wù)
) { }
public showInfo() {
console.log(this.service.info); // 讀取數(shù)據(jù)
}
6、非父子組件通信: Subject(發(fā)布訂閱)
發(fā)布訂閱模式,當(dāng)發(fā)布者數(shù)據(jù)改變時,訂閱者也能得到及時響應(yīng)
1、定義事件
@Injectable()
export class AppService {
// 開立診斷,診斷更新后通知,需要的地方注冊監(jiān)聽
afterDiagnosticUpdate: EventEmitter<'ADD' | 'DELETE' | 'INVALID'>;
constructor() {
this.afterDiagnosticUpdate = new EventEmitter();
}
}
1、在發(fā)布事件的組件中進行 emit() :發(fā)出包含給定值的事件。
this.appService.afterDiagnosticUpdate.emit('ADD');
2、在需要知道該事件的組件中進行訂閱 subscribe():即注冊此實例發(fā)出的事件的處理器。
// 注冊監(jiān)聽器(消息訂閱者)
this.msgReader = this.appService.afterDiagnosticUpdate.subscribe((opt: string) => {
// 監(jiān)聽后的處理
doSomething......
});
// 組件銷貨時,注銷監(jiān)聽器
ngOnDestroy(): void {
this.msgReader.unsubscribe();
}
重要:雖然監(jiān)聽器是在組件中定義并創(chuàng)建,但是組件銷毀時,監(jiān)聽器并未自動銷毀,需要調(diào)用unsubscribe 來執(zhí)行銷貨。 這個非常重要,否則輕則內(nèi)存泄露,重則導(dǎo)致邏輯出現(xiàn)異常(邏輯處理在一個已經(jīng)死掉的組件中執(zhí)行,并且執(zhí)行結(jié)果有效)。
7、路由傳參通信
1、 查詢參數(shù)中傳遞數(shù)據(jù)
在a標(biāo)簽上添加一個參數(shù)queryParams,接收要傳入的參數(shù)對象
<a [routerLink]="['/tab4']" [queryParams]="{id:3}" >tab4</a></pre>
在跳轉(zhuǎn)后進入的頁面(組件),注入ActivatedRoute, 用通過對queryParams訂閱的方式,來接收傳遞來的參數(shù):
constructor( private activatedRoute: ActivatedRoute) {}
ngOnInit() {
this.activatedRoute.queryParams.subscribe(params => {
// 接收參數(shù)
this.id = params.id;
});
}
2、 路由路徑(url)中傳遞參數(shù)
修改路由配置文件path還是以tab4組件為例:
{
path: 'tab4/:name',
component:Tab4Component,
children: []
},
我們在后面添加/:name,name即為傳遞的參數(shù)名
a標(biāo)簽設(shè)置如下,routerLink后面數(shù)組的第二個參數(shù)為傳遞的參數(shù)值
<a [routerLink]="['/tab4','我是url傳遞參數(shù)']" [queryParams]="{id:3}" >tab4</a></pre>
在跳轉(zhuǎn)后進入的頁面(組件),注入ActivatedRoute, 用通過對params訂閱的方式,來接收傳遞來的參數(shù)(注意和1對比):
constructor( private activatedRoute: ActivatedRoute) {}
ngOnInit() {
this.activatedRoute.params.subscribe(params => {
// 接收參數(shù)
this.name = params.name;
});
}
也可以這樣寫(**snapshot透過快照的方式獲取值 **)
( private activatedRoute: ActivatedRoute) {}
ngOnInit() {
// 接收參數(shù)
this.name = this.activatedRoute.snapshot.params['name']
}
snapshot快照和subscribe訂閱差別在于:訂閱實時監(jiān)視著數(shù)據(jù)的變化,而快照只在調(diào)用時改變一次,如果在確定路由參數(shù)只在組件初次創(chuàng)建時獲取一次可以采用快照,如需組件內(nèi)路由參數(shù)可能實時變化,則采取訂閱
3、 路由配置中設(shè)置靜態(tài)數(shù)據(jù)
修改路由配置文件path還是以tab4組件為例:
{
path: 'tab4/:name',
component:Tab4Component,
children: [],
data:[{Data:'路由配置靜態(tài)數(shù)據(jù)'}]
}
tab4組件中獲取并賦值數(shù)據(jù)
private data
ngOnInit() {
// this.id=this.activatedRoute.snapshot.queryParams["id"]
// this.name=this.activatedRoute.snapshot.params['name']
this.activatedRoute.queryParams.subscribe((params:Params)=>{
this.id=params['id']
})
this.activatedRoute.params.subscribe((params:Params)=>{
this.name=params['name']
})
//下面為新加入的
this.data=this.activatedRoute.snapshot.data[0]["Data"]
}