Extending Components with Abstract Classes — Angular

Adrian Pedro Zelada Torrez
MyCodeBad
Published in
4 min readSep 6, 2019

--

Ciudad de La Paz , Montículo — Bolivia

A medida que vamos avanzando en un proyecto iremos creando varios components, al comienzo estos components eran simples y fáciles de mantener y entender, con el paso del tiempo estos components van adquiriendo mas funcionalidades y se van haciendo cada vez mas difíciles de mantener.

Por Ejemplo.

Tenemos un component ViewerAccordionComponent que se encarga de la visualización de contenido, es un component que al hacer click en el título este te mostrará el contenido oculto.

ViewerAccordionComponent

Este Component en un manera tradicional de enfoque quizas su código seria este:

viewer-accordion.component.html

<div class="card">
<div class="card-header" (click)="clickItem()">
<b>{{data.title}}</b>
</div>
<div class="card-body" *ngIf="showContent">
<p class="card-text">{{data.paragraph}}</p>
</div>
</div>

viewer-accordion.component.ts

export class ViewerAccordionComponent implements OnInit {
@Input() data: any = {};
showContent: boolean; constructor() {
}
ngOnInit() {} clickItem() {
this.showContent = !this.showContent;
}
}

Si analizamos este component es completamente funcional y cumple su objetivo. Ya terminado esté component lo vamos integrando en distintos módulos, todo va bien hasta que el modulo A necesita que el contenido solo se muestre cuando se de click en un button que se debe adicionar en parte derecha del titulo, entonces comenzaremos a agregar ese feature, pero el modulo B no necesita esa funcionalidad el requiere la anterior y por ultimo el modulo C quiere que cuando el mouse pase por el titulo se muestre la descripción y de esta manera irán llegando nuevos requerimientos, los cuales se irán completando pero al finalizar tendremos un component con N lineas de código.

En ocaciones algunas de las funcionalidades que vamos agregando a el component estarán dirigidas a un solo Modulo X, con esto romperíamos la integridad de dicho Component que solo debería cumplir su objetivo.

El principal problema de este Component es que no esta abierto a extensiones, es por eso que cuando agregar una nueva funcionalidad o modificar un flujo de este Component tenemos que modificar directamente el código de este component lo que podría causar conflictos.

Una de las soluciones será que un Component del Modulo X extienda las funcionalidades del Component ViewerAccordionComponent y solo así agregar o modificar funcionalidad que solo le compete a el Modulo X.

A continuación convertiremos el component anterior en uno completamente extendible también implementaremos las funcionalidades que anteriormente nos pidieron agregar.

Para esto debemos de Abstraer las funcionalidades del ViewerAccordionComponent en una Clase que nos servirá como lineamiento para que tengan el flujo de trabajo y las funcionalidades de dicho Component.

Interface

Comenzaremos creando una Interface ViewerInterface está interface nos ayudara a definir una estructura y alcance que tendrán nuestros components.

Class Abstract

Una vez ya definida nuestra interface la implementaremos en nuestra clase abstracta ViewerAccordionAbstract la cual tiene parte de la lógica del component Base ViewerAccordionComponent

Default Service

El servicio ViewerAccordionDefaultService extenderá los métodos de ViewerAccordionAbstract y deberá implementar los 2 métodos abstractos de dicha Clase con la lógica que se vea conveniente.

ViewerAccordionComponent (Extendible)

Este será el component base del cual extenderemos sus funcionalidades, este tendrá un comportamiento por default, haciendo click en el título de este component se muestra en la parte inferior una descripción.

Como veremos la mayor parte de la Lógica se la ejecuta en los providers lo que nos dará la habilidad de extender el component.

Ahora que ya tenemos el component Base ViewerAccordionComponent extenderemos sus funcionalidades.

ViewerSecretComponent

El component solo mostrará la información cuando el password proporcionado por el usuario sea igual a la clave de acceso del component ViewerSecretComponent, si es un valor distinto se deberá mostrar un mensaje de error.

viewer-secret.component.ts

El component ViewerSecretComponent en su declaración tenemos estas lineas de código.

viewProviders: [
{
provide: ViewerAccordionAbstract,
useExisting: forwardRef(() => ViewerSecretComponent)
}
]

La propiedad viewProviders hará que los components que se encuentren adentro del Padre, puedan recibir un provider que este seria una instancia del component Padre, para comprender mas sobre los viewProviders les dejo un enlace donde Asin Hussain explica a detalle la Dependency Injection & Providers

El component se extiende de ViewAccordionAbstract que es un class que tiene el comportamiento por defecto que tiene el Component Base pero al implementar nos instruye que debemos de implementar algunos métodos:

  • validOpen: esta función se ejecuta déspues de realizar un click en el título del component ViewerAccordionComponent, este método se encargará de validar el valor proporcionado con la clave del component, si es igual se mostrará la información.
  • deferClick: Si el método validOpen retorna un valor falso en la comparación el método deferClick sera ejecutado mostrando un error.

Como el component ViewerSecretComponent en su maquetación contiene a el component ViewerAccordionComponent entonces este ultimo component recibirá en su constructor una clase abstracta ViewerAccordionAbstract que contendrá la lógica ya extendida en el component ViewerSecretComponent esto gracias a la propiedad viewProvider que ya explicamos anteriormente.

viewer-accordion.component.ts

constructor( @Optional() private cpnService: ViewerAccordionAbstract,
private defaultControlValueService: ViewerAccordionDefaultService) {
.......
}

De está manera el component ViewerSecretComponent tendrá las funcionalidades de ViewerAccordionComponent, de esta manera el component ViewerSecretComponent podrá extender o modificar el flujo sin necesidad de cambiar el código del component base ViewerAccordionComponent.

Puedes encontrar el código fuente de este ejemplo aquí.

Bonus….. Les dejo también otros ejemplos de similares pero con formularios aquí

Gracias su tiempo.

--

--

Adrian Pedro Zelada Torrez
MyCodeBad

Hay una fuerza motriz más poderosa que el vapor, la electricidad y la energía atómica: la voluntad. Albert Einstein.