Liam_or

Liam_or

I’m having difficulty understanding where JS can belong in a liveview app. I understand that JS hooks belong in app.js, but there is only one app.js which means every hook you write, will be bundled and sent to the client. For universal hooks that would be ok, but for component specific js, if a client never navigates to a view which uses it, it would lead to unused JS being sent to the client. It would be lovely to be able to write JS in the heex, and it appears to be possible, but is it safe to write component specific JS within tags in heex?

First 10 of 20 Posts Switch mode

frankdugan3

frankdugan3

You certainly could create components that have in JS them, but it does come with a can of worms:

  • Need to remember to load script components when needed by some component on a page
  • If JS is put in the component itself, it will be duplicated
  • A lot of JS needs to be a hook, and as far as I know, there’s not a way to load hooks dynamically

I think it’s important to note that, aside from hooks, there’s no component-level scope for JS in LiveView, so whatever scripts you are doing inside a heex template is going to need to be assigned to some globally accessible variables.

What I have done for really big JS deps (like GraphiQL) is to create a separate bundle and create a specific root layout for the pages that needed it.

I have also done a few “page-level” components for things like dark mode JS and some other little things like that, and those are loaded only once in the applicable root layouts.

frankdugan3

frankdugan3

I forget to say this, but Chris mentioned at the end of the 1.0 blog that collocated JS hooks are an upcoming planned feature for LiveView. So, this could soon be a resolved issue.

D4no0

D4no0

Very nice!

I always advised people to avoid using hooks unnecessarily because of this global nature. This might be the feature that will open the gates for UI component libraries wide open.

LostKobrakai

LostKobrakai

I don’t think colocation will change anything about that. When LV encounters a phx-hook attribute it needs to know what to do with it. That’s a completely separate concern to how you organize source files.

For particularly heavy dependencies I’d probably do a hook, which pulls in the dependency as an async import at the moment. That way the hook definition still works fine and can stay slim and you can actually model the fact that the dependency has some loading associated.

D4no0

D4no0

Since this is a internal implementation detail, it might as well be possible that the core team will find a better way to achieve the same result.

I am more interested in hooks as interaction with UI parts that cannot work without JS, for example rendering a map with maplibre.js, without the JS initialization part this is not possible.

LostKobrakai

LostKobrakai

The most I seem them doing is hoist the “async portion” to the hook level instead of it being part of a hooks implementation. But once they encounter a phx-hook they need to do something and I don’t see much alternative to either “start loading a hook” or “start using a hook”. Eventually that attribute needs to result in code being available to execute. In either way somewhere there needs to be the knowledge of which hooks exist and if they’re to be loaded where to do so. JS already has means for doing that.

I’m not sure I understand what you mean. You want to use maplibre, which seems to be a given. So you have three options.

  1. Bundle maplibre with all your other code.
    single, but large, app.js
  2. Do not bundle maplibre, but have static knowledge about which page to include the js in.
    additional dynamic js linked in head
    caches independently to app.js
    requires full page – including root layout – navigation for LV though
  3. Load the js dynamically when encountering the need for it to be included at runtime.
    async import of additional js file
Liam_or

Liam_or OP

Thanks everyone for chiming in : )
I have some questions if you don’t mind.

  1. @LostKobrakai your point regarding:

Blockquote 3. Load the js dynamically when encountering the need for it to be included at runtime.
async import of additional js file.

Would you mind giving an example of how you’d implement this?

  1. @frankdugan3 Is it possible to load a different ‘app.js’ from the root depending on the session? I.e user and user type?
LostKobrakai

LostKobrakai

For now you’d have something like this

{
  mounted() {
    this.loading = true;
    import("heavy_dep")
      .then(dep => {
        this.loading = false;
        # start using dep
        # assign stuff to this for use in other hook callbacks
        # …
      })
  },
  …
}

Depending on the bundler used this should generate additional js files, which are only loaded once that import function is called.

frankdugan3

frankdugan3

Proabably, but at the very least you’d need to ensure whenever the session changed, you did a full page refresh. TBH, I think @LostKobrakai has the proper suggestion. Dynamically loading the heavy deps of hooks is the most straightforward way to go, instead of splitting up the hooks into top-level bundles.

You could even do some preloading of those deps so there would be no loading latency when the hook is mounted, but the page would still load without waiting for the heavy modules:

<script rel="modulepreload" type="module" src="dynamic-module.js"></script>
Lucassifoni

Lucassifoni

I’m using “dynamic hooks” in an app where liveviews are not part of the main build but loaded at runtime and where I would not want those scripts to be part of the main JS bundle.

def hooks() do
   %{
     act_on_coordinates: ~JS"""
      h.el.addEventListener("mouseenter", (e) => {
         // handle your event
      });
     """
    }
end

...

~H"""
<.register_hook :for={{key, content} <- @hooks} bind="h" name={key} script={content} />
"""

The JS sigil has formatting-on-save built in, I’m working to add syntax coloring. But I allowed myself to do this in my project because I’ve read that co-located hooks will drop at some point. So when it’s the case, I’ll remove this and migrate to the new standard way.
Note that I’m not importing dependencies in those dynamic hooks, they are mostly vanilla JS 1 to 10-liners.

Phoenix and Liveview are quite permissive and provide a lot of foundations to build on - use this to your advantage but know the pitfalls if you deviate too much.

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
jonnycharles
I’m in search of an Elixir library that offers PDF generation capabilities similar to Ruby’s Prawn. While there have been discussions abo...
New
spammy
I’m looking to build a personal workflow to quickly deploy web applications written in elixir/phoenix, for local consumption (ie not on t...
New
dli
Before I dive in myself, did anyone successfully sprinkle Hologram into their existing LiveView app? Looking for hints regarding: Addi...
New
roeland
Kia ora, We have been using elixir-google-api to connect to Google Drive. However, with the updates to Tesla due to CVEs this is now bro...
New
bottlenecked
Hi all, I wanted to ask how the community is dealing with post-release steps. Today we have Ecto migrations, which make sure that the db...
New
rahultumpala
Hello, I have an Elixir backend that implements a custom protocol over TCP. I want to load test the backend and assess the performance o...
New

Other Trending Topics Top

JesseHerrick
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
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
ausimian
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
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New

We're in Beta

About us Mission Statement