Saltar al contenido

Comunicación entre espacios de nombres con LWC

Hola, tenemos la respuesta a lo que estabas buscando, deslízate y la verás más abajo.

Solución:

Comentario de OP:

Ninguno de estos enfoques funciona en espacios de nombres porque el objeto de ventana para cada espacio de nombres es una instancia diferente, como parte del Servicio de casillero.

Vea la respuesta del OP que explica cómo lo único en común entre los dos objetos de ventana es la adición de detectores de eventos y el envío de eventos DOM; Actualmente es posible (incluso en la versión Summer ’20) hacer un túnel de eventos DOM a través del límite del espacio de nombres.

adicional:

Problema:

LWC está importando diferentes recursos (apex / static / modules / wired, etc.) de diferentes maneras. La importación de módulos es idéntica a las importaciones estándar de ES6. Sin embargo, cuando importa un recurso estático, en realidad está envuelto dentro de una función con nombre y se ejecuta cuando lo invocamos en loadScript.

Podrá cargarlo como recurso estático pero no se compilará en tiempo de ejecución (debido a la declaración de exportación) durante la importación debido a lo cual no se creará ninguna función con nombre (requirió mucha depuración) y loadScript irá directamente a la declaración de captura. Para una comprensión más sencilla, intente guardar la siguiente función en cualquier componente de LWC:

staticResourceLoad() 
    const mydouble = (num) => 
        return num * 2;
    ;
    export  mydouble ;

Esto ni siquiera compilará (Error de análisis: es posible que ‘importar’ y ‘exportar’ solo aparezcan en el nivel superior). Por lo tanto, cuando desee cargar un módulo como un recurso estático, debe hacer los siguientes cambios en el archivo JS; lo más importante es definir una variable en la ventana para que tenga acceso a esa variable después de que se cargue el archivo.


Solución:

No puede usar el módulo como está en un recurso estático. Debe implementarlo usando la función de auto-invocación y definir la variable global (como pubsub) y asignar todas las funciones necesarias (como samePageRef) y devolverlo para que pueda asignarse a diferentes var si es necesario.

A continuación se muestra el pubsub que se puede usar para recursos estáticos:

(function() 
    window.pubsub = ;
    /**
 * A basic pub-sub mechanism for sibling component communication
 *
 * TODO - adopt standard flexipage sibling communication mechanism when it's available.
 */

    let events = ;

    /**
 * Confirm that two page references have the same attributes
 * @param object pageRef1 - The first page reference
 * @param object pageRef2 - The second page reference
 */
    pubsub.samePageRef = (pageRef1, pageRef2) => 
        const obj1 = pageRef1.attributes;
        const obj2 = pageRef2.attributes;
        return Object.keys(obj1).concat(Object.keys(obj2)).every((key) => 
            return obj1[key] === obj2[key];
        );
    ;

    /**
 * Registers a callback for an event
 * @param string eventName - Name of the event to listen for.
 * @param function callback - Function to invoke when said event is fired.
 * @param object thisArg - The value to be passed as the this parameter to the callback function is bound.
 */
    pubsub.registerListener = (eventName, callback, thisArg) => 
        // Checking that the listener has a pageRef property. We rely on that property for filtering purpose in fireEvent()
        if (!thisArg.pageRef) 
            throw new Error(
                'pubsub listeners need a "@wire(CurrentPageReference) pageRef" property'
            );
        

        if (!events[eventName]) 
            events[eventName] = [];
        
        const duplicate = events[eventName].find((listener) => 
            return listener.callback === callback && listener.thisArg === thisArg;
        );
        if (!duplicate) 
            events[eventName].push( callback, thisArg );
        
    ;

    /**
 * Unregisters a callback for an event
 * @param string eventName - Name of the event to unregister from.
 * @param function callback - Function to unregister.
 * @param object thisArg - The value to be passed as the this parameter to the callback function is bound.
 */
    pubsub.unregisterListener = (eventName, callback, thisArg) => 
        if (events[eventName])  listener.thisArg !== thisArg
            );
        
    ;

    /**
 * Unregisters all event listeners bound to an object.
 * @param object thisArg - All the callbacks bound to this object will be removed.
 */
    pubsub.unregisterAllListeners = (thisArg) => 
        Object.keys(events).forEach((eventName) => 
            events[eventName] = events[eventName].filter(
                (listener) => listener.thisArg !== thisArg
            );
        );
    ;

    /**
 * Fires an event to listeners.
 * @param object pageRef - Reference of the page that represents the event scope.
 * @param string eventName - Name of the event to fire.
 * @param * payload - Payload of the event to fire.
 */
    pubsub.fireEvent = (pageRef, eventName, payload) => 
        if (events[eventName]) 
            const listeners = events[eventName];
            listeners.forEach((listener) => 
                if (samePageRef(pageRef, listener.thisArg.pageRef)) 
                    try 
                        listener.callback.call(listener.thisArg, payload);
                     catch (error) 
                        // fail silently
                    
                
            );
        
    ;
    return pubsub;
)();

Para importar:

import pubsubMod from '@salesforce/resourceUrl/pubsub';

Para init:

loadScript(this, pubsubMod)
            .then(() => 
                this._pubsubInit = true;
                // Tentatively register, in case the page reference is already resolved
                pubsub.registerListener('contactSelected', this.handleContactSelected, this);
            )
            .catch((error) => 
                this.dispatchEvent(
                    new ShowToastEvent(
                        title: 'Error loading pubsub',
                        message: error.message,
                        variant: 'error'
                    )
                );
            );

Tenga en cuenta que pubsubMod se refiere a resource y pubsub se refiere a global var window.pubsub

código de muestra para su manipulación:

handleContactSelected(val) 
    console.log('val => ', val);


OPCION 2:

Como necesita una solución temporal hasta que el servicio de mensajes de la interfaz de usuario esté disponible, creo que puede usar los controladores de cambio de variable de JavaScript en el ámbito de la ventana como una solución alternativa. Creé un enlace de patio de juegos para comprender. Como será en window alcance, creo que funcionará para espacios de nombres cruzados (aunque no lo probé)

A continuación se muestra el código de muestra:

mytargetcomp.js:

connectedCallback() 
    window.myvar = 
        aInternal: 10,
        aListener: function(val) ,
        set a(val) 
            this.aInternal = val;
            this.aListener(val);
        ,
        get a() 
            return this.aInternal;
        ,
        registerListener: function(listener) 
            this.aListener = listener;
        
    ;
    window.myvar.registerListener(function(val) 
        console.log('Someone changed the value of myvar to ', val);
    );

corens__mysourcecomp.js:

refreshData() 
    window.myvar.a = 'some data ' + new Date().getTime();

Aquí es cuando desea pasar datos de corens__mytargetcomp para mycomp.

Puede implementar e invocar refreshData desde cualquier componente y cualquier componente que implemente, el manejador de cambios debería poder manejar nuevos datos.

Después de un montón de líos llegué a la siguiente conclusión: la única forma que puedo encontrar para permitir que las LWC de diferentes paquetes / espacios de nombres se comuniquen es usando eventos DOM.

Debe tenerse en cuenta lo siguiente:

  1. Según la respuesta de @ salesforce-sas, no puede usar un “módulo” completo como recurso estático ya que la funcionalidad loadScript es incompatible con este enfoque.
  2. No es posible usar el código pubsub tal cual (sin la declaración de exportación) ya que cada paquete tiene su propia instancia del objeto de ventana; debido a esto, el mapa de “eventos” dentro de window.pubsub no se comparte, sino que es exclusivo del paquete. Esto significa que la función fireEvent no funciona de manera adecuada ya que el mapa solo incluye oyentes registrados dentro del espacio de nombres.
  3. El uso del evento DOM de Salesforce CustomEvent funciona, aunque pasar la referencia de la página va un poco en contra de la documentación que dice que “la interfaz CustomEvent no impone requisitos de tipo o estructura en la propiedad de detalle. Sin embargo, es importante enviar solo datos primitivosCreo que esto no es un problema en este caso, ya que solo el código pubsub ve la parte “ref de página” de la propiedad de detalle (y no intenta actualizarla).

El código pubsub de trabajo completo de mi PoC (también tengo un paquete que contiene este código más un LWC que lo usa, y una organización en la que creé un segundo LWC usando este script) es el siguiente:

/**
 * A basic pub-sub mechanism for sibling component communication that works
 * across package boundaries.
 */
if (!window.pubsub) 
    window.pubsub = (function () 
        const _events = ;
        const _registered = ;

        const samePageRef = (pageRef1, pageRef2) => 
            const obj1 = pageRef1.attributes;
            const obj2 = pageRef2.attributes;
            return Object.keys(obj1)
                .concat(Object.keys(obj2))
                .every(key => 
                    return obj1[key] === obj2[key];
                );
        ;

        /**
         * Registers a callback for an event.
         *
         * @param string eventName - Name of the event to listen for.
         * @param function callback - Function to invoke when said event is fired.
         * @param object thisArg - The value to be passed as the this parameter to the callback function is bound.
         */
        const registerListener = (eventName, callback, thisArg) => 
            // Checking that the listener has a pageRef property. We rely on that property for filtering purpose in fireEvent()
            if (!thisArg.pageRef) 
                throw new Error(
                    'pubsub listeners need a "@wire(CurrentPageReference) pageRef" property',
                );
            

            if (!_events[eventName]) 
                _events[eventName] = [];
            

            const foundDuplicate = _events[eventName].find(listener => 
                return listener.callback === callback && listener.thisArg === thisArg;
            );

            if (!foundDuplicate) 
                _events[eventName].push(callback, thisArg);
            

            if (!_registered[eventName]) 
                window.addEventListener(eventName, function (event) 
                    if (event.type === eventName) 
                        const listeners = _events[eventName];

                        listeners.forEach(listener => 
                            if (samePageRef(event.detail.pageRef, listener.thisArg.pageRef)) 
                                try 
                                    listener.callback.call(listener.thisArg, event.detail.payload);
                                 catch (error) 
                                    // fail silently
                                
                            
                        );
                    
                );

                _registered[eventName] = true;
            
        ;

        /**
         * Unregisters a callback for an event.
         *
         * @param string eventName - Name of the event to unregister from.
         * @param function callback - Function to unregister.
         * @param object thisArg - The value to be passed as the this parameter to the callback function is bound.
         */
        const unregisterListener = (eventName, callback, thisArg) => 
            if (_events[eventName])  listener.thisArg !== thisArg
                );
            
        ;

        /**
         * Unregisters all event listeners bound to an object.
         *
         * @param object thisArg - All the callbacks bound to this object will be removed.
         */
        const unregisterAllListeners = thisArg => 
            Object.keys(_events).forEach(eventName => 
                _events[eventName] = _events[eventName].filter(
                    listener => listener.thisArg !== thisArg,
                );
            );
        ;

        /**
         * Fires an event to listeners.
         *
         * @param object pageRef - Reference of the page that represents the event scope.
         * @param string eventName - Name of the event to fire.
         * @param * payload - Payload of the event to fire.
         */
        const fireEvent = (pageRef, eventName, payload) => 
            const event = new CustomEvent(eventName, detail: pageRef: pageRef, payload: payload);

            window.dispatchEvent(event);
        ;

        return Object.freeze(
            registerListener: registerListener,
            unregisterListener: unregisterListener,
            unregisterAllListeners: unregisterAllListeners,
            fireEvent: fireEvent
        );
    )();

Esto no está completamente pulido: no maneja limpiamente la anulación del registro de los eventos DOM subyacentes, pero hace lo suficiente para minimizar el procesamiento falso de eventos después de la anulación del registro.

Sin embargo, lo que es más importante, al usar esto, puedo comunicarme con éxito entre componentes en diferentes paquetes / espacios de nombres.

ACTUALIZAR:

Si bien los medios oficiales de comunicación a través del DOM, es decir, el Servicio de mensajes Lightning, se publicaron en el lanzamiento de Summer ’20, parece que esto no se puede usar con Comunidades Lightning y los canales de mensajes no se pueden incluir en paquetes de App Exchange (lo que debemos hacer ).

También hemos descubierto un problema menor causado por el uso de oyentes agregados dinámicamente (lo que va en contra de la recomendación de Salesforce): cambiar las páginas relámpago puede dejar inesperadamente a los oyentes escuchando y los componentes aún activos. Algo más en lo que trabajar.

Acuérdate de que puedes optar por la opción de parafrasear si encontraste tu obstáculo en el momento concreto.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *