Componentes angulares con contenido en línea

Cuando crea nuevos componentes cuando desarrolla en Angular, se esfuerza por crearlos de tal manera que puedan reutilizarse muchas veces. Al igual que en el desarrollo de software, un programador se esfuerza por hacer que su código sea lo más reutilizable posible. Al mismo tiempo, quiero tener componentes que sean flexibles, pero no demasiado complejos.



Como regla general, los componentes deben hacerse lo más estúpidos posible para que sea más fácil de mantener y probar. Pero al mismo tiempo, son personalizables y flexibles para que pueda reutilizarlos muchas veces, y no lidiar con componentes de clonación que son similares visual y funcionalmente, pero que aún difieren en algo. O bien, no agregue un montón de parámetros a los componentes, en función de los cuales habilitar o deshabilitar el comportamiento de cualquier componente, sino que de hecho se utilizará en 1 de cada 100 casos.

Crear un submarino, que debería hundirse hasta el fondo del mar azul sin riesgo para la vida de la tripulación, y luego modificarlo antes de volar por el cielo azul no es una tarea fácil.

Un componente angular es la unidad más pequeña de la interfaz de usuario que debe realizar una tarea simple y nada más. Crear componentes para todas las ocasiones es una mala práctica. Por lo tanto, debe encontrar un compromiso entre flexibilidad y simplicidad.

Suponga que desea crear un componente que consista en algún tipo de envoltorio, dentro del cual hay un encabezado y algún tipo de interruptor, y algo de contenido debajo de ellos. Algo como esto:



Todo el componente, que se denominará widget, se describe en verde. El interruptor de encabezado se resalta en rojo y el contenido se resalta en azul. Angular es un marco increíblemente poderoso; crear un componente similar al de la imagen es una tarea muy trivial para Angular.

Pero, ¿qué sucede si este componente se hace más flexible, por ejemplo, para que el contenido cambie según los parámetros del componente del widget?

Puede resolver la representación externa del contenido en función del parámetro del widget, por ejemplo, utilizando la directiva estructural ngIf . Puede cambiar entre diferentes vistas. Y si hay demasiados envíos, puede usar otra directiva estructural ngSwitch .

¿Y si las diferentes vistas responden de manera diferente a las acciones del usuario? Entonces el código del componente también tendrá un montón de declaraciones condicionales. Pero el código, en el que hay muchas declaraciones condicionales, con el tiempo se vuelve difícil de mantener, corregir errores o ampliar la funcionalidad ...

Puede crear un componente de widget para que reciba cualquier contenido, pero al mismo tiempo arroja los valores seleccionados del panel de filtro en los componentes del área de contenido. Y también me gustaría poder reenviar parámetros desde el componente principal del widget al contenido, sin afectar el componente del widget en sí. Para resolver este problema, puede usar la maravillosa directiva ngTemplateOutlet , que le permitirá "resolver" la representación visual del componente. Para obtener información adicional, puede consultar la documentación , aunque en mi opinión no se muestra todo su poder allí.

En este caso, el componente del widget se puede representar de la siguiente manera:

// widget.component.ts … private widgetContentTemplate: TemplateRef<any>; //      @ContentChild(WidgetContentTemplateDirective, { read: TemplateRef }) set WidgetContentTemplate(value: TemplateRef<any>) { this.widgetContentTemplate = value; } get WidgetContentTemplate(): TemplateRef<any> { return this.widgetContentTemplate; } … 

 <!-- widget.component.html --><div class="widget__content"> <!--      ,      context.   ,     --> <ng-container *ngTemplateOutlet="WidgetContentTemplate || DefaultWidgetContentTemplate; context: { post: SelectedPost }"> </ng-container> </div> <ng-template #DefaultWidgetContentTemplate> <div>[    ]</div> </ng-template> 

La directiva ngTemplateOutlet acepta un objeto TemplateRef que se lee en el componente usando @ContentChild. Para hacer esto, necesita crear una directiva simple. Aquí está su código:

 // widget-content-template.directive.ts import { Directive, TemplateRef } from '@angular/core'; @Directive({ selector: '[appWidgetContentTemplate]' }) export class WidgetContentTemplateDirective { get Template(): TemplateRef<any> { return this.template; } constructor(private readonly template: TemplateRef<any>) {} } 

Los parámetros del conmutador de widget se pueden pasar al contenido utilizando el objeto de contexto. Para que el componente lea correctamente los parámetros del contexto, es necesario en el componente padre, en nuestro caso es AppComponent, no olvide describirlos de la siguiente manera:

 <!-- app.component.html --> <app-widget-component Title="  (  )"> <!-- let-post="post" ,         --> <ng-template appWidgetContentTemplate let-post="post"> <app-post-view [Post]="post"></app-post-view> </ng-template> </app-widget-component> <br> <br> <app-widget-component Title="  (  )"> <!--  let-post="post"     ,  ShowPostInfo  AppComponent --> <ng-template appWidgetContentTemplate let-post="post"> <app-post-view [Post]="post" [ShowPostInfo]="ShowPostInfo"></app-post-view> </ng-template> </app-widget-component> 

Por lo tanto, tenemos un componente de widget que acepta y muestra cualquier contenido, mientras que el componente de widget en sí no tiene que saber nada sobre el contenido. Simplemente reenvía las opciones de cambio. Leerlos o no es una cuestión de contenido.



La versión completa del código se puede ver aquí .

Source: https://habr.com/ru/post/478886/


All Articles