通过一张表单提交创建多条记录

问题描述 投票:0回答:1

我有 3 个模型:用户、成分以及哪个用户拥有哪些成分的地图 - UserIngredient。

我当前的设置适用于一次添加 1 种成分。我想要的是更新代码,以便用户可以输入一些成分,然后只需单击“提交”一次,而不是单独单击每种成分。我研究过nested_resources,但似乎不是使用它的正确位置。

这样做的正确方法是什么?

应用程序/模型/user.rb

class User < ApplicationRecord
  ...

  has_many :user_ingredients, dependent: :destroy
  has_many :ingredients, through: :user_ingredients
  
  ...
end 

应用程序/模型/ingredient.rb

class Ingredient < ApplicationRecord
  ...

  has_many :user_ingredients, dependent: :destroy
  has_many :owners, through: :user_ingredients

  ...
end

应用程序/模型/user_ingredient.rb

class UserIngredient < ApplicationRecord
  belongs_to :user
  belongs_to :ingredient

  validates :user, presence: true
  validates :ingredient, presence: true
end

应用程序/视图/user_ingredients/new.html.erb

<div>
  <%= turbo_frame_tag @user_ingredient do %>
    <%= render "form", user_ingredient: @user_ingredient %>
  <% end %>
</div>

应用程序/视图/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>

应用程序/控制器/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

应用程序/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());
  }
}


ruby-on-rails ruby activerecord rails-activerecord
1个回答
0
投票

您应该使用

accepts_nested_attributes_for
方法来
User
并尝试通过
User
创建相关记录。

https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

或者您可以尝试进行自定义操作来一次接受具有多个记录的自定义表单。但第一个选项将更可预测且更容易支持。

对于视图,您可以使用

cocoon
gem。它已经很旧了,但仍然运行良好。 或者您可以从中获得灵感并制定您的定制解决方案) https://github.com/nathanvda/cocoon

© www.soinside.com 2019 - 2024. All rights reserved.