Angular (v5+) - Snackbar “openFromComponent”,组件通信

Posted

技术标签:

【中文标题】Angular (v5+) - Snackbar “openFromComponent”,组件通信【英文标题】:Angular (v5+) - Snackbar "openFromComponent", component communication 【发布时间】:2018-10-29 03:58:59 【问题描述】:

Angular (v5.2.10) Snackbar

--|简介 |-- 我有一个 Angular 组件(我们称之为“父级”)初始化一个名为 snackBar 的 Angular 材质 Snackbar。传入的是 SnackbarMessage ,这是另一个带有包含小吃栏标记的模板的组件。在这种情况下使用snackBar.openFromComponent(SnackBarMessage) 是必要的,因为我需要在小吃栏中使用的不仅仅是纯文本[如标记、点击事件等],而snackBar.open(message, action) 是不够的。 --|代码 |--“父”组件:

@Component(
  selector: 'app-parent',
  templateUrl: './parent.component.html'
)
export class Parent implements AfterViewInit 

  public constructor(public snackBar: MatSnackBar)  

  public ngAfterViewInit(): void 
      this.snackBar.openFromComponent(SnackbarMessage);
  

  public dismissSnackbar(): void 
    this.snackBar.dismiss();
  

“SnackbarMessage”组件:

@Component(
  selector: 'app-snackbar-message',
  templateUrl: './snackbar-message.html'
)
export class SnackbarMessage  

“snackbar-message.html”标记:

<p>(Snackbar message)</p>
<button type="button" (click)="dismissSnackbar();">Dismiss</button>

--|问题 |-- 在导入的SnackbarMessage 模板(snackbar-message.html)中我需要调用Parent 组件的dismissSnackbar();,我们如何使用这个Angular 应用程序的当前封装来做到这一点?

【问题讨论】:

【参考方案1】:

您实际上不需要调用父组件的“dismissSnackbar()”方法来关闭snackbar。您可以简单地将“MatSnackBar”注入“SnackbarMessage”组件并在“MatSnackBar”的注入实例上调用“dismiss()”方法。正如docs 中所写,这将隐藏当前可见的 Snackbar,即在您的示例中使用“SnackbarMessage”组件打开的 Snackbar。以下是您更新的“SnackbarMessage”组件:-

“SnackbarMessage”组件:

@Component(
    selector: 'app-snackbar-message',
    templateUrl: './snackbar-message.html'
)
export class SnackbarMessage  
    constructor(public snackBar: MatSnackBar) 

    public dismissSnackbar(): void 
        this.snackBar.dismiss();
    

【讨论】:

这不是太复杂+1 如果您需要使用 MatSnackBarRef 中的方法,例如dismissWithAction(),你也可以注入它。 private matSnackBar: MatSnackBarRef&lt;MatSnackBar&gt;.【参考方案2】:

yatharth 的解决方案会奏效。

但是,如果您想让您的组件经得起未来的考验,您应该使用服务。借助服务,您可以从应用程序中的任何位置关闭您的小吃店——而不仅仅是像他的方法中的小吃店消息组件。

创建一个可观察的流,并在您的快餐栏组件中订阅它。它只会传递一个布尔值(true 或 false),每次在流中发出新值时,您都可以使用该值切换您的快餐栏组件。

简单示例:

@Injectable()
export class SnackbarService 
    status: BehaviorSubject<boolean> = new BehaviorSubject(false);
    status$: Observable<boolean> = this.status.asObservable();


@Component(
    selector: 'app-snackbar-message',
    templateUrl: './snackbar-message.html'
)
export class SnackbarMessage  
    open: boolean = false;

    constructor(public snackbarService: SnackbarService) 
        snackbarService.status$.subscribe((open: boolean) => this.open = open);
    

现在,您只需在应用中的任何位置打开快餐栏即可:

snackbarService.status.next(true);

或者这样关闭snackbar:

snackbarService.status.next(false);

如果您想获得更多的案例,而不仅仅是打开/关闭,您可以使用具有不同值的枚举:

export enum SnackbarStatus 
    Open,
    Closed,
    SomethingElse

在您的可观察流中传递一个枚举,而不是布尔值:

@Injectable()
export class SnackbarService 
    status: BehaviorSubject<SnackbarStatus> = new BehaviorSubject(SnackbarStatus.Closed);
    status$: Observable<SnackbarStatus> = this.status.asObservable();

【讨论】:

很好的解释,这个解决方案可以很好地满足我的需要。谢谢你好先生【参考方案3】:

我今天使用MatSnackBar.openFromTemplate 而不是MatSnackBar.openFromComponent 解决了与您类似的问题。使用这种方法,子组件的所有功能都可以直接从父组件访问。

“父组件”充当MatSnackBarSnackBarMessageComponent 之间的“中介”类。现在,SnackBarMessageComponent 与 MatSnackBar 解耦,成为一个普通组件,可以重命名为 MessageComponent。您可以使用普通的@Input@Output 在父组件和子组件之间发送和接收数据。您可以根据需要添加任意数量的@Inputs 或@Outputs。

代码超过 1000 个单词:

parent.component.html

<h1>parent component</h1>
<ng-template #snackBarTemplate>
    <app-message [msg]="message" (onDismissClick)="dismissSnackbar"></app-message>
</ng-template>

parent.component.ts

@Component(
  selector: 'app-parent',
  templateUrl: './parent.component.html'
)
export class Parent implements AfterViewInit 

  @ViewChild('snackBarTemplate')
  snackBarTemplate: TemplateRef<any>;

  public message: string;

  public constructor(public snackBar: MatSnackBar)  

  public ngAfterViewInit(): void 
     this.message = '(Snackbar message)';
     this.snackBar.openFromTemplate(snackBarTemplate);
  

  public dismissSnackbar(): void 
     this.snackBar.dismiss();
  

message.component.ts

@Component(
  selector: 'app-message',
  templateUrl: './message.html'
)
export class MessageComponent  
    @Input()
    msg: string;

    @Output()
    onDismissClick= new EventEmitter<any>();

    dismissClicked() 
       this.onDismissClick.emit(null);
    


message.component.html

<p>msg</p>
<button type="button" (click)="dismissClicked()">Dismiss</button>

(以上代码未经测试)

【讨论】:

【参考方案4】:

使用 ComponentRef 来调用你的方法。

MyCustomSnackBarComponent 使用您的自定义方法

public miMethod(): void  
    console.log('call from parent');

您在其中创建了 MyCustomSnackBarComponent 实例的组件:

const snackBarRef = this.snackbar.openFromComponent(MyCustomSnackBarComponent);
snackBarRef.instance.miMethod();

如果您的方法有参数,此解决方案仍然适用

【讨论】:

以上是关于Angular (v5+) - Snackbar “openFromComponent”,组件通信的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Angular 5 的 Angular 材质 Snackbar 中添加图标

Angular2 Material SnackBar 集成问题

Angular Material Snackbar 位置

Angular 材质 Snackbar 配置与自定义 panelClass 配置,用于错误、成功、警告消息

Angular Material Snackbar 未正确显示

如何在服务上使用 SnackBar 以在 Angular 2 中的每个组件中使用