Hi I have the same issue that this person had here, but I do not think the solution is really relevant to what I am looking for and I don’t understand it that well without context. I tried to ask for clarification there, but I was not able to receive a response.
I just simply need a way to pass a variable from the client-side JS to the server-rendered views/templates.
I did look into this, but that honestly just appears to be above and beyond what I need and I’m wondering if there is a simpler way.
I am using Phoenix LiveView with Surface UI. I am new to elixir/phoenix. Thank you for any help.
It depends on how complex your use-case is. If you need the Javascript variable to go through any logic on the server, then checkout LiveView Javascript Hooks, which work very well and you can create GenServer-like LiveControllers that react to client events. Most of the boilerplate is already setup in the included LiveView javascript.
However, if you simply want to display the values then the server can render an empty div with a specific id (tag) and the client-side JS can then use getElemementByTagName and set the value setting the innerHTML on the returned element.
Thank you for the quick response. I have read through those JS interoperability docs in the past several times and did not see a clear way to achieve what I am setting out to do.
I don’t want to render the value directly into the HTML, I want to simply use the value to create dynamic HTML by using the value in a surface UI conditional
It’s definitely not that complex of a use case, it is literally just one JS variable value that stores a boolean that I want to pass to the view on the server so I can render some HTML conditionally
If you want a more precise/descriptive answer, it helps to ask a more precise/descriptive answer.
For example, is the boolean set by a user interaction such as clicking a toggle or checkbox in the UI? If so, then you may be able to use the LiveView’s built-in phx-click binding.
Also, if “render some HTML conditionally” means just showing/hiding some HTML, then take a look at LiveView’s built-in JS commands which would let you show and hide elements on the page without even needing to make a round trip to the server.
Without more details, push_event would be the most flexible basic route to go as @benwilson512 noted above.
<div phx-hook="Testing">This will trigger registered hook Testing Hooks.Testing in your app.js file</div>
in your app.js file
Hooks.Testing = {
mounted() {
/* this will trigger a push_event with some data beeing sent to your backend - you will need to define a handle for that in your live view .ex file - it will be triggered when page is loaded and the dom is "mounted" */
this.pushEvent("test_event",
{data: "Some data" })
}
}
in your live view .ex file
def handle_info(%{test_event: %{data: your_data}}, socket) do
IO.inspect("this is the data sent from client: #{inspect your_data}")
{:noreply, socket}
end
hopefully this hacky example gives you some help. But as someone above pointed out, your question is very broad and after reading your responses I’m still not sure what it is you need help with.
Thank you for the response, but that is the opposite of what I want to do. I already have an event listener registered on the window in app.js that fires upon a push_event() call, I just don’t know how to transmit the variable that I defined in that JS handler there to Elixir/Phoenix for further processing
Thanks for the response and sorry, I was assuming it was a simple question with a simple answer and I also thought the forum question I linked to would provide enough context here.
The boolean in the JS event handler is not set by user interaction. It is derived from window.matchMedia().matches.
I don’t really know enough about those JS commands you mentioned but it looks like it could possibly work for my needs.
I already have push_event in the phoenix/elixir code causing the JS event to fire in app.js, what I need is the “push_event” type of functionality within the handler in app.js to send something as a response to Elixir after the JS handler runs.
I took a look through some of those JS commands and they don’t look like they would work. I need more specific functionality than that. I do indeed need an element to be conditionally rendered, but I need to preserve all it’s child elements when it’s not rendered, like this:
I appreciate it, but I don’t think it would work because there is no event that needs to be binded to any of those elements I provided in that HTML snippet
With push_event/3 you can also send events directly to hooks. So, if I understood you use-case correctly, here’s what you could do:
your hook:
{
mounted() {
this.handleEvent("event-from-server", data_from_server => {
// do something client-side with data_from_server, compute response_from_client
this.pushEvent("response-from-client", response_from_client);
})
}
}
in your LV:
def handle_event("some-event", _params, socket) do
socket = push_event(socket, "event-from-server", data_from_server)
{:noreply, socket}
end
def handle_event("response-from-client", response_from_client, socket) do
# do something with the response from the client
{:noreply, socket}
end
I have not used them for any of my previous JS event listeners and there is some additional setup with it, drobban mentioned this above Hooks.Testing = { mount: { window.my_special_thing = this } } but it looks like they are more so meant to be binded to elements. The event in question here does not fire due to any element interactions, I just call push_event() within def mount(). It looks like I may have to try and make hooks work though because there does not appear to be any other way.
thank you, I’m going to try this. Looks like you can’t do something similar without hooks by just using a plain window.addEventListener() and pushing data back to the server in the handler
That’s because pushing on mount can’t work. Mount runs before the page has rendered, which means it’s before any Js has had a chance to run. If you push out an event when mount returns it’s very likely the JS handlers won’t exist yet.
Instead the more reliable way is to have a JS hook, then have the hook tell the live view process it’s ready, and then the live view process can send it info.