我有三个模型:用户、配料和哪个用户有哪些配料的Map-用户配料。
我目前的设置适用于一次添加1种成分。我想要的是更新代码,这样用户就可以输入一些成分,只需单击“提交”一次,而不是单独单击每个成分。我已经研究了nested_resources,但似乎不是使用它的正确位置。
做这件事的正确方法是什么?
app/models/user.rb
class User < ApplicationRecord
...
has_many :user_ingredients, dependent: :destroy
has_many :ingredients, through: :user_ingredients
...
end
app/models/ingredient.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/user_ingredients/_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());
}
}
1条答案
按热度按时间7d7tgy0s1#
您应该使用
accepts_nested_attributes_for
方法为User
并尝试通过User
创建相关记录。https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
或者,您可以尝试进行自定义操作,以便一次接受具有多个记录的自定义表单。但第一种选择将更可预测,更容易支持。
对于视图,您可以使用
cocoon
gem。它很旧了,但还能用。或者,您也可以从中汲取灵感,定制解决方案)https://github.com/nathanvda/cocoon