[Angular] Why should we using Protal
Protal from Angular CDK, is a way to create dynammic component.
Consider an example of Page toolbar.
This toolbar should show differently content when route changed.
For example, when we changed the router to 'Contacts page':
There should be a Mat-icon (Plus icon) showing on the toolbar.
Tradional Approach:
Listening on the route changes and using *ngIf or *ngSwitch to switching template.
This approach works fine for small application, but for large application, switching logic becomes harder to control.
Also 'app-page-actions component' should have no idea 'currently it is in Home page or it is in Contacts page'. In other words, those logic should not be hard code inside the component.
Portal Approach:
To make portal works, it reuqests a host container and a component or a template.
The host container is the placeholder DOM element, this is the place <app-page-acitions> should be rendered into.
<!-- shell.component.html--> <div fxFlex="50px" id="page-actions-container"> <!-- content will be placed dynamically via Cdk Portal --> </div>
(full code see the bottom)
App-page-actions itself can be just a placeholder which implements content projection:
<ng-template cdk-portal> <ng-content></ng-content> </ng-template>
So now its upto each page to tell App-page-actions, whether there is any action or what action to be rendered. For example, the contacts page could be:
<app-page-actions> <button type="button" class="toolbar-btn" mat-icon-button (click)="onSave()"> <mat-icon>add</mat-icon> </button> </app-page-actions>
To connnect portal host and portal component or template:
import { Component, OnInit, AfterViewInit, ComponentFactoryResolver, Injector, ViewContainerRef, ApplicationRef, ViewChild, OnDestroy } from '@angular/core'; import { DomPortalHost, TemplatePortal, PortalHost, CdkPortal } from '@angular/cdk/portal'; @Component({ selector: 'app-page-actions', template: ` <ng-template cdk-portal> <ng-content></ng-content> </ng-template> `, styles: [] }) export class PageActionsComponent implements OnInit, AfterViewInit, OnDestroy { private portalHost: PortalHost; @ViewChild(CdkPortal) portal; constructor( private componentFactoryResolver: ComponentFactoryResolver, private injector: Injector, private appRef: ApplicationRef ) {} ngOnInit() {} // We have to wait until DOM is created, so that we can access the DOM element ngAfterViewInit(): void { // Create a portalHost from a DOM element this.portalHost = new DomPortalHost( document.querySelector('#page-actions-container'), // get the placeholder element by Id this.componentFactoryResolver, // you have to do this this.appRef, // you have to do this this.injector // you have to do this :( ); // Attach portal to host this.portalHost.attach(this.portal); } // Always remember to clean up the dynamic component ngOnDestroy(): void { this.portalHost.detach(); } }
That's all.
Well, how to do is not so important, but rather why we should do it.
To summry, portal is good for
1. Better component articulture.
We know that, app-page-actions should have no idea which page current is. It should only receive the component from content projection and display it. This can be done in Content projection.
2. We have a placeholder where the app-page-actions should be rendered for different pages.
This helps Angular to understand our applications better. (better for AOT)
--------------------------
Layout:
<!-- shell.component.html--> <mat-sidenav-container class="sidenav-container"> <mat-sidenav #drawer class="sidenav" fixedInViewport="true" [attr.role]="(isHandset$ | async) ? 'dialog' : 'navigation'" [mode]="(isHandset$ | async) ? 'over' : 'side'" [opened]="!(isHandset$ | async)"> <mat-toolbar color="primary">Menu</mat-toolbar> <mat-nav-list> <a mat-list-item routerLink="/home">Home</a> <a mat-list-item routerLink="/contacts">Contacts</a> </mat-nav-list> </mat-sidenav> <mat-sidenav-content> <mat-toolbar color="primary"> <button type="button" aria-label="Toggle sidenav" mat-icon-button (click)="drawer.toggle()" *ngIf="isHandset$ | async"> <mat-icon aria-label="Side nav toggle icon">menu</mat-icon> </button> <span>Application Title</span> <div fxFlex fxFill></div> <div fxFlex="50px" id="page-actions-container"> <!-- content will be placed dynamically via Cdk Portal --> </div> </mat-toolbar> <div class="app-container"> <ng-content></ng-content> </div> </mat-sidenav-content> </mat-sidenav-container>
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析