Tìm kiếm trang web

angular - Làm thế nào để gọi một phương thức từ một component khác sau khi view đã được tải đầy đủ?


Tôi đang sử dụng MatMenu từ Angular Material bên trong 'Header Component' của mình. Tôi chỉ cần mở Menu trong một số điều kiện nhất định. Tuy nhiên, phương pháp này chỉ hoạt động bên trong ngAfterViewInit(), sau khi view được tải.

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>

Tôi cần gọi phương thức này từ một thành phần khác, nhưng trigger của Menu lại không xác định bên ngoài ngAfterViewInit.

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

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

Làm thế nào tôi có thể thực hiện giao tiếp giữa các thành phần của mình sau khi Thành phần Header của tôi đã được tải?

Câu trả lời #1

Đây là một ví dụ về cách thực hiện (nhưng không nhất thiết là cách tốt nhất). Nó nhằm đưa ra ý tưởng về các tương tác giữa các thành phần.

StackBlitz

Thành phần sản phẩm

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

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

Đánh dấu giá sản phẩm

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

Thành phần đầu trang

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

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

Đánh dấu tiêu đề

<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>

Ý tưởng là kích hoạt một Input của component Header và việc này được thực hiện bằng cách sử dụng BehaviorSubject trong component Product.

Tham khảo :

  • https://angular.io/guide/observables-in-angular
  • https://thinkster.io/tutorials/learn-rxjs-observables/using-behaviorsubject-for-values-that-change-over-time

Câu trả lời #2

Bạn có thể đạt được điều này bằng cách sử dụng một dịch vụ chia sẻ và tiêm dịch vụ đó vào nơi cần thiết.

Thiết lập một dịch vụ chia sẻ, tôi đặt các phương thức để lấy, đặt và chuyển đổi trạng thái menu.

LƯU Ý: để làm cho điều này có hiệu ứng động vì nó sử dụng ngIf, bạn sẽ cần phải sử dụng các hiệu ứng động của Angular.

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;
    }


}

Tiêm dịch vụ vào appComponent để chúng ta có thể điều khiển trạng thái menu bằng nó.

appComponent.ts

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

...

constructor(public sharedService: SharedService){}

Đặt my-header để hiển thị/ẩn dựa trên trạng thái được thiết lập trong sharedService.

appComponent.html

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

Tiêm dịch vụ vào bất kỳ thành phần/trang nào khác để thay đổi trạng thái của menu. Trong trường hợp này là 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>

Hoặc với BehaviorSubject từ dịch vụ.

Tạo BehaviorSubject trong SharedService.

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();
}

Tiêm dịch vụ vào appComponent để chúng ta có thể đăng ký trạng thái menu.

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();
}

Đặt my-header để hiển thị/ẩn dựa trên trạng thái được thiết lập trong sharedService.

appComponent.html

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

Cuối cùng tiêm dịch vụ vào nơi chúng ta cần kiểm soát trạng thái menu.

ProductComponent.ts

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

...

constructor(public sharedService: SharedService){}

và bây giờ chúng ta có thể sử dụng dịch vụ để chuyển đổi trạng thái. ProductComponent.html

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