Call Function in Template Only Once

I have a little random greeter function in my template: generate_greeting(), which returns a string like, “Hello!”, “Bonjour!”, or “Konichiwa!”, etc.

If I put this function directly in the template <%= generate_greeting() %>, then it displays when the page first loads, but then changes when the WebSocket mounts.

The docs seem to suggest this shouldn’t happen, but it does.

I tried using a socket assign in mount, but of course that didn’t work, but that’s expected. So, I have the solution which checks if the socket is already connected to generate the greeting, like so:

if connected?(socket) do
      {:ok,
      socket
       |> assign(:greeting, generate_greeting())
    }
else
      {:ok,
        socket
        |> assign(:greeting, "")
      }
end

The problem with this is that there is a sudden flash of content when the socket loads, which is jarring.

What is the optimal solution here? I’d like to call a function on the first page render only. It seems like this should be quite easy, and the documentation seemed to imply that the function shouldn’t be called twice directly from the template, but it is.

Any ideas?
Cheers,
R

I’d suggest keeping a fixed seed around and base the randomness on said seed.

Indeed, your if connected?(socket) do should prevent the double load effect. Can you look at your network tab and see if something else is triggering a page refresh?

If your connected?(socket) check is being used correctly it shouldn’t display at all when the page first loads, can you show more code?

Sorry, to clarify, this solution does “work”, but there is a flash of content after the initial page render. I am looking for a solution where the function is executed before the first page render/mount, and then left alone after that.

Ah, that is a bit trickier. The cheating answer is to use phx-update="ignore" on the DOM node, but this is a bit of a hack in this case IMHO. It’ll also introduce issues if you want to live_redirect from other pages because then it never does a static render at all.

Fundamentally the challenge is that there is hidden state (the random seed) that you aren’t tracking, and this makes it difficult to arrive at the same conclusion for the static vs dynamic render. Your options to track state between the static and dynamic renders are:

  1. URL Params
  2. Cookies
  3. hidden Form value

(1) is in some sense the easiest because can be done entirely in Elixir. The downside is that it makes the greeting feel less magical.

(2) and (3) require a bit of JS I think which is lame.

Thanks for your reply and ideas! I don’t think it’s worth including the technical debt of those hacks for something that’s just for a bit of cheer on the page, though. I really thought this would be some simple solution. Surprisingly, it seems like phx-update="ignore" doesn’t work as expected, but even if it did, I’ve got live navigation all over the place and wouldn’t want that interfered with.

I’m a little surprised by this, it seems like a bit of a deficiency. I’ve needed this functionality lots of times before in other frameworks, so I bet I’m not the only person who needs this. Perhaps I will file a feature request…

This is not a simple problem at all given there’s no state on the server to be shared between static and non static renders. A mount will always try to clean up any inconsistent markup on the client. Imo the much better solution here is to make sure you render consistent values if you require consistent values rather than poking holes into liveview to accept inconsistent markup when most of it’s job is maintaining consistancy.

The problem is that you’re asking for something that is antithetical to the premise of the framework. The premise of the framework is that the backend controls 100% of the page state. Here, you’re asking it to not do that for part of the page.

I’d be curious what other frameworks do, can you elaborate on how they handle the state management in this case? Maybe one of those approaches will inspire an idea here.