摘要:在應用開發中,組件可以說是隨處可見的。本篇文章將介紹幾種常見的組件通訊場景,也就是讓兩個或多個組件之間交互的方法。父組件綁定到這個事件屬性,并在事件發生時作出回應。然后通過的元數據數組提供服務的實例,并通過構造函數分別注入到父子組件中。
在Angular應用開發中,組件可以說是隨處可見的。本篇文章將介紹幾種常見的組件通訊場景,也就是讓兩個或多個組件之間交互的方法。
根據數據的傳遞方向,分為父組件向子組件傳遞、子組件向父組件傳遞及通過服務傳遞三種交互方法。
父組件向子組件傳遞子組件通過@Input裝飾器定義輸入屬性,然后父組件在引用子組件的時候通過這些輸入屬性向子組件傳遞數據,子組件可通過setter或ngOnChanges()來截聽輸入屬性值的變化。
先定義兩個組件,分別為子組件DemoChildComponent和父組件DemoParentComponent.
子組件:
@Component({ selector: "demo-child", template: `{{paramOne}}
{{paramTwo}}
` }) export class DemoChildComponent { @Input() paramOne: any; // 輸入屬性1 @Input() paramTwo: any; // 輸入屬性2 }
子組件通過@Input()定義輸入屬性paramOne和paramTwo(屬性值可以為任意數據類型)
父組件:
@Component({ selector: "demo-parent", template: `` }) export class DemoParentComponent { paramOneVal: any = "傳遞給paramOne的數據"; paramTwoVal: any = "傳遞給paramTwo的數據"; }
父組件在其模板中通過選擇器demo-child引用子組件DemoChildComponent,并通過子組件的兩個輸入屬性paramOne和paramTwo向子組件傳遞數據,最后在子組件的模板中就顯示傳遞給paramOne的數據和傳遞給paramTwo的數據這兩行文本。
通過 setter 截聽輸入屬性值的變化在實際應用中,我們往往需要在某個輸入屬性值發生變化的時候做相應的操作,那么此時我們需要用到輸入屬性的 setter 來截聽輸入屬性值的變化。
我們將子組件DemoChildComponent進行如下改造:
@Component({ selector: "demo-child", template: `{{paramOneVal}}
{{paramTwo}}
` }) export class DemoChildComponent { private paramOneVal: any; @Input() set paramOne (val: any) { // 輸入屬性1 this.paramOneVal = val; // dosomething }; get paramOne () { return this.paramOneVal; }; @Input() paramTwo: any; // 輸入屬性2 }
在上面的代碼中,我們可以看到通過paramOne屬性的 setter 將攔截到的值val賦值給內部私有屬性paramOneVal,達到父組件傳遞數據給子組件的效果。當然,最重要的是,在 setter 里面你可以做更多的其它操作,程序的靈活性就更強了。
通過ngOnChanges()來截聽輸入屬性值的變化通過 setter 截聽輸入屬性值的變化的方法只能對單個屬性值變化進行監視,如果需要監視多個、交互式輸入屬性的時候,這種方法就顯得力不從心了。而通過使用 OnChanges 生命周期鉤子接口的 ngOnChanges() 方法(當組件通過@Input裝飾器顯式指定的那些變量的值變化時調用)就可以實現同時監視多個輸入屬性值的變化。
在子組件DemoChildComponent新增ngOnChanges:
@Component({ selector: "demo-child", template: `{{paramOneVal}}
{{paramTwo}}
` }) export class DemoChildComponent implements OnChanges { private paramOneVal: any; @Input() set paramOne (val: any) { // 輸入屬性1 this.paramOneVal = val; // dosomething }; get paramOne () { return this.paramOneVal; }; @Input() paramTwo: any; // 輸入屬性2 ngOnChanges(changes: {[propKey: string]: SimpleChange}) { for (let propName in changes) { // 遍歷changes let changedProp = changes[propName]; // propName是輸入屬性的變量名稱 let to = JSON.stringify(changedProp.currentValue); // 獲取輸入屬性當前值 if (changedProp.isFirstChange()) { // 判斷輸入屬性是否首次變化 console.log(`Initial value of ${propName} set to ${to}`); } else { let from = JSON.stringify(changedProp.previousValue); // 獲取輸入屬性先前值 console.log(`${propName} changed from ${from} to ${to}`); } } } }
新增的ngOnChanges方法接收的參數changes是以輸入屬性名稱為鍵、值為SimpleChange的對象,SimpleChange對象含有當前輸入屬性是否第一次變化、先前值、當前值等屬性。因此在ngOnChanges方法中通過遍歷changes對象可監視多個輸入屬性值并進行相應的操作。
獲取父組件實例前面介紹的都是子組件通過@Input裝飾器定義輸入屬性,這樣父組件可通過輸入屬性將數據傳遞給子組件。
當然,我們可以想到一種更主動的方法,那就是獲取到父組件實例,然后調用父組件的某個屬性或方法來獲取需要的數據。考慮到每個組件的實例都會添加到注入器的容器里,因此可通過依賴注入來找到父組件的示例。
子組件獲取父組件實例相比于父組件獲取子組件實例(直接通過模板變量、@ViewChild或@ViewChildren獲取)要麻煩一些。
要在子組件中獲取父組件的實例,有兩種情況:
已知父組件的類型
這種情況可以直接通過在構造函數中注入DemoParentComponent來獲取已知類型的父組件引用,代碼示例如下:
@Component({ selector: "demo-child", template: `{{paramOne}}
{{paramTwo}}
` }) export class DemoChildComponent { paramOne: any; paramTwo: any; constructor(public demoParent: DemoParentComponent) { // 通過父組件實例demoParent獲取數據 this.paramOne = demoParent.paramOneVal; this.paramTwo = demoParent.paramTwoVal; } }
未知父組件的類型
一個組件可能是多個組件的子組件,有時候無法直接知道父組件的類型,在Angular中,可通過類—接口(Class-Interface)的方式來查找,即讓父組件通過提供一個與類—接口標識同名的別名來協助查找。
首先創建DemoParent抽象類,它只聲明了paramOneVal和paramTwoVal屬性,沒有實現(賦值),示例代碼如下:
export abstract class DemoParent { paramOneVal: any; paramTwoVal: any; }
然后在父組件DemoParentComponent的providers元數據中定義一個別名 Provider,用 useExisting 來注入父組件DemoParentComponent的實例,代碼示例如下:
@Component({ selector: "demo-parent", template: ``, providers: [{provider: DemoParent, useExisting: DemoParentComponent}] }) export class DemoParentComponent implements DemoParent { paramOneVal: any = "傳遞給paramOne的數據"; paramTwoVal: any = "傳遞給paramTwo的數據"; }
然后在子組件中就可通過DemoParent這個標識找到父組件的示例了,示例代碼如下:
@Component({ selector: "demo-child", template: `子組件向父組件傳遞{{paramOne}}
{{paramTwo}}
` }) export class DemoChildComponent { paramOne: any; paramTwo: any; constructor(public demoParent: DemoParent) { // 通過父組件實例demoParent獲取數據 this.paramOne = demoParent.paramOneVal; this.paramTwo = demoParent.paramTwoVal; } }
依然先定義兩個組件,分別為子組件DemoChildComponent和父組件DemoParentComponent.
子組件:
@Component({ selector: "demo-child", template: `子組件DemoChildComponent
` }) export class DemoChildComponent implements OnInit { readyInfo: string = "子組件DemoChildComponent初始化完成!"; @Output() ready: EventEmitter = new EventEmitter(); // 輸出屬性 ngOnInit() { this.ready.emit(this.readyInfo); } }
父組件:
@Component({ selector: "demo-parent", template: `父組件監聽子組件的事件readyInfo: {{demoChild.readyInfo}}
readyInfo: {{demoChildComponent.readyInfo}}
` }) export class DemoParentComponent implements AfterViewInit { // @ViewChild("demoChild") demoChildComponent: DemoChildComponent; // 通過模板別名獲取 @ViewChild(DemoChildComponent) demoChildComponent: DemoChildComponent; // 通過組件類型獲取 ngAfterViewInit() { console.log(this.demoChildComponent.readyInfo); // 打印結果:子組件DemoChildComponent初始化完成! } onReady(evt: any) { console.log(evt); // 打印結果:子組件DemoChildComponent初始化完成! } }
子組件暴露一個 EventEmitter 屬性,當事件發生時,子組件利用該屬性 emits(向上彈射)事件。父組件綁定到這個事件屬性,并在事件發生時作出回應。
在上面定義好的子組件和父組件,我們可以看到:
子組件通過@Output()定義輸出屬性ready,然后在ngOnInit中利用ready屬性的 emits(向上彈射)事件。
父組件在其模板中通過選擇器demo-child引用子組件DemoChildComponent,并綁定了一個事件處理器(onReady()),用來響應子組件的事件($event)并打印出數據(onReady($event)中的$event是固定寫法,框架(Angular)把事件參數(用 $event 表示)傳給事件處理方法)。
父組件與子組件通過本地變量(模板變量)互動父組件不能使用數據綁定來讀取子組件的屬性或調用子組件的方法。但可以在父組件模板里,新建一個本地變量來代表子組件,然后利用這個變量來讀取子組件的屬性和調用子組件的方法。
在上面定義好的子組件和父組件,我們可以看到:
父組件在模板demo-child標簽上定義了一個demoChild本地變量,然后在模板中獲取子組件的屬性:
父組件調用@ViewChild()readyInfo: {{demoChild.readyInfo}}
本地變量方法是個簡單便利的方法。但是它也有局限性,因為父組件-子組件的連接必須全部在父組件的模板中進行。父組件本身的代碼對子組件沒有訪問權。
如果父組件的類需要讀取子組件的屬性值或調用子組件的方法,就不能使用本地變量方法。
當父組件類需要這種訪問時,可以把子組件作為 ViewChild,注入到父組件里面。
在上面定義好的子組件和父組件,我們可以看到:
父組件在組件類中通過@ViewChild()獲取到子組件的實例,然后就可以在模板或者組件類中通過該實例獲取子組件的屬性:
readyInfo: {{demoChildComponent.readyInfo}}
ngAfterViewInit() { console.log(this.demoChildComponent.readyInfo); // 打印結果:子組件DemoChildComponent初始化完成! }通過服務傳遞
Angular的服務可以在模塊注入或者組件注入(均通過providers注入)。
在模塊中注入的服務在整個Angular應用都可以訪問(除惰性加載的模塊)。
在組件中注入的服務就只能該組件和其子組件進行訪問,這個組件子樹之外的組件將無法訪問該服務或者與它們通訊。
下面的示例就以在組件中注入的服務來進行父子組件之間的數據傳遞:
通訊的服務:
@Injectable() export class CallService { info: string = "我是CallService的info"; }
父組件:
@Component({ selector: "demo-parent", template: `{{callService.info}}
`, providers: [CallService] }) export class DemoParentComponent { constructor(public callService: CallService) { console.log(callService.info); // 打印結果:我是CallService的info } changeInfo() { this.callService.info = "我是被父組件改變的CallService的info"; } }
子組件:
@Component({ selector: "demo-child", template: ` ` }) export class DemoChildComponent { constructor(public callService: CallService) { console.log(callService.info); // 打印結果:我是CallService的info } changeInfo() { this.callService.info = "我是被子組件改變的CallService的info"; } }
上面的代碼中,我們定義了一個CallService服務,在其內定義了info屬性,后面將分別在父子組件通過修改這個屬性的值達到父子組件互相傳遞數據的目的。
然后通過DemoParentComponent的providers元數據數組提供CallService服務的實例,并通過構造函數分別注入到父子組件中。
此時,通過父組件改變info按鈕或子組件改變info按鈕在父組件或子組件中改變CallService服務的info屬性值,然后在頁面可看到改變之后對應的info屬性值。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/93675.html
摘要:斯坦福宣布使用作為計算機課程的首選語言近日,某位有年教學經驗的斯坦福教授決定放棄,而使用作為計算機入門課程的教學語言。斯坦福官方站點將它們新的課程描述為是最流行的構建交互式的開發語言,本課程會用講解中的實例。 前端每周清單第 11 期:Angular 4.1支持TypeScript 2.3,Vue 2.3優化服務端渲染,優秀React界面框架合集 為InfoQ中文站特供稿件,首發地址為...
摘要:是文檔的一種表示結構。這些任務大部分都是基于它。這個實踐的重點是把你在前端練級攻略第部分中學到的一些東西和結合起來。一旦你進入框架部分,你將更好地理解并使用它們。到目前為止,你一直在使用進行操作。它是在前端系統像今天這樣復雜之前編寫的。 本文是 前端練級攻略 第二部分,第一部分請看下面: 前端練級攻略(第一部分) 在第二部分,我們將重點學習 JavaScript 作為一種獨立的語言,如...
摘要:具體來說,包管理器就是可以通過命令行,幫助你把外部庫和插件放到你的項目里面并在之后進行版本升級,這樣就不用手工復制和更新庫。現在有的包管理器主要是和。 一、基礎 1、學習HTML基礎 HTML給你的網頁賦予了結構。它就像是人的骨架那樣讓你保持站立。首先你需要去學習語法以及它必須提供的一切。你的學習應該聚焦在下面這些東西上: 學習HTML基礎,了解如何編寫語義HTML 理解如何把網頁分...
閱讀 649·2021-10-13 09:39
閱讀 1456·2021-09-09 11:53
閱讀 2649·2019-08-29 13:55
閱讀 725·2019-08-28 18:08
閱讀 2596·2019-08-26 13:54
閱讀 2411·2019-08-26 11:44
閱讀 1839·2019-08-26 11:41
閱讀 3782·2019-08-26 10:15