zoedsoupe

zoedsoupe

How to properly create nested form for 1-N relations?

I followed this tutorial: Nested model forms with Phoenix LiveView · FullstackPhoenix

I have a schema called Folha, defined as:

  schema "folhas" do
    field :competencia, :string

    belongs_to :empresa, Empresa
    has_many :itens, ItensFolha

    timestamps()
  end

  @doc false
  def changeset(folha, attrs) do
    folha
    |> cast(attrs, [:competencia, :empresa_id])
    |> validate_required([:competencia, :empresa_id])
    |> parse_string(:competencia)
    |> foreign_key_constraint(:empresa_id)
    |> cast_assoc(:itens, required: true, with: &ItensFolha.changeset/2)
  end

And the ItensFolha is defined as:

  schema "itens_folha" do
    field :adicional_noturno, :decimal
    field :bonificacao, :decimal
    field :descontos, :decimal
    field :dias_faltas, :integer
    field :finalizado?, :boolean, default: false
    field :hora_extra, :decimal

    # fields to enable nested form
    field :temp_id, :string, virtual: true
    field :delete, :boolean, virtual: true

    belongs_to :folha, Folha
    belongs_to :funcionario, Funcionario

    timestamps()
  end

  @doc false
  def changeset(itens_folha, attrs) do
    itens_folha
    |> cast(attrs, @fields)
    |> validate_required(@required_fields)
    |> foreign_key_constraint(:funcionario)
    |> maybe_mark_for_deletion()
  end

  defp maybe_mark_for_deletion(%{data: %{id: nil}} = changeset), do: changeset

  defp maybe_mark_for_deletion(changeset) do
    if get_change(changeset, :delete) do
      %{changeset | action: :delete}
    else
      changeset
    end
  end

On the live view side, I have the scaffold files: index.ex, show.ex, form_component.ex and it’s templates!

My Folha form is:

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

<%= f = form_for @changeset, "#",
  id: "folha-form",
  phx_target: @myself,
  phx_change: "validate",
  phx_submit: "save" %>
  <%= label f, :competencia %>
  <%= text_input f, :competencia %>
  <%= error_tag f, :competencia %>

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

  <p>Item Folha</p>    
  
  <%= inputs_for f, :itens, fn i -> %>
    <%= label i, :dias_faltas %>
    <%= number_input i, :dias_faltas %>
    <%= error_tag i, :dias_faltas %>

    <%= label i, :hora_extra %>
    <%= number_input i, :hora_extra, step: "any" %>
    <%= error_tag i, :hora_extra %>

    <%= label i, :adicional_noturno %>
    <%= number_input i, :adicional_noturno, step: "any" %>
    <%= error_tag i, :adicional_noturno %>

    <%= label i, :descontos %>
    <%= number_input i, :descontos, step: "any" %>
    <%= error_tag i, :descontos %>

    <%= label i, :finalizado? %>
    <%= checkbox i, :finalizado? %>
    <%= error_tag i, :finalizado? %>

    <%= label i, :bonificacao %>
    <%= number_input i, :bonificacao, step: "any" %>
    <%= error_tag i, :bonificacao %>

    <%= label i, :funcionario_id %>
    <%= text_input i, :funcionario_id %>
    <%= error_tag i, :funcionario_id %>

    <%= if is_nil(i.data.temp_id) do %>
      <%= checkbox i, :delete %>
    <% else %>
      <%= hidden_input i, :temp_id %>
      <a href="#" phx-click="remove-item" phx-value-remove="<%= i.data.temp_id %>">&times</a>
    <% end %>
  <% end %>

  <a href="#" phx-click="add-item">Add a item folha</a>
  
  <%= submit "Save", phx_disable_with: "Saving..." %>
</form>

However, where I put the handle_event/3 “add-item” and “remove_item” clauses?

I tried to put on form_component.ex but when I clicked the button, it throws a “validate” event…
So I tried to put on index.ex, however, the socket on this live view does not have the field changeset, so I can’t to retrieve existing items. What I’m doing wrong?

I created a gist with some code: https://gist.github.com/a0e7230f312fb50fe681b984bac9b7a6

I also have all templates and live views for the ItensFolha schema, could I “embed” one form_component into another?

Most Liked

zoedsoupe

zoedsoupe

Also, my test fails as:

1) test Index saves new folha (ContsWeb.FolhaLiveTest)
     test/conts_web/live/folha_live_test.exs:34
     ** (ArgumentError) could not find non-disabled input, select or textarea with name "folha[itens][]" within:

         <input name="_csrf_token" type="hidden" value="ACJTGywjKjwjHhgWHSJNEToZXmsgfTo1Vo2yIMyef-oRSVusVAi8O1R_"/>
         <input id="folha-form_competencia" name="folha[competencia]" type="text" value=""/>
         <input id="folha-form_empresa_id" name="folha[empresa_id]" type="text" value=""/>

     code: |> render_submit()
     stacktrace:
       (phoenix_live_view 0.15.4) lib/phoenix_live_view/test/live_view_test.ex:885: Phoenix.LiveViewTest.call/2
       test/conts_web/live/folha_live_test.exs:52: (test)

Where Next?

Popular in Questions Top

Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
New
shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call t...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" =&gt; #BSON.ObjectId&lt;58eb1a7a9ad169198c3dXXXX&gt;, "email" =&gt; ...
New

Other popular topics Top

Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
Lily
In templates/appointment/index.html.eex: &lt;%= for appointment &lt;- @appointments do %&gt; &lt;tr&gt; &lt;td&gt;&lt;%= appoi...
New
AngeloChecked
What learn first? Rust or Elixir Hi Elixir community! I’m here because i want learn a new language. I’m a junior developer and mainly i ...
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
saif
Hello everyone, Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New

Latest on Elixir Forum

We're in Beta

About us Mission Statement