Angular組件間(如父子組件)通訊及子組件如何修改父組件的屬性

  • 組件間通訊
    • 官網-組件之間的交互
    • Angular Pass data to parent component
    • angular 組件通訊的幾種實現方式
    • Angular組件——父子組件通信
    • Angular中屬性綁定是單向的,從父組件到子組件
    • 父到子
      • 使用@Input()輸入型綁定把數據從父組件傳到子組件
      • 經過setter截聽輸入屬性值的變化
      • 經過ngOnChanges()來截聽輸入屬性值的變化
      • 父組件與子組件經過本地變量互動
      • 父組件調用@ViewChild()
    • 雙向
      • 父組件和子組件經過服務+Rxjs來通信
      • 使用EventEmitter讓父組件監聽子組件的事件
        • 父組件和子組件經過EventEmitter來通信
          • 前提是父組件的html顯式使用了子組件,好比app.component.html中,使用router-outlet標籤結合app.routing.ts來引入不一樣的不肯定的子組件,而沒有直接具體寫出某個子組件的標籤,那就不行了。
          • 假設父組件是A,子組件是B且html名稱爲selector: 'b-template-list',步驟以下:
            • 子組件中建立有合適參數(要傳遞給父組件的數據類型)的用@Output修飾的EventEmitter,至關於C#中的事件:@Output() selectedTemplateData = new EventEmitter ();
            • 子組件中根據業務需求,在合適的地方(如ngAfterViewInit、ngOnInit等生命週期函數或其餘函數中)觸發事件:this.selectedTemplateData.emit(null);
            • 父組件中在其ts中定義子組件發過來事件後要觸發的邏輯函數即回調函數,好比發生事件時要改變某個變量值: onTemplateModeChanged(event) {this.templateMode = event;}
            • 父組件中在其html中的子組件上增長事件、回調函數定義(其實回調函數的參數都寫成$event,其實會被替換成子組件觸發事件時傳遞的參數),將子組件的事件和父組件的回調函數聯繫起來,說明該子組件發出這類事件時觸發父組件的該回調函數:<b-template-list id="b-template-list" (selectedTemplateData)='onTemplateSelected($event)'>
        • 父組件和子組件經過服務+EventEmitter來通信
import { Injectable, EventEmitter, OnInit } from '@angular/core';

@Injectable()
export class EmitService {
    public clearContentMarginBottomEmit: EventEmitter<boolean>;

    constructor() {
        this.clearContentMarginBottomEmit = new EventEmitter(true);
    }
}
* 子組件中引入該服務,並在業務邏輯須要的時機觸發事件
import { EmitService } from '../../shared/emit.service';

export class XXXComponent implements OnInit, OnDestroy, AfterViewInit {
  constructor(
    private readonly emitService: EmitService
}

  ngOnInit() {
    this.emitService.clearContentMarginBottomEmit.emit(false);
  }
}
* 父組件中使用該服務訂閱該事件,並作相關業務處理
import { EmitService } from './shared/emit.service';

export class AppComponent implements OnInit {
  constructor(
    private readonly emitService: EmitService) {
  }

  ngOnInit() {
    this.emitService.clearContentMarginBottomEmit.subscribe((value: boolean) => {
      this.isClearContentMarginBottom = value;
    });
  }
}
  • 關於ExpressionChangedAfterItHasBeenCheckedError
    • 關於ExpressionChangedAfterItHasBeenCheckedError
    • 這是一個用於防止模型數據和ui之間數組不一致的一個警告機制,以便不讓用戶在頁面上看到陳舊的或者錯誤的數據。
    • 該額外的檢查只會在開發模式下執行,多是由於一個不穩定的模型並不像框架產生的運行時錯誤那樣嚴重。
    • 根本緣由
      • 一個運行中的angular應用是一個組件樹。
      • 在組件的生命週期內,angular有一個變動檢測機制,會在進入一個新的生命週期階段時檢查新舊值是否一致。和ASP.NET有點相似?模擬桌面程序、服務器端控件模擬了組件狀態?
      • 一般致使變化的罪魁禍首每每是組件或者指令。
      • 固然,真實場景的案例是錯綜複雜的,那些致使dom渲染的父組件屬性更新或操做每每是經過服務或者可觀察對象模式間接實現的,可是根本原理和緣由是相同的。
    • 一些真實場景下致使錯誤的共同模式
      • 共享服務:該應用設計爲在父組件和子組件之間共享一個服務。子組件爲服務設置一個值,繼而經過更新父組件的屬性實現反應,我稱這個父屬性的更新爲間接的,由於與上面的例子不一樣,如今子組件更新父組件屬性不是很是顯著的。
      • 同步事件廣播(Synchronous event broadcasting):該應用設計爲一個子組件發送一個事件,而後父組件監聽這個事件,該事件會致使父組件一些屬性值的更新。同時這些屬性被用於子組件的輸入綁定中。這也是一個間接的父組件屬性更新。
      • 動態組件實例化(Dynamic component instantiation)ngAfterViewInit:這種模式不一樣於以前輸入綁定受到影響,而是會引發dom更新操做拋出錯誤。該應用設計爲在父組件的ngAfterViewInit中動態的添加一個子組件,因爲添加子組件須要dom修改,dom更新後繼而觸發ngAfterViewInit生命週期鉤子,拋出錯誤。
    • 可能的修復解決方案
      • 異步屬性更新:setTimeout(() => {this.parent.text = 'updated text';});
      • 強制更新檢測:沒有報錯,彷佛正確工做了,可是該解決方案存在一個問題,當觸發對父組件的更新檢測時,angular將運行對全部子組件的變動檢測,會存在父組件屬性更新的可能。
export class AppComponent {
    name = 'I am A component';
    text = 'A message for the child component';
    constructor(private cd: ChangeDetectorRef) {
    }
    ngAfterViewInit() {
        this.cd.detectChanges();
    }
}
相關文章
相關標籤/搜索