dorgan
Currently the Phoenix.LiveView.JS struct is defined as an @opaque type, meaning it’s fields are internal api not to be relied upon.
I have the following use case I’d like to support to keep working in elixir land as much as possible:
- I have a toast notifications implementation in JS
- The liveview process sends events to show or dismiss toasts, and the client handles via
window.addEventListener, so I’m able to fully control the toasts api with liveview - I need to add additional behavior to the toasts: the toasts can have an action, so when the user clicks them some JS will run
- This can be achieved normally with the
phx-clickattribute set on the toast, and it works if the value is a simple event name string and the value is inphx-value, but it gets ugly to implement when the value should be the result of many chains ofJS.*functions
This is trivial to solve if I do this:
%{
actions: %{
"Foo" => Jason.encode!(JS.dispatch("foo", detail: %{some: "arg"}).opts)
}
}
However, the opts field is private, so I can’t just rely on this approach always working.
Is there any chance of this field being public in the near future, or for the JS struct to be json serializable via some phoenix public api?
Trending in Proposals: Ideas
Other Trending Topics
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve.
They are GUI (Emerge) and State management (S...
New
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #blog-post
- #phoenix_html
- #iex
- #graphql
- #ai
- #genstage
- #elixirconf-us
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex











First 10 of 16 Posts
LostKobrakai
I’ve build custom executions of js commands just be sending the string and using
LiveSocket.execJS: JavaScript interoperability — Phoenix LiveView v1.2.5dorgan
That works of course, the problem is that liveview doesn’t control the rendering of the toast element(I’m using toasts here as an example, but the same applies to other “js sprinkles” that render content with js in a container with
phx-update="ignore"). For that approach to work I’d need again to serialize the js command, which apparently isn’t possible with the current public apis.LostKobrakai
I’m not sure I understand the issue then. You can call
execJSwhenever you need.dorgan
execJSis JS code; the point is to not have to drop to JS code to run a JS command. It also relies in me having access to the serialized JS command, which I don’t.Normally, with templates, you can do this:
However, in my case the rendering of the “button” happens in JS code, outside of Liveview’s reach. This means that I need to write a JS hook or some other code to run that command or something equivalent. In other words, I need to write specialized JS code for each interaction I want to add to that dynamically created element and I want to avoid that.
To avoid that, I can tell the js rendering code to render the attributes I pass as the payload for the “show toast” event, meaning I can leverage
phx-clickfor this and not have to worry about anything else in the JS code, letting me write elixir code like:Which would render the following in JS land:
The problem is that there’s no public api to serialize the
JSstruct here, so it will throw an error. I can still do that withJason.encode!(JS.some_command(...).opts)but because the:optsis private api I have no guarantee this won’t break in upcoming liveview releases, nor that it will keep being JSON.The whole point of the exercise is to not have to resort to hooks or any other JS code whatsoever.
phx-clickand similar are already stable public api, and they rely on some sort of serialised JS command as the value, so I think it wouldn’t be a stretch to expose ato_stringorencodefunction toPhoenix.LiveView.JSto support this use case or any other use case that escapes templates yet can still avoid the burden of having to drop down to writing js hook/bindings/whatever.LostKobrakai
Oh. I thought it would just implement
to_stringalready. Thinking about this some more I might have usedJason.encode!(command.ops)in the past as well. It would certainly be useful to be able to use the serialization phoenix uses.rhcarvalho
This is a reasonable proposal, maybe it needs to go to the Proposals section on the forum for better visibility?
There is still no official way to serialize JS commands on the server side.
Here’s an example use case of exec’ing server-pushed JS commands: Animating list items with LiveView streams - #6 by rhcarvalho.
What I found is that, in testing, JS commands are serialized using
Phoenix.HTML.Safe.to_iodata(), but that HTML-encodes the input, making it not directly usable on client JavaScript (e.g."becomes".https://github.com/phoenixframework/phoenix_live_view/blob/f5ee3f88b91cd51f0711c07e207ec992cc64b3f3/test/phoenix_live_view/js_test.exs#L1136-L1141
https://github.com/phoenixframework/phoenix_live_view/blob/f5ee3f88b91cd51f0711c07e207ec992cc64b3f3/test/phoenix_live_view/js_test.exs#L67-L70
What works is what is described in the OP, JSON-encoding the
:opsfield of the struct, with no forward-compatibility guarantee.steffend
LiveView only exposes APIs for working with the encoded JS, so using Phoenix.HTML.Safe for serialization is the best approach, I’d say.
rhcarvalho
Thanks for the answer!
Phoenix.HTML.Safetreats the input as if it would be placed in a generic HTML context. When using JS commands within a HEEx template, the engine is smart enough to escape the resulting string according to context, most often within an HTML attribute (this is the common case that works great).In terms of APIs for encoding JS commands on the server, are we limited to those two options?
The OP wanted to encode the value on the server side and send it down to the client via
push_eventthat encodes the value in transit as JSON and becomes a JavaScript event, with JS payload on the client. If encoded withPhoenix.HTML.Safe, JS code could need to manually unescape HTML entities like"→".LostKobrakai
I’d love if there would be a
JS.to_json(_iodata)function. I know phoenix cannot implement any json library protocol given it doesn’t depend on a specific json library. But having such a function would allow anyone to implement the protocol for the library they’re using.steffend
Ugh, sorry. I thought we’d handle unescaping in execJS, but I was mistaken. So yeah, I don’t think there’s anything that prevents us from implementing
String.Chars.https://github.com/phoenixframework/phoenix_live_view/pull/4044
You’ll still need to do write it like the following, but you can implement Jason.Encoder by just delegating to
to_stringand then omit the extra to_string call.