Nested Forms and Associations in Phoenix 1.4

phoenix

#1

I’m trying to learn how to create nested forms in Phoenix 1.4. The only tutorials I’ve been able to find are for Phoenix 1.2 or otherwise don’t work. Does anyone have any examples of a complex dynamic nested form? I’m looking at a library called Formex that I might end up using but my preferences are to write the code myself for better understanding. Thank you in advance.


#2

You can try this thread.

Let me know if it works for you.

Best regards


#3

I made it to the documents api and I have no idea where to put that or implement it. This is what I mean, nothing on this subject is very explicit. Even a simple project with a nested form implemented would be great. Even the author of that project says he “was not able to figure out ecto cast_assoc, put_assoc and how they work together.”


#4

Oh, you can find the rest of the subject here:

If this doesn’t helps, try the following resources:

  1. Dynamic nested forms
  2. http://blog.plataformatec.com.br/2018/05/nested-layouts-with-phoenix/
  3. https://hexdocs.pm/phoenix_html/Phoenix.HTML.Form.html#module-nested-inputs

Even though this resources may not be up to date the main concepts are still holding.

Hope this helps.

Best regards,


#5

I recommend the article, Working with Ecto Associations and Embeds, specifically for your question the section “Nested associations and embeds”.


#6

I got the form working but it doesn’t insert the associated :store_id into the :siteinfo database. I have cast_assoc(:siteinfo, required: true) in the Store changeset but when I query the siteinfo database after creating a store it hasn’t filled in the :store_id field. I only created the schema and migration for the :siteinfo database using mix phx.gen.schema, I shouldn’t need a controller or templates for the child right?

You can see my project under github.com/codball/blackbook under the nestedforms branch


#7

Hi,

I checked the project but was unable to find :store_id or :siteinfo

Not sure if this is the last version. I checked both branches master and nestedforms.

Let me know if this are the last changes or point me out to the specific files.

Best regards,


#8

Sorry about that, I’ve added them to the branch. Trying something with build_assoc but I might just have to do some more reading.


#9

I’ve set up a new project for learning purposes here https://github.com/Codball/nestedform. At the moment I’ve got a deadlock error I’m trying to figure out probably have something named wrong.


#10

What error are you getting?


#11

@Codball,

Actually, we can do it much better because cast_assoc do all the heavy work for us.

In my dummy project I changed the following:

file: detail.ex
change: deleted the invoice_id
code:

  def changeset(%Detail{} = detail, attrs) do
    detail
    |> cast(attrs, [:tax, :code, :desc, :up])
    |> validate_required([:tax, :code, :desc, :up])
  end

file: invoice.ex
change: added cast_assoc function
code:

  def changeset(%Invoice{} = invoice, attrs) do
    invoice
    |> cast(attrs, [:provider])
    |> validate_required([:provider])
    |> cast_assoc(:details)
  end

documents.ex
change: a) deleted functions: add_records, save_invoice; b) changed the create_invoice function
code:

  def create_invoice(attrs \\ %{}) do
      %Invoice{}
      |> Invoice.changeset(attrs)
      |> Repo.insert()
  end

I will work in a second version of this post to clarify.

Hope this works,

Best regards,


#12

==> nestedform Compiling 20 files (.ex) warning: redefining @doc attribute previously set at line 40 lib/nestedform/stores/stores.ex:58: Nestedform.Stores (module)

== Compilation error in file lib/nestedform/stores/store.ex == ** (CompileError) deadlocked waiting on struct Store lib/nestedform/stores/store.ex:16: (module) (stdlib) erl_eval.erl:677: :erl_eval.do_apply/6 (elixir) lib/kernel/parallel_compiler.ex:206: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/6

== Compilation error in file lib/nestedform/stores/siteinfo.ex == ** (CompileError) deadlocked waiting on struct Siteinfo lib/nestedform/stores/siteinfo.ex:20: (module) (stdlib) erl_eval.erl:677: :erl_eval.do_apply/6 (elixir) lib/kernel/parallel_compiler.ex:206: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/6

Compilation failed because of a deadlock between files. The following files depended on the following modules:

lib/nestedform/stores/store.ex => Store lib/nestedform/stores/siteinfo.ex => Siteinfo

Ensure there are no compile-time dependencies between those files and that the modules they reference exist and are correctly named

I run into this error quite a bit, usually after I’ve changed too many things at once.


#13

So,

The first error is because you have a @doc definition without a function. You have the function commented.

The other ones are because you have to alias the module you are in to use the small struct syntax as this:

defmodule Nestedform.Stores.Store do
  use Ecto.Schema
  import Ecto.Changeset

  alias Nestedform.Stores.Store
  ....

  def changeset(%Store{} = store, attrs) do

  end
end

Hope this helps,


#14

That cleared up quite a few errors, I guess I assumed the module would know about itself without the alias. Thank you for holding my hand.
I’m still working on it but I think I’m on the right track you’ve helped me tremendously so far please let me know when you finish that rewrite, I’ll probably be making a guide of my own once I get through all this.

There’s some good documentation for associations I didn’t see until looking for something else in the Phoenix Contexts Documentation under “In-Context Relationships.”