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

Angular (v5+) - Snackbar "openFromComponent", component communication

Angular (v5.2.10) Snackbar

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

--|代码|--

"Parent"分量:

@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应用程序的当前封装来做到这一点?

您实际上不需要调用父组件的 "dismissSnackbar()" 方法来关闭快餐栏。您可以简单地将 "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();
    }
}

yatharth 的解决方案将起作用。

但是,如果您想让您的组件面向未来,您应该使用服务。通过服务,您可以从应用程序的任何位置关闭您的小吃店——而不仅仅是像他的方法那样的小吃店消息组件。

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

简单示例:

@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);

或者关闭快餐栏:

snackbarService.status.next(false);

如果您想要获得的案例不只是 open/closed,您可以使用具有不同值的枚举:

export enum SnackbarStatus {
    Open,
    Closed,
    SomethingElse
}

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

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

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

'Parent Component' 充当 MatSnackBarSnackBarMessageComponent 之间的 'Mediator' class。现在,SnackBarMessageComponent 与 MatSnackBar 分离,成为一个普通组件,可以重命名为 MessageComponent。您可以使用 @Input@Output 在父组件和子组件之间发送和接收数据。您可以根据需要添加任意数量的 @Input@Output

代码超过 1.000 个字:

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>

(以上代码未经测试)

使用 ComponentRef 调用您的方法。

MyCustomSnackBarComponent 使用您的自定义方法

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

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

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

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