1 回のフォーム送信で複数のレコードを作成する
概要
ユーザー、材料、そしてどのユーザーがどの材料を持っているかのマップである UserIngredient の 3 つのモデルがあります。
私の現在のセットアップは、一度に 1 つの材料を追加するために機能します。私が望んでいるのは、ユーザーが各材料を個別にクリックするのではなく、いくつかの材料を入力して「送信」を 1 回クリックするだけで済むようにコードを更新することです。 nested_resources を調べましたが、それを使用するのに適切な場所ではないようです。
これを行う正しい方法は何ですか?
app/models/user.rb
class User < ApplicationRecord
...
has_many :user_ingredients, dependent: :destroy
has_many :ingredients, through: :user_ingredients
...
end
アプリ/モデル/成分.rb
class Ingredient < ApplicationRecord
...
has_many :user_ingredients, dependent: :destroy
has_many :owners, through: :user_ingredients
...
end
app/models/user_ingredient.rb
class UserIngredient < ApplicationRecord
belongs_to :user
belongs_to :ingredient
validates :user, presence: true
validates :ingredient, presence: true
end
app/views/user_ingredients/new.html.erb
<div>
<%= turbo_frame_tag @user_ingredient do %>
<%= render "form", user_ingredient: @user_ingredient %>
<% end %>
</div>
app/views/ユーザー成分/_form.html.erb
<div class="w-full mx-auto">
<%= form_for @user_ingredient do |f| %>
<div class="flex-row gap--md">
<%= f.select(
:ingredient_id,
options_from_collection_for_select(Ingredient.where(id: f.object.ingredient_id), :id, :name, :selected => f.object.ingredient_id),
{ prompt: 'Start typing to search' },
{ id: "drDwn_ingredient",
class: "w-full border border-black",
required: true,
data: {
controller: "selectIngredient",
selectIngredient_url_value: autocomplete_ingredients_path,
},
}) %>
<div class="flex-row gap--xxxs">
<label>
<input type="submit" class="add_cancel_ing gap--md" />
<%= inline_svg_tag "svg/circle-check.svg", class: "svg_add_ing" %>
</label>
<%= link_to user_ingredients_path do %>
<%= inline_svg_tag "svg/circle-xmark.svg", class: 'svg_cancel_ing' %>
<% end %>
</div>
</div>
<% end %>
</div>
app/controllers/user_ingredients_controller.rb
class UserIngredientsController < ApplicationController
before_action :authenticate_user!
before_action :set_user_ingredient, only: [:show, :destroy]
def index
@user_ingredients = current_user.user_ingredients
end
def new
@user_ingredient = UserIngredient.new
end
def create
@user_ingredient = UserIngredient.new(user_ingredient_params.merge(user: current_user))
if @user_ingredient.save
respond_to do |format|
format.html { redirect_to user_ingredients_path, notice: 'Ingredient was successfully added to your bar!' }
format.turbo_stream { flash.now[:notice] = 'Ingredient was successfully added to your bar!' }
end
else
render :new
end
end
def destroy
@user_ingredient.destroy
respond_to do |format|
format.html { redirect_to user_ingredients_path, notice: "Ingredient was removed!" }
format.turbo_stream { flash.now[:notice] = "Ingredient was removed!" }
end
end
private
...
def set_user_ingredient
@user_ingredient = current_user.user_ingredients.find(params[:id])
end
def user_ingredient_params
params.require(:user_ingredient).permit(:id, :ingredient_id)
end
end
app/javascript/controllers/selectIngredient_controller.js
import { Controller } from "@hotwired/stimulus";
import { get } from "@rails/request.js";
import TomSelect from "tom-select";
export default class extends Controller {
static values = { url: String };
multi_select_config = function () {
return {
plugins: ["remove_button", "no_active_items"],
valueField: "value",
load: (q, callback) => this.search(q, callback),
closeAfterSelect: true,
persist: false,
create: false,
};
};
async search(q, callback) {
const response = await get(this.urlValue, {
query: { q: q },
responseKind: "json",
});
if (response.ok) {
const list = await response.json;
callback(list);
} else {
console.log("Error in select_ctrl: ");
console.log(response);
callback();
}
}
connect() {
new TomSelect(this.element, this.multi_select_config());
}
}
解決策
ユーザーには accepts_nested_attributes_for メソッドを使用する必要があります そして、ユーザー経由で関連レコードを作成してみてください。
https://translate.google.com/translate?hl=ja&sl=en&tl=ja&u=https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
または、複数のレコードを含むカスタム フォームを一度に受け入れるためのカスタム アクションを作成してみることもできます。ただし、最初のオプションの方が予測可能であり、サポートが容易です。
ビューには cocoon gem を使用できます。かなり古いものですが、まだまだ現役で使えます。 または、そこからインスピレーションを受けてカスタム ソリューションを作成することもできます) https://translate.google.com/translate?hl=ja&sl=en&tl=ja&u=https://github.com/nathanvda/cocoon