• [Angular] Why should we using Protal


    Origianl article

    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>
  • 相关阅读:
    [Swift]LeetCode895. 最大频率栈 | Maximum Frequency Stack
    [Swift]LeetCode894. 所有可能的满二叉树 | All Possible Full Binary Trees
    [Swift]LeetCode893. 特殊等价字符串组 | Groups of Special-Equivalent Strings
    [Swift]LeetCode892. 三维形体的表面积 | Surface Area of 3D Shapes
    [Swift]LeetCode891. 子序列宽度之和 | Sum of Subsequence Widths
    [Swift]LeetCode890. 查找和替换模式 | Find and Replace Pattern
    find missing conjunction, why?
    sh 脚本报错
    What's mean ORA-25191?
    饼状图
  • 原文地址:https://www.cnblogs.com/Answer1215/p/9285900.html
Copyright © 2020-2023  润新知