Generic events between LiveView and the Javascript side

How do I send a unsolicited and generic event from the javascript side to the root LiveView, to be handled in handle_event/3? Do I have to use a Hook object attached to something, or is there a simpler way?

I was trying to do:

liveSocket.root.pushEvent(...)

But don’t know what to put in the parenthesis.

Also, How do I do the reverse, sending an generic event from the liveview to window or window.document object?

Hi @derek-zhou - have you read through the JS interop docs at JavaScript interoperability — Phoenix LiveView v0.15.4?

You will need hooks attached to an element in your liveview. The docs referenced above give an example of a hook setting up a listener for window events.

I did. I can attach a hook to an element and achieve back and forth event. However, it feels awkward, because what I am doing has nothing to do with the element, or UI at all; I was just passing some data back and forth.

I was hoping since the Phoenix.LiveView is built on top of a Phoenix.Channel, I can use it to pass my sideband signals using the existing LiveView, without causing any harm to the LiveView.

I’m using Vue as UI render in my LiveView project. It will call Vue.createApp to render whole page every time when the html elements mounted or updated. (It’s not the best way but fast enough and simple).

function renderApp() {
    let fields = {}
    // Vue read data from html element
    document.querySelectorAll('.cg-v-model').forEach((p) => {
        Object.assign(fields, JSON.parse(p.getAttribute('vdatajson')))
    })
    const component = {
        data() {
            return fields
        },
        methods: {
            push: (event, params) => {
                this.pushEvent(event, params)
            }
        }
    }
    const app = Vue.createApp(component)
    app.mount('#' + this.el.id)
}

Hooks.createApp = {
    mounted() {
        renderApp.call(this)
    },
    updated() {
        renderApp.call(this)
    }
}

Then I can push event directly from vue components:

<el-button type="primary" @click="push('reset', resetForm.pass)">RESET</el-button>

That‘s correct, but Liveview uses a custom handler on the client side. It uses the same infrastructure, but is more specialized than plain channels. So it doesn‘t necessarily do what plain channels do anymore.

1 Like

With a hook I can push_event from the server side and catch in the client side, and the reverse is also true. So I can achieve my goal with just a little detour. However, If I have a push_event followed by a push_redirect, the push_redirect will cull the earlier push_event, as documented in the doc at the very end. So my question is:

Do we have a guaranty that push_event themselves are handled in order? How about the other direction, from client to the server?

To get around the problem of push_redirect culling push_event, I am doing 2 push_events, the latter will trigger something like this:

        this.handleEvent("redirect", ({href}) => {
            let url = new URL(href, location.href);
            liveSocket.historyRedirect(url.toString(), "push");
        });

So far with limited testing it seems to work well.

I’ve packaged a little utility library for interacting with the client JS context from LV - GitHub - Miserlou/live_json: LiveJSON - LiveView for JSON

To send data to the server:

import { sendData } from 'live_json';
sendData('your_handler', your_data); 

To send data to the client:

socket
|> LiveJson.assign("foo", "bar")