Rails 5:jquery用户输入数据不保存

Posted

技术标签:

【中文标题】Rails 5:jquery用户输入数据不保存【英文标题】:Rails 5: jquery user input data not saving 【发布时间】:2019-03-29 02:35:02 【问题描述】:

我在 Rails 应用程序中有一个表单正在发布但不保存输入数据。

服务提供商有能力定义不同的葡萄酒套装,他提供例如 1、3、6、12 和更多一般信息,这些信息显示在 show.html.erb (wine_controller) 中。

更新

在 show.html.erb 中显示了一个 _form.html.erb,它使用了 reservations_controller。

逻辑是葡萄酒可以有很多保留。

目前瓶子被保存到数据库,但总数没有保存到数据库,因为 Reservations Controller 给出了 NoMethodError,因为瓶子是未定义的局部变量或方法。

葡萄酒控制器

class WinesController < ApplicationController
  before_action :set_wine, except: [:index, :new, :create]
  before_action :authenticate_user!, except: [:show]
  before_action :is_authorised, only: [:details, :pricing, :description, :photo_upload, :more_details, :sets, :location, :update]

  def index
    @wines = current_user.wines
  end

  def new
    @wine = current_user.wines.build
  end

  def create
    @wine = current_user.wines.build(wine_params)
    if @wine.save
      redirect_to details_wine_path(@wine), notice: "Saved..."
    else
      flash[:alert] = "Something went wrong..."
      render :new
    end
  end

  def show
    @photos = @wine.photos
    @guest_reviews = @wine.guest_reviews
  end

  def details
  end

  def pricing
  end

  def description
  end

  def photo_upload
    @photos = @wine.photos
  end

  def more_details
  end

  def sets
  end

  def location
  end

  def update

    new_params = wine_params
    new_params = wine_params.merge(active: true) if is_ready_wine

    if @wine.update(wine_params)
      flash[:notice] = "Saved..."
    else
      flash[:alert] = "Something went wrong..."
    end
    redirect_back(fallback_location: request.referer)
  end

  def preload
    today = Date.today
    # Reservations greater than today (upcoming reservations)
    reservations = @wine.reservations.where("start_date >= ?", today)

    render json: reservations
  end


  private
  def set_wine
    @wine = Wine.find(params[:id])
  end
  # Authorize user (current_user == ID 1)
  def is_authorised
    redirect_to root_path, alert: "You don't have permission" unless current_user.id == @wine.user_id
  end

  def is_ready_wine
    !@wine.active && !@wine.price.blank? && !@wine.base_price.blank? && !@wine.wine_name.blank? && !@wine.summary.blank? && !@wine.photos.blank? && !@wine.alcohol.blank? && !@wine.filling.blank? && !@wine.in_stock.blank? && !@wine.address.blank?
  end

  def wine_params
    params.require(:wine).permit(:wine_type, :wine_color, :wine_art, :alcohol, :wine_name, :summary, :address, :is_1, :is_3, :is_6, :is_12, :filling, :base_price, :price, :in_stock, :year, :active)
  end
end

Reservation.rb

class Reservation < ApplicationRecord
  before_save :update_financials

  belongs_to :user
  belongs_to :wine

  def update_financials
    total = wine.price * bottles if wine.present? && bottles.present?
    price = wine.price if wine.present?
  end
end

我的 _form.html.erb 助手函数

  def wine_quantity(wine)
    case
    when wine.is_1 && wine.is_3 && wine.is_6 && wine.is_12 then [["1 Flasche", 1], ["3 Flaschen", 3], ["6 Flaschen", 6], ["12 Flaschen", 12]]
    when wine.is_1 && wine.is_3 && wine.is_6 then [["1 Flasche", 1], ["3 Flaschen", 3], ["6 Flaschen", 6]]
    when wine.is_1 && wine.is_3 && wine.is_12 then [["1 Flasche", 1], ["3 Flaschen", 3], ["12 Flaschen", 12]]
    when wine.is_1 && wine.is_6 && wine.is_12 then [["1 Flasche", 1], ["6 Flaschen", 6], ["12 Flaschen", 12]]
    when wine.is_1 && wine.is_12 then [["1 Flasche", 1], ["12 Flaschen", 12]]
    when wine.is_1 && wine.is_6 then [["1 Flasche", 1], ["6 Flaschen", 6]]
    when wine.is_1 && wine.is_3 then [["1 Flasche", 1], ["3 Flaschen", 3]]
    else
      end
    end

如果供应商选择提供 6 瓶和 12 瓶,则 form.html.erb 中仅显示 6 瓶和 12 瓶

我的_form.html.erb:

<div class="panel-body">
    <%= form_for([@wine, @wine.reservations.new]) do |f| %>
      <div class="panel-body">
        <div class="col-md-12">
          <label>Lieferdatum</label>
          <%= f.text_field :start_date, readonly: true, placeholder: "Lieferdatum", class: "form-control datepicker" %>
        </div>
        <!-- BUY SET? -->
        <div class="col-md-12 select" style="margin-top:10px">
          <div class="form-group">
            <label>Anzahl an Flaschen</label>
            <%= f.select :bottles, wine_quantity(@wine), , id: "bottles", prompt: "Auswählen...", class: "form-control" %>
        </div>
      </div>
      </div>

      <div id="preview" style="display:none">
        <table class="reservation-table">
          <tbody>
            <tr>
              <td>Preis pro Flasche</td>
              <td class="text-right"><%= @wine.price %>€</td>
            </tr>
            <tr>
              <td>Anzahl Flaschen</td>
              <td class="text-right">x <span id="reservation_bottles"></span></td>
            </tr>
            <tr>
              <td class="total">Gesamt</td>
              <td class="text-right"><span id="reservation_total"></span>€</td>
            </tr>
          </tbody>
        </table>
      </div>

      <br/>
      <%= f.submit "Bestellen", id: "btn_book", class: "btn btn-normal btn-block", disabled: true %>
    <% end %>
  </div>
</div>

<script>

$(function() 

  $.ajax(
    url: '<%= preload_wine_path(wine_id: @wine.id) %>',
    dataTyp: 'json',
    success: function(data) 

      var bottles = document.getElementById("bottles").value;
      var total = bottles * <%= @wine.price %>
      $('#reservation_bottles').text(bottles);
      $('#reservation_total').text(total.toFixed(2));

      $('#reservation_start_date').datepicker(
        dateFormat: 'dd-mm-yy',
        minDate: 2,
        maxDate: '5d',
        beforeShowDay: $.datepicker.noWeekends,
        onSelect: function(selected) 
          $('#preview').show();
          $('#btn_book').attr('disabled', false);

          $('#bottles').on('click', function() 
          var bottles = $(this).val();
          var total = bottles * <%= @wine.price %>

          $('#reservation_bottles').text(bottles);
          $('#reservation_total').text(total.toFixed(2));

          );
        
      );
    
  );
);

</script>

我的预订控制器:

class ReservationsController < ApplicationController
  before_action :authenticate_user!

  def create
    wine = Wine.find(params[:wine_id])

    if current_user == wine.user
      flash[:alert] = "Du kannst nicht deinen eigenen Wein kaufen!"
    else

    start_date = Date.parse(reservation_params[:start_date])

    @reservation = current_user.reservations.build(reservation_params)
    @reservation.wine = wine
    @reservation.price = wine.price
    @reservation.total = wine.price * bottles
    @reservation.save

    flash[:notice] = "Erfolgreich Bestellt!"
  end
    redirect_to wine
  end

  def your_orders
    @orders = current_user.reservations.order(start_date: :asc)
    @today = Date.today
  end

  def your_reservations
    @wines = current_user.wines
  end

  # Each Order Details
  def order_details
    @orders = current_user.reservations.order(start_date: :asc)
  end

  private
  def reservation_params
    params.require(:reservation).permit(:start_date)
  end

end

当我尝试在 reservations_controller.rb 中实现 :bottles 时,我得到了 NoMethodError... 我想我将输入数据从 form.html.erb 解析到了 reservations 控制器错误。

这是请求:

"utf8"=>"✓",
 "authenticity_token"=>"3xgM8kSLwa+8JafBZYMoX+BA2XgelWY5bY7yENMrtqAxXCrBb6IabNFa72BG0H6jYnszkzZ/oilOLbka2mmVUA==",
 "reservation"=>"start_date"=>"26-10-2018", "bottles"=>"3",
 "commit"=>"Bestellen",
 "wine_id"=>"3"

【问题讨论】:

【参考方案1】:

看起来您的 ajax 正在将数据发送到不同的控制器。不是将其发送到“wine”控制器的“预加载”操作吗?

<script>
$(function() 

  $.ajax(
    // updated URL
    url: '<%= reservation_path(wine_id: @wine.id) %>',
    // updated datatype field name
    dataType: 'json',
    success: function(data) 

      var bottles = document.getElementById("bottles").value;
      var total = bottles * <%= @wine.price %>
      $('#reservation_bottles').text(bottles);
      $('#reservation_total').text(total);

      $('#reservation_start_date').datepicker(
        dateFormat: 'dd-mm-yy',
        minDate: 2,
        maxDate: '5d',
        beforeShowDay: $.datepicker.noWeekends,
        onSelect: function(selected) 
          $('#preview').show();
          $('#btn_book').attr('disabled', false);

          $('#bottles').on('click', function() 
          var bottles = $(this).val();
          var total = bottles * <%= @wine.price %>

          $('#reservation_bottles').text(bottles);
          $('#reservation_total').text(total.toFixed(2));

          );
        
      );
    
  );
);
</script>

另外,在您的表单中为 wine_id 创建一个隐藏字段;注意:假设您使用的是 form_for 或 form_with

<%= form.hidden_field :wine_id, @wine.id %>

您的控制器似乎还没有接受瓶子数据,然后您可能没有正确引用它。我假设您的预订模型有一个名为瓶子的字段,其中包含整数数据。因此,这是我建议的更改:

class ReservationsController < ApplicationController
  def create
    # you don't need to look up the wine to assign it the reservations; skip this
    # wine = Wine.find(params[:wine_id])

    # TBH, I would move this into a validation check
    if current_user == wine.user
      flash[:alert] = "Du kannst nicht deinen eigenen Wein kaufen!"
      # don't you need to render the original page so the user can edit the choice?  something like:
      # render :new
    else

    # what are you using start_date for?  seems like you should remove it
    # start_date = Date.parse(reservation_params[:start_date])

    @reservation = current_user.reservations.build(reservation_params)
    # The math on reservation is more appropriate within the reservations model

    if @reservations.save
      flash[:notice] = "Erfolgreich Bestellt!"
    else
      # handle errors here
    end
  end

 ...

  private

  # you need to add bottles and wine_id to your permitted params
  def reservation_params
    params.require(:reservation).permit(:start_date, :bottles, :wine_id)
  end
end

然后为回调修改您的 Reservations 模型

class Reservation < ActiveModel
  # use a callback to update the model when fields are changed
  before_save :update_financials
  ...
  def update_financials
    total = wine.price * bottles if wine.present? && bottles.present?
    price = wine.price if wine.present?
  end
end

最后一条评论,我可能会将current_user == wine.user 移动到预订的验证检查中。这有点复杂,但简化了您的控制器并允许您更轻松地在模型中对其进行测试。看这里:https://guides.rubyonrails.org/active_record_validations.html#custom-methods

【讨论】:

看起来您的 ajax 正在将数据发送到不同的控制器。不是将其发送到“葡萄酒”控制器的“预加载”操作吗?此外,也许只是将 wine_id 嵌入到预订时提交的 ajax 数据中。我将更新编辑内容 感谢您的回答,但提供的解决方案无法完全发挥作用。 ajax 将数据发送到葡萄酒控制器的预加载操作,没错,因为我也在预订表单中使用日历,因此我在 reservations_controller.rb 中使用 start_date。我将 update_financials 添加到了 Reservation 模型,并做了一些您建议的其他小改动。现在瓶子被保存到数据库中,但问题是我不能在预订控制器中调用 wine.bottles,因此无法计算总价。我收到一个方法错误... 在调用wine控制器时如何调用reservations控制器? 用户从 Wine 控制器看到 show.html.erb,在 show.html.erb 中,_form.html.erb 是从 Reservations Controller 呈现的,wines 控制器显示来自wine(供应商),并且预订控制器显示供应商提供的选项。葡萄酒和预订之间存在关联。我会用更多信息更新我的帖子。 我还是不明白为什么你的 ajax url 会发布到 wine 控制器?它不应该发布到预订控制器吗?你能连同请求数据一起显示整个错误和回溯吗

以上是关于Rails 5:jquery用户输入数据不保存的主要内容,如果未能解决你的问题,请参考以下文章

Rails 和 JQuery-将 DB 数据加载到文本字段中以进行保存和删除

Rails 5:如何防止来自 jQuery 的多次提交

在提交失败时保持 jQuery - Rails

Grafana 5插件开发,如何保存用户数据/面板状态?

Rails 6将输入添加到页面中的列表

Rails.ajax 数据未传递给控制器