Patrón de portal de Angular: por qué necesita un componente raíz en la interfaz de usuario de Taiga

Mi colega Roman anunció recientemente el lanzamiento de nuestra nueva biblioteca de componentes para Angular Taiga UI. Los Getting Started instrucciones dicen que la aplicación necesita ser envuelto en una tui-root



. Averigüemos qué hace, averigüemos cómo y por qué usamos los portales y qué son en general.





¿Qué es un portal?

Imagina un componente select



. Tiene un menú desplegable con opciones para elegir. Si lo almacena en el mismo lugar en el DOM que el componente en sí, puede encontrarse con varios problemas. Los elementos posteriores pueden aparecer en la parte superior y el contenedor puede cortar el contenido:





z-index



, Z . 100, 10000, 10001. , overflow: hidden



. ?





. z-index



. «». Root- Taiga UI . :





<tui-scroll-controls></tui-scroll-controls>
<tui-portal-host>
    <div class="content"><ng-content></ng-content></div>
    <tui-dialog-host></tui-dialog-host>
    <ng-content select="tuiOverDialogs"></ng-content>
    <tui-notifications-host></tui-notifications-host>
    <ng-content select="tuiOverNotifications"></ng-content>
</tui-portal-host>
<ng-content select="tuiOverPortals"></ng-content>
<tui-hints-host></tui-hints-host>
<ng-content select="tuiOverHints"></ng-content>
      
      



tui-dialog-host



, tui-portal-host



— . -. . Taiga UI . . :





@Injectable({
   providedIn: 'root',
})
export class TuiPortalService {
   private host: TuiPortalHostComponent;

   add<C>(componentFactory: ComponentFactory<C>, injector: Injector): ComponentRef<C> {
       return this.host.addComponentChild(componentFactory, injector);
   }

   remove<C>({hostView}: ComponentRef<C>) {
       hostView.destroy();
   }

   addTemplate<C>(templateRef: TemplateRef<C>, context?: C): EmbeddedViewRef<C> {
       return this.host.addTemplateChild(templateRef, context);
   }

   removeTemplate<C>(viewRef: EmbeddedViewRef<C>) {
       viewRef.destroy();
   }
}
      
      



. , , — . , , .





, - . , «», .





, . :





  1. . Material .





  2. , . .





  3. .





. , . , requestAnimationFrame



. — , . . , . , . , . .









— . Obscured. .





, . , .





:





<section
   *ngFor="let item of dialogs$ | async"
   polymorpheus-outlet
   tuiFocusTrap
   tuiOverscroll="all"
   class="dialog"
   role="dialog"
   aria-modal="true"
   [attr.aria-labelledby]="item.id"
   [content]="item.component"
   [context]="item"
   [@tuiParentAnimation]
></section>
<div class="overlay"></div>
      
      



, ngFor



, . , . , . , .





, . Taiga UI — . - iOS Android. . , .





Observable



. , — . . . , , DI- POLYMORPHEUS_CONTEXT



. content



observer



. complete



, , next



. , , . :





const DIALOG = new PolymorpheusComponent(MyDialogComponent);
const DEFAULT_OPTIONS: MyDialogOptions = {
   label: '',
   size: 's',
};

@Injectable({
   providedIn: 'root',
})
export class MyDialogService extends AbstractTuiDialogService<MyDialogOptions> {
   protected readonly component = DIALOG;
   protected readonly defaultOptions = DEFAULT_OPTIONS;
}
      
      



.





, Taiga UI, ng-polymorpheus . , , , .





tuiFocusTrap



. , DOM, , . - — :





@HostListener('window:focusin.silent', ['$event.target'])
onFocusIn(node: Node) {
   if (containsOrAfter(this.elementRef.nativeElement, node)) {
       return;
   }

   const focusable = getClosestKeyboardFocusable(
       this.elementRef.nativeElement,
       false,
       this.elementRef.nativeElement,
   );

   if (focusable) {
       focusable.focus();
   }
}
      
      



root-. root , tuiOverscroll



. CSS- overscroll-behavior. . , , . , , .





: tui-root?

, . , root-. tui-scroll-controls



. , . <ng-content select="tuiOverDialogs"></ng-content>



. Taiga UI. , .





root event manager plugin` DI. — . , TuiRootModule



BrowserModule



, . : — .





, root-. Taiga UI open-source, «» npm. StackBlitz-. , , !








All Articles