插入具有 has_many 关联的模型
Insert a model with has_many association
我有以下型号:
# Foo model
schema "foo" do
field :name, :string
has_many: :bars, App.Bar
end
# App model
schema "bar" do
field :name, :string
belongs_to: foo, App.Foo
end
还有这个表格:
# form.html (Foo)
<%= form_for @changeset, @action, fn f -> %>
<%= text_input f, :name, class: "form-control" %>
<%= submit "Submit", class: "btn btn-primary" %>
<% end %>
在此表单中,如何添加文本字段以使用 Bars
填充新的 Foo
?
以下内容不起作用,因为 bars
未预加载:
<%= text_input f, :bars, class: "form-control" %>
我走在正确的轨道上吗?如果是这样,我如何在表单中预加载 Bars
?
更新,控制器:
def new(conn, _params) do
changeset = %Foo{} |> Repo.preload(:bars) |> Foo.changeset
render(conn, "new.html", changeset: changeset)
end
def create(conn, %{"foo" => foo_params}) do
changeset = %Foo{} |> Repo.preload(:bars) |> Foo.changeset(foo_params)
if changeset.valid? do
Repo.insert!(changeset)
conn
|> put_flash(:info, "Foo created successfully.")
|> redirect(to: foo_path(conn, :index))
else
render(conn, "new.html", changeset: changeset)
end
end
预加载似乎有效,但到达时我得到一个 Argument error
<%= text_input f, :bars, class: "form-control" %>
:
[error] #PID<0.280.0> running App.Endpoint terminated
Server: 192.168.48.202:4000 (http)
Request: GET /
** (exit) an exception was raised:
** (ArgumentError) argument error
:erlang.bit_size([])
(phoenix_html) lib/phoenix_html/tag.ex:66: anonymous fn/2 in Phoenix.HTML.Tag.tag_attrs/1
(elixir) lib/enum.ex:1261: Enum."-reduce/3-lists^foldl/2-0-"/3
(phoenix_html) lib/phoenix_html/tag.ex:35: Phoenix.HTML.Tag.tag/2
(app) web/templates/foo/form.html.eex:16: anonymous fn/1 in App.FooView.form.html/1
(phoenix_html) lib/phoenix_html/form.ex:181: Phoenix.HTML.Form.form_for/4
(app) web/templates/foo/form.html.eex:1: App.FooView."form.html"/1
(app) web/templates/foo/new.html.eex:3: App.FooView."new.html"/1
如果您的模型有嵌套关联,您可以使用 inputs_for 将嵌套数据附加到表单。例如,请参阅 嵌套输入 部分中的 here。
查看 Jose 在 Working with Associations and Embeds 上的 post 以获得使用 ToDoList 和 ToDoItems 的可靠示例(特别是标题为 "Nested associations and embeds" 的部分)。下面的示例是一个衍生示例,反映了 Foos 与 Bars.
的组合
首先,您走在正确的轨道上:
has_many: :bars, App.Bar
修改您的表单以反映:
# form.html (Foo)
<%= form_for @changeset, @action, fn f -> %>
<%= text_input f, :name, class: "form-control" %>
<%= inputs_for f, :bars, fn i -> %>
<div class="form-group">
<%= label i, :name, "Bar ##{i.index + 1}", class: "control-label" %>
<%= text_input i, :name, class: "form-control" %>
</div>
<% end %>
<%= submit "Submit", class: "btn btn-primary" %>
<% end %>
这利用 Phoenix.HTML.Form
中的 inputs_for/4
函数为您的 :bars 关联生成字段。在这里,我们按顺序标记了每个 "Bar #1" 和 "Bar #2",并为每个提供了 text_input
标签。
现在,您必须调整控制器的 new
和 create
操作以反映包含一些条(例如,假设两个):
def new(conn, _params) do
changeset = %Foo{} |> Foo.changeset(%Foo{bars: [%MyApp.Bar{}, %MyApp.Bar{}]})
render(conn, "new.html", changeset: changeset)
end
def create(conn, %{"foo" => foo_params}) do
changeset = %Foo{} |> Foo.changeset(foo_params)
case Repo.insert(changeset) do
conn
|> put_flash(:info, "Foo created successfully.")
|> redirect(to: foo_path(conn, :index))
else
render(conn, "new.html", changeset: changeset)
end
end
您的 edit
和 update
操作将需要预加载栏:
foo = Repo.get!(Foo, id) |> Repo.preload(:bars)
我有以下型号:
# Foo model
schema "foo" do
field :name, :string
has_many: :bars, App.Bar
end
# App model
schema "bar" do
field :name, :string
belongs_to: foo, App.Foo
end
还有这个表格:
# form.html (Foo)
<%= form_for @changeset, @action, fn f -> %>
<%= text_input f, :name, class: "form-control" %>
<%= submit "Submit", class: "btn btn-primary" %>
<% end %>
在此表单中,如何添加文本字段以使用 Bars
填充新的 Foo
?
以下内容不起作用,因为 bars
未预加载:
<%= text_input f, :bars, class: "form-control" %>
我走在正确的轨道上吗?如果是这样,我如何在表单中预加载 Bars
?
更新,控制器:
def new(conn, _params) do
changeset = %Foo{} |> Repo.preload(:bars) |> Foo.changeset
render(conn, "new.html", changeset: changeset)
end
def create(conn, %{"foo" => foo_params}) do
changeset = %Foo{} |> Repo.preload(:bars) |> Foo.changeset(foo_params)
if changeset.valid? do
Repo.insert!(changeset)
conn
|> put_flash(:info, "Foo created successfully.")
|> redirect(to: foo_path(conn, :index))
else
render(conn, "new.html", changeset: changeset)
end
end
预加载似乎有效,但到达时我得到一个 Argument error
<%= text_input f, :bars, class: "form-control" %>
:
[error] #PID<0.280.0> running App.Endpoint terminated
Server: 192.168.48.202:4000 (http)
Request: GET /
** (exit) an exception was raised:
** (ArgumentError) argument error
:erlang.bit_size([])
(phoenix_html) lib/phoenix_html/tag.ex:66: anonymous fn/2 in Phoenix.HTML.Tag.tag_attrs/1
(elixir) lib/enum.ex:1261: Enum."-reduce/3-lists^foldl/2-0-"/3
(phoenix_html) lib/phoenix_html/tag.ex:35: Phoenix.HTML.Tag.tag/2
(app) web/templates/foo/form.html.eex:16: anonymous fn/1 in App.FooView.form.html/1
(phoenix_html) lib/phoenix_html/form.ex:181: Phoenix.HTML.Form.form_for/4
(app) web/templates/foo/form.html.eex:1: App.FooView."form.html"/1
(app) web/templates/foo/new.html.eex:3: App.FooView."new.html"/1
如果您的模型有嵌套关联,您可以使用 inputs_for 将嵌套数据附加到表单。例如,请参阅 嵌套输入 部分中的 here。
查看 Jose 在 Working with Associations and Embeds 上的 post 以获得使用 ToDoList 和 ToDoItems 的可靠示例(特别是标题为 "Nested associations and embeds" 的部分)。下面的示例是一个衍生示例,反映了 Foos 与 Bars.
的组合首先,您走在正确的轨道上:
has_many: :bars, App.Bar
修改您的表单以反映:
# form.html (Foo)
<%= form_for @changeset, @action, fn f -> %>
<%= text_input f, :name, class: "form-control" %>
<%= inputs_for f, :bars, fn i -> %>
<div class="form-group">
<%= label i, :name, "Bar ##{i.index + 1}", class: "control-label" %>
<%= text_input i, :name, class: "form-control" %>
</div>
<% end %>
<%= submit "Submit", class: "btn btn-primary" %>
<% end %>
这利用 Phoenix.HTML.Form
中的 inputs_for/4
函数为您的 :bars 关联生成字段。在这里,我们按顺序标记了每个 "Bar #1" 和 "Bar #2",并为每个提供了 text_input
标签。
现在,您必须调整控制器的 new
和 create
操作以反映包含一些条(例如,假设两个):
def new(conn, _params) do
changeset = %Foo{} |> Foo.changeset(%Foo{bars: [%MyApp.Bar{}, %MyApp.Bar{}]})
render(conn, "new.html", changeset: changeset)
end
def create(conn, %{"foo" => foo_params}) do
changeset = %Foo{} |> Foo.changeset(foo_params)
case Repo.insert(changeset) do
conn
|> put_flash(:info, "Foo created successfully.")
|> redirect(to: foo_path(conn, :index))
else
render(conn, "new.html", changeset: changeset)
end
end
您的 edit
和 update
操作将需要预加载栏:
foo = Repo.get!(Foo, id) |> Repo.preload(:bars)