【问题标题】:How to call a method from another component after view is fully loaded?视图完全加载后如何从另一个组件调用方法?
【发布时间】:2019-01-16 08:58:26
【问题描述】:

我在“标题组件”中使用来自 Angular Material 的 MatMenu。我只需要在某些条件下打开菜单。但是这个方法只在 ngAfterViewInit() 中起作用,在视图被加载之后。

export class HeaderComponent implements AfterViewInit {
  @ViewChild('triggerCart') trigger: MatMenuTrigger;

  ngAfterViewInit() {
    this.openMenu();
  }

  openMenu() {
    this.trigger.openMenu();
  }
}
<button mat-button 
        [matMenuTriggerFor]="shoppingCartMenu" 
        #triggerCart="matMenuTrigger" 
        (menuOpened)="isOpened($event)" 
        (mouseenter)="largeScreen ? triggerCart.openMenu() : ''">
</button>

我需要从另一个组件调用此方法,但菜单触发器在 ngAfterViewInit 之外未定义。

<button mat-stroked-button
        (click)="someMethod()">
</button>

export class ProductDetailComponent {
  @ViewChild(HeaderComponent) header: HeaderComponent;
  
  someMethod() {
    this.header.openMenu();
  }
}

我的 Header 组件加载后如何实现组件之间的通信?

【问题讨论】:

  • 我不确定您是否可以查询选择也有分配的模板参考。 #triggerCart="matMenuTrigger" 可能与 @ViewChild('triggerCard') 不匹配。我是说我不确定,但是如果您将另一个 #something 添加到同一个元素并尝试查询它会怎样。
  • #triggerCart="matMenuTrigger" 实际上匹配 @ViewChild('triggerCard') 并在我的组件中工作。但仅限于ngAfterViewInit() 方法内。
  • 这意味着在标题组件完成附加视图之前调用someMethod()someMethod() 什么时候被调用?
  • 我在ProductDetailComponent 视图中有一个(click) 事件。 HeaderComponent 内的“菜单”需要通过来自ProductDetailComponent 的点击来触发。

标签: angular typescript angular-material


【解决方案1】:

这里有一个方法示例(但不一定是最好的方法)。这是为了让人们了解组件之间的交互。

StackBlitz

产品组件

export class ProductComponent {
  clickBehavior = new BehaviorSubject(null);

  click() {
    this.clickBehavior.next(1);
  }
}

产品标记

<my-header [openMenu]="clickBehavior"></my-header>
<div>
  <button (click)="click()">Click</button>
</div>

标题组件

export class HeaderComponent implements AfterViewInit {
  @ViewChild('trigger') trigger: MatMenuTrigger;
  @Input() openMenu: Observable<any>;

  ngAfterViewInit() {
    this.openMenu.subscribe(value => {
      if (value) {
        this.trigger.openMenu();
      }
    });
  }
}

标题标记

<button mat-button
        [matMenuTriggerFor]="menu"
        #trigger="matMenuTrigger">Menu</button>
<mat-menu #menu="matMenu">
  <button mat-menu-item>Item 1</button>
  <button mat-menu-item>Item 2</button>
</mat-menu>

这个想法是触发 Header 组件的 Input,这是使用 Product 组件中的 BehaviorSubject 完成的。

参考:

【讨论】:

  • 是否有可能在这种结构中实现相同的结果? app.component.ts &lt;app-header [openMenu]="clickBehavior"&gt;&lt;/app-header&gt; &lt;!-- ProductDetailComponent belongs here --&gt; &lt;router-outlet&gt;&lt;/router-outlet&gt;
  • @James 我会提出一个单独的问题。
  • 你去:link
【解决方案2】:

您可以通过使用共享服务并在需要的地方注入服务来实现此目的。

设置一个共享服务,我设置了获取、设置和切换菜单状态的方法。

注意:为了对此进行动画处理,因为它使用 ngIf,您将需要使用角度动画。

SharedService.ts

import { Injectable } from '@angular/core';

    @Injectable()
    export class SharedService {

    //The current menu state
    private showMenu_ = false;

    //get the current menu state
    get showMenu() {
        return showMenu_;
    }

    //set the menu state
    set showMenu(state: boolean) {
        this.showMenu_ = state;
    }

    //toggle the menu
    public toggleMenu() {
        this.showMenu_ = !this.showMenu;
    }


}

将服务注入到 appComponent 中,这样我们就可以用它来控制菜单状态。

appComponent.ts

import { SharedService } from 'PATH TO SHARED SERVICE';

...

constructor(public sharedService: SharedService){}

根据 sharedService 中设置的状态设置 my-header 以显示/隐藏。

appComponent.html

<my-header *ngIf="sharedService.showMenu"></my-header>

将服务注入任何其他组件/页面以更改菜单的状态。 在本例中为 ProductComponent.ts。

ProductComponent.ts

import { SharedService } from 'PATH TO SHARED SERVICE';

...

constructor(public sharedService: SharedService){}

ProductComponent.html

<div>
  <button (click)="sharedService.toggleMenu()">Click</button>
</div>

或使用服务中的 BehavourSubject。

在 SharedService 中创建 BehaviorSubject。

import { Injectable } from '@angular/core';

@Injectable()
export class SharedService {

//The current menu state
private showMenu_ = false;
private showMenu$: BehaviorSubject<boolean> = new 
BehaviorSubject(false);

//get a reference to showMenu$ for subscription
public menuState() {
    return showMenu$;
}

//Change menu state to show.
public showMenu(){
    this.showMenu_ = true;
    this.showMenu$.next(this.showMenu_);
}

//Change menu state to hide.
public hideMenu(){
    this.showMenu_ = false;
    this.showMenu$.next(this.showMenu_);
}

//Toggle menu state.
public toggleMenu(){
    this.showMenu_ = !this.showMenu;
    this.ShowMenu$.next(this.showMenu_);
}

//get the current menu state.
public getMenuState() {
    return this.showMenu$.getValue();
}

将服务注入到 appComponent 中,以便我们可以订阅菜单状态。

appComponent.ts

import { SharedService } from 'PATH TO SHARED SERVICE';

...

export class appComponent implements OnInit, OnDestroy {

//variable used to show/hide the menu.
public showMenu;

//reference to subscription so we can unsubscribe later.
private this.menuStateSub: Subscription;

constructor(public sharedService: SharedService){}

ngOnInit() {  
    //subscribe to the menuState BehaviorSubject
    this.menuStateSub = this.sharedService.menuState().subscribe((state)=>{
        this.showMenu = state;
    })
}

ngOnDestroy() {
    //unsubscribe before leaving the page
    this.menuStateSub.unsubscribe();
}

根据 sharedService 中设置的状态设置 my-header 以显示/隐藏。

appComponent.html

<my-header *ngIf="sharedService.showMenu"></my-header>

最后在我们需要控制菜单状态的地方注入服务。

ProductComponent.ts

import { SharedService } from 'PATH TO SHARED SERVICE';

...

constructor(public sharedService: SharedService){}

现在我们可以使用该服务来切换状态。 ProductComponent.html

<div>
  <button (click)="sharedService.toggleMenu()">Click</button>
</div>

【讨论】:

    猜你喜欢
    • 2019-01-17
    • 2020-01-11
    • 2022-01-25
    • 2018-10-30
    • 2021-02-19
    • 2014-03-21
    • 2017-05-19
    • 2018-12-11
    • 1970-01-01
    相关资源
    最近更新 更多