通过您的 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<T>) 或组件(ComponentType<T>) 传递给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 供你玩耍。
希望这会有所帮助!