Usando APP_INITIALIZER en Angular

Aprende cómo usar APP_INITIALIZER para antes que arranque tu aplicación de Angular puedas ejecutar algunas validaciones.
Angular

Supongamos el siguiente caso:

Tenemos una aplicación de un dashboard que, debe corroborar que existe la token de acceso, cumple con los requisitos y a nivel back-end se debe validar, así no podrán acceder personas creando la token de forma manual.

Bueno, de esta forma muchos que somos novatos pensaríamos, “bueno hago un servicio que lo llamo en el constructor de un componente global, hago que tenga un setInterval y que se ejecute cada 5 segundos para no saturar el back-end…”

Bueno, esto en teoría funciona, pero, pensemos ahora que ponemos una alerta a nuestros clientes cuando no se puede procesar X cosa, por lo que, si por ejemplo la token expira en el tiempo a nivel back-end, entonces, estos 5 segundos y las validaciones al ya haber iniciado Angular puede que salgan errores o funcionamientos extraños, entonces, ¿por qué no antes que se haya iniciado Angular y todo hacer esta validación?.

Este ejemplo puede sonar extraño, pero vamos a usar este ejemplo, además de, usar un segundo ejemplo anexo para realizar dos implementaciones en el mismo APP_INITIALIZER que ya diré qué es…

Este segundo ejemplo es que tenemos que nuestra aplicación tiene distintos estados, staging, development y production.

Entonces, podemos hacer un archivo de configuración que, guarde en staging la URL del API que usamos para este caso, el de development en otro y en production otro…Entendiendo así, que, usaremos estos dos proveedores.

APP_INITIALIZER es entonces quien nos permitirá ejecutar todo esto antes que Angular inicie para que así, Angular tenga sus dos condicionales correctamente, que el Token se valide y que se llamen las configuraciones.

Creando los servicios a ejecutar

Lo primero es tener nuestros dos servicios listos, primero el de configuración:

// ... imports
@Injectable()
export class AppConfig {
load() {
 // Código que carga las configuraciones
  }
}

Y ahora el de la token:

// Imports...
@Injectable()
export class TokenProvider {
  constructor() {}
  
  load(){
    // Código que checa la token de acceso
  }
}

Como podrás darte cuenta no incluí código esto porque cada uno tendrá su manera, pero, ¿se puede cargar el HttpClient y todo lo demás?, ¡Claro!.

Antes de irnos a la siguiente sección, el apartado de load() requiere retornar una promesa, por lo que te recomiendo hacer un wrapper del código como:

return new Promise((resolve, reject) => { /* tu código */ resolve(true); });

Integración con APP_INITIALIZER

Ya que tenemos estos dos servicios viene la parte de la integración:

Lo primero será abrir nuestro app.module e incluir el import del APP_INITIALIZER:

import { APP_INITIALIZER } from '@angular/core';

Si tienen algo más que llamen del core de Angular, pues solo integrarlo para evitar múltiples líneas de import del mismo lugar.

Después, hay que crear una función que se exporte y que reciba como parámetros las dependencias (o servicios(?)) que va a llamar:

export function servicesOnRun(config: AppConfig, token: TokenProvider) {
  return () => config.load().then(() => token.load());
}

Debido a que, debemos pensar siempre en AOT en vez de JIT (les dejo de tarea investigar qué son en caso de que no sepan o quizá después haga otra publicación para ello), y ya que el APP_INITIALIZER requiere de una promesa en el return, obtenemos las promesas en orden basados en el return que asignamos en la funciónload() con el wrapper de la promesa que previamente comentamos.

Cabe destacar que cada parámetro será un servicio más a llamar y deberán concatenarse cada load() para ir llamando uno por uno.

Importar al app.module

Ahora, ya que tenemos casi todo al 90%, nos falta solo importar:

// ... otros imports
import { APP_INITIALIZER } from '@angular/core';
import { TokenProvider } from './token.service';
import { AppConfig } from './app.config';
// ... otros imports
// La función exportada para ejecutar los proveedores antes que arranque angular
export function servicesOnRun(config: AppConfig, token: TokenProvider) {
  return () => config.load().then(() => token.load());
}
@NgModule({
  declarations: [
    // ...todas nuestras declaraciones
  ],
  imports: [
    // ... todos nuestros imports
  ],
  providers: [
    // ... otros providers
    AppConfig,
    TokenProvider,
    {
      provide: APP_INITIALIZER,
      useFactory: servicesOnRun,
      multi: true,
      deps: [AppConfig, TokenProvider]
    },
 // ... otros providers 
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Aquí en este código podemos ver algo así como todo el app.module pero como ya explicamos arriba, solo nos basaremos para finalizar este tutorial en la sección de providers .

Podemos observar que tenemos el siguiente código:

    AppConfig,
    TokenProvider,
    {
      provide: APP_INITIALIZER,
      useFactory: servicesOnRun,
      multi: true,
      deps: [AppConfig, TokenProvider]
    },

Básicamente:

Primero importamos nuestros servicios, después asignamos un parámetro como objeto con, el proveedor asignado como el APP_INITIALIZER, que usará la función servicesOnRun, que será múltiple y las dependencias, que básicamente es algo así como “el/los parámetro/s a pasar a la función de servicesOnRun” como explicación simple.

Con esto, podemos observar que, si agregan un console.log al load podrán verlo antes que se indique que Angular fue inicializado en modo desarrollo cuando estén en este “estado de la aplicación” y podrán corroborar el funcionamiento.

¿Cuál es tu reacción?
+1
1
+1
1
+1
0
+1
1
+1
3
Total
0
Shares
Publicación anterior

Usando concatMap, mergeMap y forkJoin en Angular para peticiones HTTP

Siguiente Publicación

Creando un Proxy en Angular para conectarte a un API local y de producción

Publicaciones Relacionadas