Eventos

Los Plugsters no llaman a los métodos de otros. Publican eventos y reaccionan a eventos. El framework ofrece una API pequeña y deliberada para esto: cada evento que un Plugster puede emitir se declara en la clase misma, y las suscripciones se cablean ya sea en HTML (por defecto) o en JavaScript (para Plugsters que cobran existencia en tiempo de ejecución). Esta página cubre las tres piezas — declarar, despachar y suscribirse.

Declarar un evento #

Cada evento que un Plugster publica debe declararse con registerEventSignature. La declaración es un método cuyo nombre es el nombre del evento; dentro, la clase le entrega al framework una referencia a ese nombre, más un slot para la forma del payload del evento y un callback opcional. La declaración en sí no despacha nada — es el contrato.

        
            export class ExchangeRatesPlugster extends Plugster {

                // ... constructor y afterInit ...

                // Declara el evento 'currencyChanged' que el Plugster puede publicar
                currencyChanged(data, callback) {
                    this.registerEventSignature(this.currencyChanged.name, data, callback);
                }

            }
        
    

La convención es mecánica y vale la pena internalizarla:

  • El nombre del método es el nombre del evento. El evento del ejemplo de arriba es currencyChanged; nada más.
  • Siempre referencia el nombre como this.currencyChanged.name — la propia propiedad .name de la función — en lugar de escribir hardcodeado el string 'currencyChanged'. Renombrar el método se vuelve un refactor seguro; los strings hardcodeados derivan.
  • Los parámetros data y callback parecen argumentos de método ordinarios porque lo son — reciben el payload y un callback opcional de ack cuando el evento luego se consume vía la API de suscripción explícita.

Despachar un evento #

Para disparar un evento declarado, llama a this.dispatchEvent(name, payload). El payload es un objeto plano cuya forma debería coincidir con lo que la firma declarada documenta:

        
            export class ExchangeRatesPlugster extends Plugster {

                afterInit() {
                    let self = this;
                    self._.currencySelector.on('change', function () {
                        self._notifyCurrencyChange(this.value);
                    });
                }

                _notifyCurrencyChange(currencyCode) {
                    this.dispatchEvent(this.currencyChanged.name, { currencyCode });
                }

                currencyChanged(data, callback) {
                    this.registerEventSignature(this.currencyChanged.name, data, callback);
                }

            }
        
    

Dos notas prácticas sobre dispatchEvent:

  • Es seguro despachar desde afterInit() directamente — por ejemplo, para señalar "estoy listo" justo después del enlazado. Si los suscriptores aún no se han registrado, el evento se encola en Plugster.eventQueue y se entrega tan pronto como Plugster.plug() termine de cablear.
  • Despachar un evento cuya firma no se ha declarado es una violación silenciosa de contrato. Los suscriptores reciben nada útil y el framework no ofrece advertencia. Siempre declara primero.

Suscribirse — declarado en HTML (por defecto) #

Cuando tanto el publicador como el suscriptor existen en el HTML de la página al cargar — el caso típico — las suscripciones se cablean declarativamente desde la vista del suscriptor usando data-on-{publicador}-{evento}. El valor del atributo es el nombre del método handler en la clase suscriptora.

        
            <div data-controller-name="RatesListPlugster"
                 data-on-exchangeratesplugster-currencychanged="handleCurrencyChange">
                <ul data-outlet-id="ratesList"></ul>
            </div>
        
    
        
            export class RatesListPlugster extends Plugster {

                afterInit() {
                    // Sin código de suscripción aquí — el cableado vive en el atributo HTML.
                }

                handleCurrencyChange(data) {
                    let currencyCode = data.args.currencyCode;
                    this._refreshList(currencyCode);
                }

            }
        
    

En el momento en que el último Plugster de la página llama a Plugster.plug(), el framework recorre el registro, lee cada atributo data-on-* en cada vista registrada, y conecta el handler de cada suscriptor al evento correspondiente del publicador. El método handler DEBE existir en la clase suscriptora — si no, el framework lanza excepción en el momento del cableado. Esto es por diseño: un typo en el atributo o el nombre del método debería fallar ruidosamente.

El payload llega envuelto: el handler recibe data, y el objeto payload va bajo data.args. La envoltura es consistente entre ambos modos de suscripción.

Suscribirse — explícito (listenTo) #

Algunos Plugsters no existen en el HTML de la página al momento de carga — se crean programáticamente en respuesta a acciones del usuario, salida de fábricas o cambios de ruta. Para estos, las suscripciones declaradas en HTML no son posibles (el suscriptor no sabe en tiempo de autoría qué publicadores dinámicos existirán). El framework provee listenTo para este caso.

        
            export class DashboardPlugster extends Plugster {

                async _mountVisualization(spec, data) {
                    let runtime = await this._visualizationFactory.mount({
                        mount: this._.vizContainer[0],
                        vizSpec: spec,
                        runResult: data
                    });

                    // Suscribirse explícitamente a eventos publicados por la instancia runtime
                    this.listenTo(runtime.instance, runtime.instance.runParamsChanged);
                    this.listenTo(runtime.instance, runtime.instance.dimensionClicked);

                    return runtime;
                }

                // Router único para todas las suscripciones explícitas
                onNewMessage(publisherName, eventName, args) {
                    if (eventName === 'dimensionClicked') {
                        this._applyFilter(args.field, args.value);
                    }
                    if (eventName === 'runParamsChanged') {
                        this._reloadData(args);
                    }
                }

            }
        
    

Dos cosas hacen a este modo diferente de las suscripciones declaradas en HTML:

  • Los argumentos de listenTo son referencias, no strings. Pasa la instancia publicadora y una referencia a su método declarador de evento — el mismo método que habrías envuelto con registerEventSignature del lado publicador. El framework lee method.name internamente para emparejar contra el evento despachado.
  • La entrega va por onNewMessage. A diferencia de las suscripciones declaradas en HTML donde cada evento tiene su propio handler con nombre, las suscripciones explícitas se canalizan en un solo método onNewMessage(publisherName, eventName, args) del suscriptor. El patrón router mostrado arriba (ramificando sobre eventName) es la forma canónica.

Cuando un Plugster dinámico se remueve y reemplaza — común en patrones de dashboard / fábrica — llama a Plugster.unplug en la instancia vieja antes de montar la nueva. unplug remueve automáticamente las suscripciones explícitas que configuraste contra ella. Ver Ciclo de vida.

Elegir un modo #

  • El publicador y el suscriptor ambos existen en el HTML al momento de carga → declarado en HTML. Casi todo.
  • Cualquiera de los lados se crea en tiempo de ejecución vía JavaScript → explícito (listenTo). Dashboards, fábricas, montaje impulsado por rutas.
  • Nunca mezcles los dos para la misma relación de suscripción. Elige uno y quédate con él.

Sobre la cola de eventos #

El cableado de Plugster es deliberado sobre el ordenamiento. Un suscriptor solo está listo para recibir eventos después de que ambos extremos hayan sido registrados con Plugster.plug(). Para manejar la ventana incómoda entre — donde un Plugster ya está activo y despachando mientras otro aún termina init() — el framework aparca eventos pendientes en Plugster.eventQueue. A medida que cada plug() subsiguiente se completa, el framework re-chequea la cola y entrega cualquier cosa que el recién llegado debía recibir.

En la práctica no interactúas con la cola directamente. Saber que existe es suficiente para explicar por qué despachar desde afterInit() es seguro incluso cuando no todos los Plugsters de la página se han registrado aún.

Content