libs/suspense/types/suspenseable-event-driven.ts
Definición de la clase abstracta que permite implementar un componente de tipo Suspenseable en su modo de
operación en base a un control de estados de tipo evento. Sigue la misma lógica que el modo "normal" (usando función setup()
), sin embargo
el estado listo o error se determina en base al valor de las correspondientes variables de estado.
Se desconoce en que momento, y tras la ejecución de que operación/operaciones, el componente estará listo para ser desplegado. Por ello se disponibilizan
los BehaviorSubject setupReady
y hasError
para que el componente pueda notificar cuando se encuentre en estado listo o error.
Se forzará la implementación de la función setup()
para este subtipo de componentes Suspenseable, de paso se
provee la función defaultEventDrivenSetup()
que permite implementar el control de estados de forma sencilla.
Pick
Propiedades |
Métodos |
constructor()
|
initialized |
Valor por defecto : false
|
Variable de control para saber si ya está lista la inicializacion del componente. Pudiera no utilizarse. |
setupReady |
Tipo : BehaviorSubject<boolean>
|
Valor por defecto : new BehaviorSubject(false)
|
Indica que el componente está listo para ser desplegado. Como es un BehaviorSubject, admite un valor inicial. |
defaultDisplay |
Tipo : string
|
Valor por defecto : 'inherit'
|
Heredado desde
SuspenseableRenderer
|
Definido en
SuspenseableRenderer:92
|
elementRef |
Tipo : ElementRef
|
Valor por defecto : inject(ElementRef)
|
Heredado desde
SuspenseableRenderer
|
Definido en
SuspenseableRenderer:89
|
platformId |
Tipo : Object
|
Valor por defecto : inject(PLATFORM_ID)
|
Heredado desde
SuspenseableRenderer
|
Definido en
SuspenseableRenderer:90
|
renderer |
Tipo : Renderer2
|
Valor por defecto : inject(Renderer2)
|
Heredado desde
SuspenseableRenderer
|
Definido en
SuspenseableRenderer:88
|
init |
init()
|
Devuelve :
void
|
Abstract setup |
setup()
|
Devuelve :
ObservableInput<any>
|
renderComponenteReady |
renderComponenteReady()
|
Heredado desde
SuspenseableRenderer
|
Definido en
SuspenseableRenderer:110
|
En caso que la operación sea desde dentro de un browser, se cambia el valor de
Devuelve :
void
|
import { inject } from "@angular/core";
import { BehaviorSubject, combineLatest, filter, map, ObservableInput, takeUntil, tap } from "rxjs";
import { ISuspenseable, SuspenseableRenderer, SUSPENSE_LOG } from "./types";
let suspenseConsole: Console;
/**
* Definición de la clase abstracta que permite implementar un componente de tipo Suspenseable en su modo de
* operación en base a un control de estados de tipo evento. Sigue la misma lógica que el modo "normal" (usando función `setup()`), sin embargo
* el estado listo o error se determina en base al valor de las correspondientes variables de estado.
* Se desconoce en que momento, y tras la ejecución de que operación/operaciones, el componente estará listo para ser desplegado. Por ello se disponibilizan
* los BehaviorSubject `setupReady` y `hasError` para que el componente pueda notificar cuando se encuentre en estado listo o error.
*
* * setupReady: Indicará que el componente está listo para ser desplegado
* * hasError: Indicará que el componente no se pudo cargar debido a un estado de error
*
* Se forzará la implementación de la función `setup()` para este subtipo de componentes Suspenseable, de paso se
* provee la función `defaultEventDrivenSetup()` que permite implementar el control de estados de forma sencilla.
*/
export abstract class SuspenseableEventDriven extends SuspenseableRenderer implements Pick<ISuspenseable, 'setup'> {
/**
* Wrapper para función de registro de mensajes de log.
* Mostrará los mensajes solamente si la aplicación ha configurado el provider DEBUG_SUSPENSE explícitamente en true.
*/
suspenseConsole = inject(SUSPENSE_LOG);
constructor() {
super();
suspenseConsole = this.suspenseConsole;
}
/**
* Variable de control para saber si ya está lista la inicializacion del componente.
* Pudiera no utilizarse.
*/
initialized = false;
/**
* Indica que el componente está listo para ser desplegado.
* Como es un BehaviorSubject, admite un valor inicial.
*/
setupReady : BehaviorSubject<boolean> = new BehaviorSubject(false);
/**
* Indica que el componente llegó a un estado de error, por lo que no debe ser desplegado.
* Como es un BehaviorSubject, admite un valor inicial.
*/
hasError : BehaviorSubject<boolean> = new BehaviorSubject(false);
/**
* Implementación de la función `setup()` que permite aprovechar el control de estados de forma sencilla.
* La idea es que dentro del mismo componente se seteen los valores para `setupReady` o para `hasError`. Llamando esta función desde `setup()` se
* realiza el monitoreo de estas variables, asegurando que el componente efectivamente se despliegue cuando esté listo, o bien se informe el estado
* de error.
* @param response { [key: string]: unknown } Objeto JSON (estructura a priori desconocida) que será devuelto como respuesta
* @param useInit { boolean } Indica si se debe llamar a la función `init()` antes de resolver la función `setup()`
* @returns { ObservableInput<any> } Observable que se resolverá cuando el componente esté listo para ser desplegado. La respuesta es del mismo tipo que la de setup()
*/
defaultEventDrivenSetup(response: { [key: string]: unknown }, useInit = false): ObservableInput<any> {
suspenseConsole.log('[defaultEventDrivenSetup] setup()');
if(useInit) {
this.init();
}
/**
* Se suscribe inicialmente a setupReady.
* Escuchará el observable mientras no este listo Ó tenga error, por ello se combina el observable.
* Se filtra para que escuche alguna de las 2 respuestas y en caso de error lanzará una excepción (new Error).
* Responde un observable con el objeto de respuesta indicado.
*/
return this.setupReady.pipe(
takeUntil(
combineLatest([this.setupReady, this.hasError]).pipe(
filter(([ isReady, hasError ]) => isReady || hasError),
tap(
([ isReady, hasError ]) => {
suspenseConsole.log('[defaultEventDrivenSetup] isReady, hasError: ', isReady, hasError );
if (hasError) throw new Error('[defaultEventDrivenSetup] No se pudo cargar el componente');
}
)
)
),
map(() => (response))
);
}
init() {
/**
* Función de inicialización del componente, quizás sea pasar lo del ngInit a otra función.
* Típicamente seteará la variable initialized a true.
*/
throw new Error('init() no implementado');
}
abstract setup(): ObservableInput<any>;
}