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

Posted

技术标签:

【中文标题】Angular 材质 Snackbar 配置与自定义 panelClass 配置,用于错误、成功、警告消息【英文标题】:Angular material Snackbar configuration with custom panelClass configuration for error, success, warning messages 【发布时间】:2020-09-10 03:06:35 【问题描述】:

我在我的 Angular 应用程序中创建了一个全局的snackBarService。我想根据消息类型(错误、成功、警告等)自定义panelClass。我采用的方法是在构造函数中进行全局配置,这有助于为小吃店定义全局样式/配置,并将添加自定义类以根据消息类型更改背景颜色。

SnackBarService.ts

import  Injectable, NgZone  from "@angular/core";
import  MatSnackBar, MatSnackBarConfig  from "@angular/material";

@Injectable(
  providedIn: "root",
)
export class SnackbarService 
  private config: MatSnackBarConfig;

  constructor(private snackbar: MatSnackBar, private zone: NgZone) 
    this.config = new MatSnackBarConfig();
    this.config.panelClass = ["snackbar-container"];
    this.config.verticalPosition = "top";
    this.config.horizontalPosition = "right";
    this.config.duration = 4000;
  

  error(message: string) 
    this.config.panelClass = ["snackbar-container", "error"];
    this.show(message);
  

  success(message: string) 
    this.config.panelClass = ["snackbar-container", "success"];
    this.show(message);
  

  warning(message: string) 
    this.config.panelClass = ["snackbar-container", "warning"];
    this.show(message);
  

  private show(message: string, config?: MatSnackBarConfig) 
    config = config || this.config;
    this.zone.run(() => 
      this.snackbar.open(message, "x", config);
    );
  

app.scss

.snackbar-container 
  margin-top: 70px !important;
  color: beige;
  &.error 
    background-color: #c62828 !important;
  
  &.success 
    background-color: #2e7d32 !important;
  

  &.warning 
    background-color: #ff8f00 !important;
  

从组件中我将使用这样的服务

this.snackbarService.success("This message is from snackbar!!!");

以上代码完美运行。

但是,

由于panelClass没有.push方法,我无法添加动态类,因此我每次都需要像this.config.panelClass = ["snackbar-container", "error"];这样复制全局类

 error(message: string) 
    this.config.panelClass.push("error"); // this throws error in typescript
    this.show(message);
  

有没有更好的方法来解决这个问题?

【问题讨论】:

【参考方案1】:

Angular Material 实际上为您提供了一种设置默认配置的本地方式,因此您无需实例化 MatSnackBarConfig 然后设置其值。在您导入 MatSnackBarModule 的模块(App/Shared/Material 模块)中,添加以下内容:

import  MatSnackBarModule, MatSnackBarConfig, MAT_SNACK_BAR_DEFAULT_OPTIONS  from '@angular/material/snack-bar';

const matSnackbarDefaultConfig: MatSnackBarConfig = 
  verticalPosition: 'top',
  horizontalPosition: 'right',
  duration: 4000,
;

@NgModule(
  // ...
  providers: [
    
      provide: MAT_SNACK_BAR_DEFAULT_OPTIONS,
      useValue: matSnackbarDefaultConfig,
    ,
  ],
)
export class MaterialModule  

然后,您可以像这样使用您的服务(我添加了更多的输入内容,如果您不喜欢它并且不使用 strictNullChecks,请随时删除它们):

import  Injectable, NgZone  from '@angular/core';
import  MatSnackBar, MatSnackBarConfig  from '@angular/material/snack-bar';

// I actually recommend that you put this in a utils/helpers folder so you can use reuse it whenever needed
export const coerceToArray = <T>(value: T | T[]): T[] => (
  Array.isArray(value)
    ? value
    : [value]
);

@Injectable(
  providedIn: 'root',
)
export class SnackbarService 
  constructor(private snackbar: MatSnackBar, private zone: NgZone)  

  error(message: string): void 
    this.show(message,  panelClass: ['snackbar-container', 'error'] );
  

  success(message: string): void 
    this.show(message,  panelClass: ['snackbar-container', 'success'] );
  

  warning(message: string): void 
    this.show(message,  panelClass: ['snackbar-container', 'warning'] );
  

  private show(message: string, customConfig: MatSnackBarConfig = ): void 
    const customClasses = coerceToArray(customConfig.panelClass)
      .filter((v) => typeof v === 'string') as string[];

    this.zone.run(() => 
      this.snackbar.open(
        message,
        'x',
         ...customConfig, panelClass: ['snackbar-container', ...customClasses] ,
      );
    );
  


此外,由于您的公共方法不接受其他配置来传递(例如持续时间),您可以将您的服务减少到:

import  Injectable, NgZone  from '@angular/core';
import  MatSnackBar  from '@angular/material/snack-bar';

// Just add the new required types here and TypeScript will require the public consumer to pass a valid type
export type SnackBarType = 'error' | 'success' | 'warning'; 

@Injectable(
  providedIn: 'root',
)
export class SnackbarService 
  constructor(private snackbar: MatSnackBar, private zone: NgZone)  

  show(message: string, type: SnackBarType): void 
    this.zone.run(() => 
      this.snackbar.open(
        message,
        'x',
         panelClass: ['snackbar-container', type] ,
      );
    );
  

【讨论】:

【参考方案2】:

你可以这样做:

(this.config.panelClass as string[]).push('error');

但是您将添加类而不删除已经存在的类。您仍然需要每次使用初始类重置数组:

this.config.panelClass = ['snackbar-container']

【讨论】:

以上是关于Angular 材质 Snackbar 配置与自定义 panelClass 配置,用于错误、成功、警告消息的主要内容,如果未能解决你的问题,请参考以下文章

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

Angular Material Snackbar 未正确显示

如何添加具有功能的 Angular 材质自定义 mdToast?

Angular 2 Snackbar - 全局持续时间配置

角材质覆盖 SnackBar 组件的默认样式

无按钮角材质基本小吃吧