tmk

tmk

Hey, just getting started with Phoenix + LiveView, coming from Django and React. I’m a bit overwhelmed by the various ways of approaching templating, so apologies in advance if there’s an obvious solution here.

What I’m looking for is the ability to hook into a LiveView render() call, such that I can calculate derived assigns to pass to the template (i.e. so that I can modify assigns). I’d like to do this to keep my LiveView state small and shaped like an entity store, and derive template usage in the render method, as in React.

I understand that function components support this, when leveraging the ~H sigil, but I’m looking to keep my template separate from the LiveView. As an example, the auto generated core components:

  def flash(assigns) do
    assigns = assign_new(assigns, :id, fn -> "flash-#{assigns.kind}" end)

    ~H"""
    ... template here

What I’m looking to do:

  @impl true
  def render(assigns) do
    assigns = derive_view_model(assigns)

    actual_render("template.html.heex", assigns)
  end

As I understand, LiveView automatically generates a render() function for the template of the same module name. What I’d like to do is understand how to reproduce that method so that I can customize it, or otherwise hook into it.

My current approach is to hook into assign() calls manually - e.g. socket |> delete_entity(:alerts, alert) |> derive_view_model() . However even in my first LiveView page I have forgotten to do this several times, so I’m hoping there’s a better way.

Showing Posts 1 to 8

krishandley

krishandley

I have a function called set_state in pretty much every LiveView which basically does this, yes you have to manually call it where needed. But it’s pretty flexible, you could have different ones depending on how expensive they are, and only call them when needed. But there is no automatic way of doing it.

Zurga

Zurga

You can use attach_hook/4 to run a function after handle_params, handle_event and handle_info. This will ensure that these derived assigns are set all the time. Use on_mount for hooking into mount

tmk

tmk OP

Reading the docs, I see that hooks are run before the handle_foo handlers:

Lifecycle hooks take place immediately before a given lifecycle callback is invoked on the LiveView

This would not work for my use case, since I’d be looking to update the derived assigns after the handle_foo methods have run. Eg this code:

  @impl true
  def handle_info({:stock_patch, stock}, socket) do
    {:noreply, socket |> patch_entity(:stocks, stock) |> derive_view_model()}
  end

I’d need to be able to run a hook post-handler, with the updated assigns, so that the view model may be calculated correctly.

If I’m missing something, please let me know!

tmk

tmk OP

Thanks for the reply - I’m glad I’m not the only one. It stands out to me that it is possible to accomplish this by only leveraging function components (including for render()) - for consistency, it’d be great to be able to update assigns for template file rendering, too.

krasenyp

krasenyp

What you can do, and that’s similar to Emacs’ advice system, is to use the socket as a container for assign hooks but you need to redefine assign. Something like the following:

# The second argument is when the hook is called.
# Could be before, after, around and so on.
advice_assign(socket, :after, &derive_assigns/1)

defp derive_assigns(%{assigns: %{...}} = socket) do
  # Use Phoenix.Component.assign/2,3 here
end

defp assign(%{private: %{assign_hooks: hooks}} = socket) do
  # Process the hooks accordingly.
end
Zurga

Zurga

You could do something similar to what Surface does with quoted_mount. Just move the super call to run before you do the state handling:

defmacro before_compile(env) do
quoted_mount(env)
end

defp quoted_mount(env) do
defaults = env.module |> Surface.API.get_defaults() |> Macro.escape()

if Module.defines?(env.module, {:mount, 3}) do
  quote do
    defoverridable mount: 3

    def mount(params, session, socket) do
      socket =
        socket
        |> Surface.init()
        |> assign(unquote(defaults))

      super(params, session, socket)
    end
  end
else
  quote do
    def mount(_params, _session, socket) do
      {:ok,
       socket
       |> Surface.init()
       |> assign(unquote(defaults))}
    end
  end
end

end

sorry for the formatting, I am on mobile now

rhcarvalho

rhcarvalho

In LiveView “assigns” are state stored in the server.

The bare building blocks you have are a module and functions (thankfully only two concepts).

The LiveView “as a whole” (~page) is a module, all logic goes into functions.

The framework will call callbacks that you implement on your module: mount, handle_*, etc.

The way I approach derived assigns is computing them as a function of their dependencies (often a private function defp inside the module and called as appropriate).

You can as well inline your templates in your LiveView module, def render(assigns), and then you can do as you wanted/saw in function components as in the CoreComponents module.

Note you need to be careful to follow the rules with regards to change tracking: common pitfalls.

tmk

tmk OP

Thanks for all the suggestions. Coming from a React background, I feel most comfortable with @rhcarvalho ‘s approach to inline templates. In React, I’d map liveview assigns to component state , with the render() method in each supporting derived state.

— All posts loaded —

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
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
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
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

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews