Skip to content

Instantly share code, notes, and snippets.

@jmichalicek
Last active September 10, 2016 02:41
Show Gist options
  • Save jmichalicek/85fd1bac89f6ec9620af54f210c1a05a to your computer and use it in GitHub Desktop.
Save jmichalicek/85fd1bac89f6ec9620af54f210c1a05a to your computer and use it in GitHub Desktop.
Form for a model and related child models in Phoenix 1.2 and Ecto 2.0.
defmodule MyApp.Child do
use MyApp.Web, :model
schema "child" do
field :name, :string
field :description, :string
belongs_to :event, MyApp.Parent
timestamps
end
@doc """
Creates a changeset based on the `model` and `params`.
If no params are provided, an invalid changeset is returned
with no validation performed.
"""
def changeset(model, params \\ %{}) do
model
|> cast(params, [:name, :description])
|> cast_assoc(:parent, required: True)
|> validate_required([:name, ])
end
def nested_changeset(model, params \\ %{}) do
# Does not try to cast the parent assoc
model
|> cast(params, [:name, :description])
|> validate_required([:name, ])
end
end
defmodule MyApp.Parent do
use MyApp.Web, :model
schema "parent" do
field :name, :string
field :description, :string
has_many :children, MyApp.Child
timestamps
end
@error_messages %{
:name => %{
:required => "Please include a name."
},
:description => %{
:required => "Please include a short description."
}
}
def error_messages, do: @error_messages
def changeset(model, params \\ %{}) do
model
|> cast(params, [:name, :description])
|> validate_required(:name, message: @error_messages.name.required)
|> validate_required(:description, message: @error_messages.description.required)
|> cast_assoc(:location_choices, with: &MyApp.Child.nested_changeset/2)
end
end
<%= form_for @changeset, @action, fn f -> %>
<%= if @changeset.action do %>
<div class="alert alert-danger">
<p>Oops, something went wrong! Please check the errors below.</p>
</div>
<% end %>
<div class="form-group">
<%= label f, :name, class: "control-label" %>
<%= text_input f, :name, class: "form-control" %>
<%= error_tag f.source, :name %>
</div>
<div class="form-group">
<%= label f, :description, class: "control-label" %>
<%= textarea f, :description, class: "form-control" %>
<%= error_tag f.source, :description %>
</div>
<hr>
<h3>Children</h3>
<%= inputs_for f, :children, [append: [Child.nested_changeset(%Child{})]], fn fp -> %>
<div class="form-group">
<%= label fp, :name, class: "control-label" %>
<%= text_input fp, :name, class: "form-control" %>
<%= error_tag fp, :name %>
</div>
<div class="form-group">
<%= label fp, :description, class: "control-label" %>
<%= text_input fp, :description, class: "form-control" %>
<%= error_tag fp, :description %>
</div>
<% end %>
<div class="form-group">
<%= submit "Submit", class: "btn btn-primary" %>
</div>
<% end %>
defmodule MyApp.ParentController do
use MyApp.Web, :controller
alias MyApp.Parent
import Ecto.Changeset
plug :scrub_params, "parent" when action in [:create, :update]
def new(conn, params,) do
changeset = Parent.changeset(%Parent{})
render(conn, "new.html", changeset: changeset)
end
def create(conn, %{"parent" => params}, user) do
changeset = Parent.changeset(%Parent{}, params)
case Repo.insert(changeset) do
{:ok, parent} ->
conn
|> put_flash(:info, "Event created successfully.")
|> redirect(to: parent_path(conn, :show, parent))
{:error, changeset} ->
render(conn, "new.html", changeset: changeset)
end
end
def edit(conn, %{"id" => id}) do
event = Repo.get!(Parent, id) |> Repo.preload(:children)
changeset = Parent.changeset(parent)
render(conn, "edit.html", parent: parent, changeset: changeset)
end
def update(conn, %{"id" => id, "parent" => params}, user) do
parent = Repo.get!(Parent, id) |> Repo.preload(:children)
changeset = Parent.changeset(parent, params)
case Repo.update(changeset) do
{:ok, parent} ->
conn
|> put_flash(:info, "Parent updated successfully.")
|> redirect(to: event_path(conn, :edit, parent)) # just going back to edit because it is simple example
{:error, changeset} ->
render(conn, "edit.html", parent: parent, changeset: changeset)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment