簡體   English   中英

如何調用 Angular 中任何組件的特定公共方法,例如 `ngOnInit` 的工作原理

[英]How can I call a specific public method of any component in Angular, like how `ngOnInit` works

在我的項目中,我有很多組件。 我有一個事件處理程序。 當特定事件發生時,我想通過名稱調用組件內的特定方法(如果它存在於任何組件中)。 請找到以下示例:

@Component({
...
})
export class MyComponent {

   public onMyEvent() {
     // do stuff on that event
   }
}

在這里,我希望在事件發生時調用onMyEvent() ,如下所示:

export class MyEventHandler {
   private myEvent: Subject;
   
   public registerEvents() {
      this.myEvent.subcribe(() => {
        // call "onMyEvent" function of all components that exists in the view
      });
   }

}

該場景類似於ngOnInit()ngAfterViewInit() .. 的工作方式。 當您實現OnInitAfterViewInit接口時,您正在定義一個合同,保證該組件具有指定的方法,該方法隨時可以調用。 我想做同樣的事情。

這可以使用ComponentRef來實現。 但是只有父組件可以調用它的子組件,並且非常依賴於 HTML 結構。 我希望它像ngOnInit一樣無縫。

我覺得這里有一些並行的概念,所以如果我的回答有誤,請原諒我。 請讓我知道哪些元素不正確,我可以相應地修改我的答案

據我了解,您需要以下內容:

  1. 一組可能/可能不實現onMyEvent方法的組件
  2. 一些/所有這些組件中的觀察者(訂閱)如果存在則嘗試觸發此方法

tl:dr 以實現類似ngOnInit的體驗(無需組件配置即可開箱即用)所有組件將默認強制設置訂閱,這在架構上不是很好。 注入由組件本身確定的訂閱的 singleton 服務似乎是一種更明智的方法,但這需要您沒有生命周期掛鈎的設置,因此可能不符合“無縫”的條件


第 1 點很簡單,正如您已經提到的,我們可以為許多要實現的組件定義一個共享接口,這要求存在onMyEvent

export interface IEventHandler {
  onMyEvent: () => void
}
export class MyComponent1 implements IEventHandler {
  constructor() {}

  public onMyEvent() {
    // do stuff on that event
  }
}

export class MyComponent2 {
  constructor() {}
}

export class MyComponent3 implements IEventHandler { // error: `onMyEvent` is missing
  constructor() {}
}

第 2 點需要單個 stream(即ObservableSubjectEventEmitter等),它能夠被多個組件監視

顯而易見的解決方案是一個包含這個 stream 的服務,並且可以依賴注入到任何需要它的組件中。

export class EventHandlerService {
  public myEventSubject: Subject;
}

但是,我假設您不想為每個需要它的組件生成一個唯一的服務實例,而是想要一個 singleton 服務,它向所有組件廣播相同的事件。 結果,您將訂閱事件的責任放在組件本身上,即

export class MyComponent1 implements IEventHandler {
  constructor(private eventHandlerService: EventHandlerService) {}

  ngOnInit() {
    this.eventHandlerService.myEventSubject.subscribe({
      next: () => this.onMyEvent() // guaranteed to exist as we have implemented `IEventHandler`
    })
  }

  public onMyEvent() {
    // do stuff on that event
  }
}

這需要手動設置,而內置的生命周期掛鈎(如ngOnInit )則不需要,但這里的區別在於所有組件都需要ngOnInit ,而其中只有一些組件需要訂閱myEventSubject 正是這種選擇性阻止了體驗的“無縫”

你當然可以探索一個基礎 class 來讓你的工作組件稍微更“整潔”

export class BaseEventHandler implements IEventHandler {
  constructor(public eventHandlerService: EventHandlerService) {}

  ngOnInit() {
    this.eventHandlerService.myEventSubject.subscribe({
      next: () => this.onMyEvent() // guaranteed to exist as we have implemented `IEventHandler`
    })
  }

  public onMyEvent() {
    // do stuff on that event
  }
}

export class MyComponent1 extends BaseEventHandler {
  constructor(public eventHandlerService: EventHandlerService) {
    super(eventHandlerService)
  }

  ngOnInit() {
    super.ngOnInit();
  }
}

export class MyComponent2 {
  constructor() {}
}

但是與以前的實現相比似乎沒有什么好處,考慮到您仍然依賴於服務和對服務變量的選擇性訂閱(在本例中是BaseEventHandler的選擇性擴展)


您還提到了ComponentRef ,即大概使用ViewChild訪問組件實例並以如下方式調用相關方法:

@ViewChild(ChildComponent) childComponentRef: ComponentRef

this.childComponentRef.onMyEvent()

但是在這種情況下,事件的觀察者仍然必須駐留在各個組件中(在這種情況下是父組件而不是子組件)並且由於並非所有父組件都需要訂閱觀察者,您將再次實現這是使用選擇性訂閱與始終可用的掛鈎,例如ngOnInit()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM