carlosarli
Dynamic nested forms
Hello,
I wanted to ask you if you could point me in the right direction on how to do something that I’ve been banging my head against for a couple of days now. I have a table user which links with a has_many relationship to a table sibling that contains two fields name and surname. If I were to create a form that lets you add siblings and for each new sibling add the name and surname, how would you suggest I go on about doing it? What I mean is how can I add a “add” button to the user form that lets me add a new sibling every time i press it?
as of now I made the form multipart and added the inputs_for f, :siblings p nested form which lets me add a sibling per user.
I then followed an article from AlchemistCamp to make the form dynamic and added the add and remove buttton with relative js. problem is the article i followed is imprinted on adding only one field to fill an array structure rather than a separate model, so I was wondering if anybody had ever done that and could point me in the right direction.
Thank you very much
Marked As Solved
carlosarli
Thanks for your help
I solved it using the Formex library
it made things quite easy actually
Also Liked
joaquinalcerro
alexandrubagu
If you don’t want to use Formex here’s an example of nested form. Assuming User has_one Profile:
defmodule Database.Schema.User do
use Ecto.Schema
import Ecto.Changeset
alias Database.Schema.{
User,
Profile,
Post
}
schema "users" do
field :email, :string
field :password, :string
has_one :profile, Profile
has_many :posts, Post
timestamps()
end
# @doc false
def changeset(user, attrs) do
user
|> cast(attrs, [:email, :password])
|> validate_required([:email, :password])
|> validate_format(:email, ~r/@/)
|> unique_constraint(:email)
end
def changeset_assoc(%User{} = user, attrs) do
user
|> changeset(attrs)
|> cast_assoc(:profile, required: true)
end
end
defmodule Database.Schema.Profile do
use Ecto.Schema
import Ecto.Changeset
alias Database.Schema.User
schema "profiles" do
field :address, :string
field :name, :string
field :phone, :string
belongs_to :user, User
timestamps()
end
@doc false
def changeset(profile, attrs) do
profile
|> cast(attrs, [:name, :phone, :address])
|> validate_required([:name, :phone, :address])
end
end
Here’s the user repository for creating changesets:
defmodule Database.Repo.User do
import Ecto.Query, warn: false
alias Database.Repo
alias Database.Schema.User
def create_assoc(attrs \\ %{}) do
%User{}
|> User.changeset_assoc(attrs)
|> Repo.insert()
end
def change_assoc(%User{} = user) do
User.changeset_assoc(user, %{})
end
end
Inside your controller you can do something like this:
defmodule Frontend.User.AuthController do
use Frontend, :controller
def register(conn, params) do
case params do
%{"user" => user_params}
-> case Database.Repo.User.create_assoc(user_params) do
{:ok, user} ->
conn
|> put_session(:user_id, user.id)
|> put_flash(:info, "User created successfully.")
|> redirect(to: home_path(conn, :index))
{:error, %Ecto.Changeset{} = changeset} ->
render(conn, "register.html", changeset: changeset)
end
_ -> render(conn, "register.html", changeset: Database.Repo.User.change_assoc(%Database.Schema.User{}))
end
end
end
and inside your template you can use the nested form:
<section class="service-layout1 bg-accent s-space-custom2">
<div class="container">
<div class="section-title-dark">
<h1>Register</h1>
</div>
<div class="row">
<div class="col-lg-4 col-md-4 col-sm-6 col-xs-6 col-mb-12 item-mb">
<%= form_for @changeset, auth_path(@conn, :register), fn f -> %>
<%= inputs_for f, :profile, fn p -> %>
<div class="form-group">
<%= label p, :name, class: "control-label" %>
<%= text_input p, :name, class: "form-control" %>
<%= error_tag p, :name %>
</div>
<% end %>
<div class="form-group">
<%= label f, :email, class: "control-label" %>
<%= text_input f, :email, class: "form-control" %>
<%= error_tag f, :email %>
</div>
<div class="form-group">
<%= label f, :password, class: "control-label" %>
<%= text_input f, :password, class: "form-control" %>
<%= error_tag f, :password %>
</div>
<%= inputs_for f, :profile, fn p -> %>
<div class="form-group">
<%= label p, :phone, class: "control-label" %>
<%= text_input p, :phone, class: "form-control" %>
<%= error_tag p, :phone %>
</div>
<div class="form-group">
<%= label p, :address, class: "control-label" %>
<%= text_input p, :address, class: "form-control" %>
<%= error_tag p, :address %>
</div>
<% end %>
<div class="form-group">
<%= submit "Submit", class: "btn btn-primary" %>
</div>
<% end %>
</div>
</div>
</div>
</section>
Hope will help ![]()
feliperenan
If somebody is looking for a solution that uses only Javascript, I have just created this library that should do this work.
https://github.com/feliperenan/dynamic_nested
The code I have there is just a start after facing this problem again in my new project. So it’s just about adding/removing group of fields. Feel free to contribute if you have any ideas ![]()
Last Post!
feliperenan
If somebody is looking for a solution that uses only Javascript, I have just created this library that should do this work.
https://github.com/feliperenan/dynamic_nested
The code I have there is just a start after facing this problem again in my new project. So it’s just about adding/removing group of fields. Feel free to contribute if you have any ideas ![]()
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #hex









