Using liveview functions in another liveview

Let’s say I have a liveview

defmodule MyLiveview do
  ...
  def mount(_, _, socket) do
    ...doing something
    {:ok, socket}
  end

  def handle_params(_, _, socket) do
    socket
    ...
    |> assign(:data, get_data(socket))
  end

  def get_data(socket) do
    socket =   
      socket
      |> assign(data: some_data)
    {:noreply, socket}
  end
end

I want to create another liveview that does all the same things except i want to change how get_data/1 puts the data on assigns. so I basically want to use MyLiveview and defoveridable get_data/1 in this new liveview MyLiveView.AnotherOne. but if i wrap MyLiveview in

 defmacro __using__(_opts) do
    quote do

and use MyLiveview in MyLiveView.AnotherOne it no longer works for the route that uses MyLiveview but works fine for MyLiveView.AnotherOne. So, how can I use all the functions from one liveview while overriding some? Basically I have a use case for a liveview that acts exactly like another liveview except for one small aspect in how data is fetched and I dont want to maintain 2 liveviews with 95% the same code. Any suggestions would be greatly appreciated!

Welcome to on_mount!

In your live view modules, you can use the on_mount function on compile time (defined at a module level).

Something like

defmodule MyLiveview do

   # use Phoenix.LiveView,  ...
   # or
   # use ProjectWeb, :live_view
    on_mount({HooksModule, {:do_your_thing, args}})

end

Then on your HooksModule you can:


defmodule HooksModule do
   def on_mount({:do_your_thing, args}, _params, _session, socket) do
      # Use this first argument to customize you mount strategy

      socket = 
          socket
          |> attach_hook(:event, :handle_event, &handle_custom_event/3)
          |> attach_hook(:params, :handle_params, &handle_custom_params/3)
          |> attach_hook(:info, :handle_info, &handle_custom_info/2)
      # The first (after socket) argument of attach_hook is not that important, the other two, very much so.
      {:cont, socket}
   end 
   
    defp handle_custom_event(event, params, socket) do
        IO.inspect(params, label: "#{event} params")
        case event do
           "captured_event" -> 
                 # perform global logic
                 {:halt, socket}
            _ -> 
                 {:cont, socket}
        end
        
    end 

    defp handle_custom_params(params, uri, socket) do
        IO.inspect(event, label: "PARAMS")
        # same :cont / :halt logic
        {:cont, socket}
    end 

    defp handle_custom_info(event, socket) do
        IO.inspect(event, label: "INFO")
        # same :cont / :halt logic
        {:cont, socket}
    end
end 

If you are coming from a OOP language, this is how you’d do something akin to inheritance. In general, whenever you want to encapsulate code, think more about how to inject boilerplate (before or after), more than how to give super powers to something already there. Immutability is real.

4 Likes

ah nice! i’ll experiment with this. thanks!