从任何组件动态更改 mat-sidenav 内容

Posted

技术标签:

【中文标题】从任何组件动态更改 mat-sidenav 内容【英文标题】:change mat-sidenav content dynamically from any component 【发布时间】:2019-11-08 09:04:48 【问题描述】:

我正在尝试根据按钮单击等操作从不同的路由组件加载 mat-sidenav 中的不同组件?

我创建了一个单例服务“SidenavService”以使用 @ViewChild 获取对 mat-sidenav 的引用,这样我就可以从应用程序的任何位置控制(如打开、关闭、切换等)sidenav。

我还在 mat-sidenav 中创建了一个 ng-container 并将其 ViewConteinerRef 存储在 SidenavService 中,现在在组件层次结构中的任何组件中,我都可以注入 SidenavService 并且可以使用 ViewConteinerRef 来创建嵌入式视图。

stackblitz 链接 https://stackblitz.com/edit/angular-material-sidenav-dynamic-content

有没有更好的方法来实现这一点?从应用程序的任何位置加载 mat-sidenav 中的不同组件。

【问题讨论】:

【参考方案1】:

通过您的 StackBlitz 进行实验,我提出了一个使用 Angular CDK(组件开发工具包)中的 portal component 的解决方案。

我所做的是修改原始SidenavService 代码以允许将Portal 传递给Subject,这将向任何订阅Subject 的组件发出Portal。动态 sidenav 将在 sidenav 内部有一个 <ng-template> 部分,其中 cdkPortalOutlet 属性绑定到 Portal Subject

<mat-sidenav #rightPanel position="end">
  <ng-template [cdkPortalOutlet]="panelService.panelPortal | async"></ng-template>
</mat-sidenav>
export class AppComponent 
  constructor(public panelService: PanelService)  

但是,请务必将 PortalModule@angular/cdk/portal 入口点导入到您应用的模块中:

import  PortalModule  from '@angular/cdk/portal';
// ...

@NgModule(
  imports: [
    // ...
    PortalModule
  ],
  // ...
)

通过允许SidenavService 代码允许Portal 引用,您现在可以将模板引用(TemplateRef&lt;T&gt;) 或组件(ComponentType&lt;T&gt;) 传递给SidenavService。这样可以简化代码,无需为 sidenav 的动态内容创建专用组件。

但是,TemplatePortal (which is a Portal that represents an embedded template) 要求为第二个参数指定 ViewContainerRef。在这种情况下,我所做的是允许用户指定一个 ViewContainerRef 以传递给服务,以便将其用于 TemplatePortal

无论如何,这是服务代码的一部分。 (注:我已将服务名称修改为更好听的名称:PanelService

import  from  from 'rxjs';
// ...

export class PanelService 
  /** The panel. */
  panel: MatSidenav;
  private vcr: ViewContainerRef;
  // Note: The Portal class requires that a generic is specified for the component/template type.
  private panelPortal$ = new Subject<Portal<any>>();

  get panelPortal() 
    return from(this.panelPortal$); // Or this.panelPortal$.asObservable()
  

  setViewContainerRef(vcr: ViewContainerRef) 
    this.vcr = vcr;
  

  setPanelPortal(portal: Portal<any>) 
    this.panelPortal$.next(portal);
  

  // Wrapper methods for MatSidenav go here.
  // ...

这里有一个StackBlitz 供你玩耍。

希望这会有所帮助!

【讨论】:

很好的答案,谢谢。由于 flexlayout,您的 stackblitz 不起作用。 Stackbliz 现在在 ivy 上运行,显然它有问题。我刚刚删除了 flexlayout 部分,它可以工作。 嘿@Laker,我已经解决了 StackBlitz 项目无法正常运行的问题。

以上是关于从任何组件动态更改 mat-sidenav 内容的主要内容,如果未能解决你的问题,请参考以下文章

无法从 Angular 的 mat-sidenav 内的组件中检索路由参数

fxFlex 规则不适用于 mat-sidenav

角度 5 - 如何从 dom 中删除动态添加的组件

如何固定 mat-toolbar 和 mat-sidenav 并且在 mat-sidenav-content 内只有一个滚动条?

jQuery动态更改引导折叠组件内的progress-bar“aria-valuenow”属性和CSS“width”属性的值

动态更改内容