Assocations not working when using liveview

I am making an event calendar. I use 2 classes for this, an organisation and an event. I want to create a one to many relation between organisation and event. I will post the code i used below. The problem raises when I trie to create an event. I get this error:

key :organisations not found

This is my code

event.ex:

defmodule EventCalendar.Events.Event do
  use Ecto.Schema
  import Ecto.Changeset
  alias EventCalendar.Organisations.Organisation

  schema "events" do
    field(:date, :date)
    field(:location, :string)
    belongs_to(:organisation, Organisation, foreign_key: :organisation_id)

    timestamps()
  end

  # @doc false
  # def changeset(event, attrs) do
  #   event
  #   |> cast(attrs, [:date, :location])
  #   |> validate_required([:date, :location])
  # end

  def changeset(event, attrs) do
    event
    |> cast(attrs, [:date, :location, :organisation_id])
    |> validate_required([:date, :location, :organisation_id])
    |> foreign_key_constraint(:organisation_id)
  end
end

organisation.ex

defmodule EventCalendar.Organisations.Organisation do
  use Ecto.Schema
  import Ecto.Changeset
  alias EventCalendar.Events.Event

  schema "organisations" do
    field(:name, :string)
    has_many(:events, Event)
    timestamps()
  end

  @doc false
  def changeset(organisation, attrs) do
    organisation
    |> cast(attrs, [:name])
    |> validate_required([:name])
  end
end

Event form_component

defmodule EventCalendarWeb.EventLive.FormComponent do
  use EventCalendarWeb, :live_component

  alias EventCalendar.Events
  alias EventCalendar.Organisations
  alias EventCalendar.Organisations.Organisation
  alias EventCalendar.Repo

  @impl true
  def update(%{event: event} = assigns, socket) do
    changeset =
      event
      |> Repo.preload(:organisation)
      |> Events.change_event()

    {:ok,
     socket
     |> assign(assigns)
     |> assign(:changeset, changeset)}
  end

  @impl true
  def handle_event("validate", %{"event" => event_params}, socket) do
    changeset =
      socket.assigns.event
      |> Events.change_event(event_params)
      |> Map.put(:action, :validate)

    {:noreply, assign(socket, :changeset, changeset)}
  end

  def handle_event("save", %{"event" => event_params}, socket) do
    save_event(socket, socket.assigns.action, event_params)
  end

  defp save_event(socket, :edit, event_params) do
    case Events.update_event(socket.assigns.event, event_params) do
      {:ok, _event} ->
        {:noreply,
         socket
         |> put_flash(:info, "Event updated successfully")
         |> push_redirect(to: socket.assigns.return_to)}

      {:error, %Ecto.Changeset{} = changeset} ->
        {:noreply, assign(socket, :changeset, changeset)}
    end
  end

  defp save_event(socket, :new, event_params) do
    case Events.create_event(event_params) do
      {:ok, _event} ->
        {:noreply,
         socket
         |> put_flash(:info, "Event created successfully")
         |> push_redirect(to: socket.assigns.return_to)}

      {:error, %Ecto.Changeset{} = changeset} ->
        {:noreply, assign(socket, changeset: changeset)}
    end
  end
end

event form_component.html.heex


<div>
  <h2><%= @title %></h2>

  <.form
    let={f}
    for={@changeset }
    id="event-form"
    phx-target={@myself}
    phx-change="validate"
    phx-submit="save">

    <%= label f, :date %>
    <%= date_select f, :date %>
    <%= error_tag f, :date %>

    <%= label f, :location %>
    <%= text_input f, :location %>
    <%= error_tag f, :location %>

    <%= label f, :organisation_id %>
     <%= select f, :organisation_id, @organisations%>
    <%= error_tag f, :organisation_id %>
    <div>
      <%= submit "Save", phx_disable_with: "Saving..." %>
    </div>
  </.form>
</div>

There appears at this line: <%= select f, :organisation_id, @organisations%>

I really don’t get it, can someone explain to me what I am doing wrong?

This error means that your update function isn’t setting an assign named organisations, which should be one of the shapes that Phoenix.HTML.Form.select/4 expects for options.

2 Likes