javascript - 如何设置包含属于product_category 的选项的订单

标签 javascript jquery ruby-on-rails simple-form

目标

我想设置一个订单表单,用户可以在其中订购一种产品。填写完product_category后,用户可以选择

  • 属于product_category的产品
  • 属于product_category的每个选项的数量。

当前状态

我当前设置代码的方式在必须再次构建表单时会导致问题:

  • 当触发验证问题时,(1) 产品类别、(2) 产品和 (3) 选项为空,但预订仍会保存,从而造成预订被保存两次的情况。

=> 我知道这是因为我首先将预订保存在 Controller 中,然后保存选项,但我不知道如何解决这个问题(例如,它是在触发验证时以及用户有之后正确填写表格)。

代码

型号

class Order < ApplicationRecord
  belongs_to :store
  belongs_to :product

  has_many :order_options, dependent: :destroy
  has_many :options, through: :order_options
  accepts_nested_attributes_for :order_options
end

class OrderOption < ApplicationRecord
  belongs_to :option
  belongs_to :order
  accepts_nested_attributes_for :option
end

class Option < ApplicationRecord
  belongs_to :product_category
  has_many :order_options, dependent: :destroy
  has_many :orders, through: :order_options
end

class ProductCategory < ApplicationRecord
  belongs_to :store
  has_many :products, dependent: :destroy
  accepts_nested_attributes_for :products, allow_destroy: true
  has_many :options, dependent: :destroy
  accepts_nested_attributes_for :options, allow_destroy: true
end

订单 Controller

class OrdersController < ApplicationController
  # skip_before_action :authenticate_user!
  def new
    @user = current_user
    @store = Store.find(params[:store_id])
    @order = Order.new
    @order.build_order_contact
    @product_category_list = @store.product_categories
    @all_options = @store.options
    @products = []
    @options = []
    if params[:product_category].present?
      @products = ProductCategory.find(params[:product_category]).products
      @options = ProductCategory.find(params[:product_category]).options
    else
    end
    if request.xhr?
      respond_to do |format|
        format.json {
        render json: {products: @products, options: @options}
      }
        format.js
      end
    end

    authorize @order
  end

  def create
    @user = current_user
    @store = Store.find(params[:store_id])
    @order = Order.new(order_params)
    @order.store = @store
    authorize @order
    if @order.save
      params[:order_options_attributes].each do |order_option|
        if @option = Option.find_by(id: order_option[:option_id])
          @option_quantity = order_option[:option_quantity]
          @order.options << @option
          order_option = @order.order_options.where(option: @option)
          order_option.update(option_quantity: @option_quantity)
        end
      end
      redirect_to store_path(@store)
    else
      @product_category_list = @store.product_categories
      render 'new'
    end
  end

View /订单/new.js

$("#product_options").html("<%= escape_javascript(render partial: 'option_fields', collection: @options) %>");


$("#dynamic-products").empty();
<% @products.each do |pro| %>
    $("#dynamic-products").append('<option value="<%= pro.id %>"><%= pro.name %></option>')
<% end %>

View /订单/new.html.erb

<%= simple_form_for [@store, @order] do |f|%>
  <%= f.simple_fields_for :products do |product| %>
    <%= product.input :product_category, collection: @product_category_list, prompt: "Select type of product", label:false,
      input_html:{
      id: "product_category"
    }%>

  <%= f.association :product, collection: @products,  input_html:{
    value: @products.object_id,
    id: "dynamic-products"
  } %>

  <div class="product_category-options" id="product_options">
  </div>
  <% end %>
<% end %>

<script >
  // dynamic products and options for change category
  $(document).on("change", "#product_category", function(){
    var product_category = $(this).val();


    $.ajax({
      url: "/stores/<%= @store.id %>/orders/new",
      method: "GET",
      // dataType: "json",
      dataType: "script",
      data: {product_category: product_category},
      error: function (xhr, status, error) {
        console.error('AJAX Error: ' + status + error);
      },
      success: function (response) {
    }
  });
  });
  // dynamic products and option for releading form (e.g. new)
   $(document).ready(function(){
    var product_category = $("#product_category").val();

    $.ajax({
      url: "/stores/<%= @store.id %>/orders/new",
      method: "GET",
      dataType: "json",
      data: {product_category: product_category},
      error: function (xhr, status, error) {
        console.error('AJAX Error: ' + status + error);
      },
      success: function (response) {
    }
  });
  });

</script>

views/orders/_option_fields.html.erb

<div class="product_option order-form-quantity-row border-bottom col col-sm-10">
  <div class="product_option_name order-form-quantity-name">
    <strong>  <%= option_fields.name %></strong>
  </div>

  <div class="order-form-input">
    <%= hidden_field_tag("order_options_attributes[]option_id", option_fields.id ) %>
    <%= select_tag("order_options_attributes[]option_quantity", options_for_select((0..9)), {class:'form-control col col-sm-12'} ) %>

  </div>
</div>

最佳答案

这是非常复杂和误导的。您真正需要的是:

<%= simple_form_for([@store, @order]) do |f| %>
  <% f.simple_fields_for(:order_options) do |ff| %>
    <%= ff.association :option %>
    <%= ff.input :option_quantity %>
  <% end %>
   # ...
<% end %>
<小时/>
class OrdersController
  # Use callbacks to DRY your code
  before_action :set_store, only: [:new, :create, :index]

  def new
    @order = @store.order.new
    # seed the record to create the inputs
    5.times { @order.order_options.build }
    authorize @order
  end

  def create
    @order = @store.orders.new(order_params) do |order|
       order.user = current_user
    end

    if @order.save
       redirect_to @order.store
    else
       render :new
    end
  end

  def set_store
    @store = Store.find(params[:store_id])
  end

  def order_params
    params.require(:order)
          .permit(:foo, :bar, 
             order_options_attributes: [:option_id, :option_quantity]
          )
  end 
end

您不需要接受选项的嵌套属性,除非您让用户动态创建它们,这似乎不是一个好主意,因为您在单个组件中已经有 100 个级别的复杂性。

您也不需要执行 params[:order_options_attributes].each do |order_option| 并迭代嵌套属性。真的永远不要这样做,因为它首先违背了使用嵌套属性的全部目的。

当您使用由 accepts_nested_attributes 创建的 order_options_attributes= setter 时,Rails 将处理将属性分配给 order_options 的新实例,并将在保存记录之前执行此操作。当您调用 save 时,它​​会立即将所有内容保存在事务中,从而避免您遇到的大多数问题。

您可以在保存之前使用 validates_linked 触发对 order_options 的验证。

如果您想使用 AJAX 来修饰它,请随意。但您实际上应该从设置一些简单且同步的东西开始,以便了解嵌套属性的工作原理。

总的来说,这段代码似乎速度太快了。首先仅设置基础知识(即仅创建产品订单)。测试它 - 重构 - 然后添加更多功能。如果您尝试一次完成所有事情,通常会导致垃圾箱着火。

关于javascript - 如何设置包含属于product_category 的选项的订单,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58693234/

相关文章:

android - JSON 或 HTML : Designing my server side to reduce latency and support multiple clients

javascript - 在 Firebase 中对项目集合进行建模的正确方法

javascript - 获取 MooTools 日期选择器的值(value)

javascript - 如何在 Handlebars 模板中使用西类牙语字符

javascript - 无法使用nodemailer在nodejs中发送电子邮件

jquery - 单击滚动按钮,每隔几秒只需单击一次

jquery - 如何在jquery中读取ViewBag列表

mysql - 如何查找从rails数据库返回的记录数组的索引?

javascript - 如何从自动完成列表中删除以前选择的项目

ruby-on-rails - 实时聊天,不想丢失消息——使用队列(redis、zeromq)?