Controllers for LiveView

Hi, Rails developer here learning about Phoenix and LiveView.

I’ve been trying to accomplish something that would be quite simple in Rails. I have two models - Location and Country, and when I create a Location I want to be able to choose a country for it from a drop-down list, sourced from the Country table. In Rails this would be done with options_from_collection_for_select.

For Phoenix, I finally found a solution here. However, I can’t figure out how to convert it to LiveView.

It suggests to put the following code in what I take to be the new action in the controller:

  books =
    Media.list_books
      |> Enum.map(&{"#{&1.title} by #{&1.author}", &1.id})

However, my LiveView code, which is largely built by the generators, doesn’t appear to have a controller. live/location_live/index.ex is where most of the magic is happening. I’ve tried putting the code in a few different places, but got all sorts of error messages. In desperation I lumped the code into form_component.html.leex and voila, it works, but I’m pretty sure that’s not the right place to put it.

I feel there’s something I’m missing conceptually and would be grateful if someone could try to explain what that is. I have little experience with SPA’s.

Hello and welcome,

this file live/location_live/index.ex acts as a controller, whose actions are events based.

In the mount function, You can set these books, and assign it to the socket.

This will be available as @books in the template. You can use it as the source of your select :slight_smile:

Thanks for the response. What you say makes sense (I re-remembered some stuff about sockets when I read your reply) but I can’t get it to work.

My mount function reads like this: (sorry for the confusion about books/locations, I should’ve changed the example in my first post to be consistent)

countries = 
  Weather.list_countries
    |> Enum.map(&{"#{&1.name}", &1.id})
socket = assign(socket, :countries, countries)
{:ok, assign(socket, :locations, list_locations)

Then in form_component.html.leex I have

<%= select f, :country_id, @countries %>

That line errors with (ArgumentError) assign @countries not available in eex template.

Your code looks ok… which version of Phoenix? You can get it in your mix.exs file.

Consider using something like this as a cleaner way…

socket = 
  socket
    |> assign(:countries, countries)
    |> assign(:locations, list_locations)
{:ok, socket}

Oh… You are in a component. So You should show how You display it… because You need to pass the countries to the component as well.

If You are on Phoenix 1.5.4, You should find code like this in your index.html.leex…

<%= if @live_action in [:new, :edit] do %>
  <%= live_modal @socket, LabLvWeb.UserLive.FormComponent,
    id: @user.id || :new,
    title: @page_title,
    action: @live_action,
    user: @user,
    return_to: Routes.user_index_path(@socket, :index) %>
<% end %>

I’m on Phoenix 1.5.4. As you suggested, I do indeed see something like this in index.html.leex

<%= if @live_action in [:new, :edit] do %>
  <%= live_modal @socket, LabLvWeb.UserLive.FormComponent,
    id: @user.id || :new,
    title: @page_title,
    action: @live_action,
    user: @user,
    return_to: Routes.user_index_path(@socket, :index) %>
<% end %>

and I added counties: @countries there and everything works now, thanks! :tada:

So… this code is passing parameters to the form component if it’s a new/edit action? That makes sense to me, but why do we need to do this, aren’t some the values are already part of the socket? Doesn’t the component doesn’t have access to the socket directly? Can you help me understand what’s going on here?

A live component encapsulate some part of liveview state, but You need to pass it…