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 