Rails - 如何避免在视图中使用 hidden_fields 将值传递给控制器​​?

Rails - How to avoid using hidden_fields in the view to pass values to controller?

有什么方法可以避免 hidden_field 将视图中的值传递给控制器​​的方法?出于安全原因,我更喜欢控制器方法。不幸的是 strong_parameters 不支持值配对 @variables

EDIT 6/18 1:00 PM EST

  1. I've renamed my garages controller to appointments
  2. cars_controller no longer creates a new appointment (formally garages). A new appointment is created in the appointments_controller

我目前的结构


路线

Rails.application.routes.draw做

resources :techs, only: [:index, :show], shallow: true do
    resources :cars, only: [:new, :create]
end

资源:约会

#For searchkick
resources :cars, only: [:show] do
    collection do
        get 'search'
    end
end

root "home#index"

end

型号

tech.rb

class Tech < ActiveRecord::Base
    searchkick

    has_many :appointments
    has_many :customers, :through => :appointments
    has_many :service_menus
    has_many :services
    has_many :cars
end

service.rb

class Service < ActiveRecord::Base
    belongs_to :tech
    belongs_to :service_menu

    has_many :cars, dependent: :destroy
    accepts_nested_attributes_for :cars, :reject_if => :all_blank, :allow_destroy => true
end

car.rb

class Car < ActiveRecord::Base  
  belongs_to :service
  belongs_to :tech
  has_many :appointments 
end

appointment.rb

class Garage < ActiveRecord::Base
  belongs_to :customer
  belongs_to :tech
  belongs_to :car
end

控制器

cars_controller

def new
  @car = Car.find(params[:id])
  @tech = Tech.find(params[:tech_id])

  @appointment = Garage.new 
end

appointments_controller

def create
  @appointment = current_customer.appointments.build(appointment_params)

  if @appointment.save
    redirect_to appointments_path, notice:  "You car has been added to this appointment." 
  else
    redirect_to appointments_path, notice:  "Uh oh, an error has occured." 
  end
end

private

def appointment_params
  params.require(:appointment).permit(:tech_id, :service_id, :car_id, ...and a bunch of other keys here)
end

观看次数

cars.new.html

Please note this form passes hidden values to the appointment_controller.

Value from @car.name and other alike are not from a text_field but rather a pre-defined value based on selections from a previous page which is store in the cars db.

<%= simple_form_for(@appointment, { class: 'form-horizontal' }) do |f| %>
        <%= f.hidden_field :tech_id, value: @tech.id %>
        <%= f.hidden_field :car_id, value: @car.id %>
        <%= f.hidden_field :service_id, value: @car.service.id %>
        <%= f.hidden_field :customer_car, value: current_customer.car %>
        <%= f.hidden_field :customer_street_address, value: current_customer.street_address %>
        <%= f.hidden_field :customer_city, value: current_customer.city %>
        <%= f.hidden_field :customer_state, value: current_customer.state %>
        <%= f.hidden_field :customer_zip_code, value: current_customer.zip_code %>
        <%= f.hidden_field :service_name, value: @car.service.service_menu.name %>    
        <%= f.hidden_field :car_name, value: @car.name %>
    
        <%= **And a bunch of other hidden values here which are too long to list**  %>
    
    <%= f.submit "Add to appointment", class: 'btn btn-default' %>
    <% end %>

service.html

<%= render 'form' %>

_form.html

<%= simple_form_for @service do |f| %>
  <div class="field">

    <%= f.label "Select service category" %>
    <br>
    
    <%= collection_select(:service, :service_menu_id, ServiceMenu.all, :id, :name, {:prompt => true }) %>

    <%= f.fields_for :cars do |task| %>
      <%= render 'car_fields', :f => task %>
    <% end %>
  </div>

  <div class="links">
    <%= link_to_add_association 'Add New Car', f, :cars, class: 'btn btn-default' %>
  </div><br>

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

_car_fields.html

<div class="nested-fields">
  <div class="field">
        <%= f.label :name %><br>
        <%= f.text_field :name %><br>
        <%= f.label :hours %>
        <%= f.select :hours, '0'..'8' %>
        <%= f.label :minutes %>
        <%= f.select :minutes, options_for_select( (0..45).step(15), selected: f.object.minutes) %><br>
        <%= f.label :price %><br>
        <%= f.text_field :price, :value => (number_with_precision(f.object.price, :precision => 2) || 0) %> <br>
        <%= f.label :details %><br>
        <%= f.text_area :details %></div>
  <%= link_to_remove_association "Remove Car", f, class: 'btn btn-default' %>
  <%= f.hidden_field :tech_id, value: current_tech.id %>
  <br>
    <hr>
</div>

> 编辑 7/14 1:30 下午 EST

应用程序此特定功能的简要介绍

  1. A customer 点击了 tech 必须提供的服务列表
  2. customer 选择一项服务,例如 brakes,这是 tech 在其个人资料中列出的一项服务。
  3. brakes 的属性列在 cars db
  4. cars belongs_totechs
  5. customer 可以节省 brakes 这是 techs carappointment[=166 的属性=]
  6. 来自 tech 的大量预定义值、customer 的街道地址等...,car 已预加载到 form 用于存储在 appointments table.
  7. appointment 充当 历史记录 table。因此,如果 tech 决定修改他的任何一个 services 在这个例子中 brakesappointments tables 将保持不变刹车条目。
  8. 一旦 customer 选择了 Add to appointment 按钮,它将保存 techcustomercar 属性中的所有预定义值(在这个例子中 brakes) 到 appointments db.

另一种方法是完全摆脱 strong parameters 并执行以下操作:

def create
    @appointment = Garage.create(tech_id: @car.service.tech.id,
                                      customer_id: current_customer.id,
                                      customer_street_address: current_customer.street_address,
                                      customer_city: current_customer.city,
                                      customer_state: current_customer.state,
                                      customer_zip_code: current_customer.zip_code,
                                      customer_phone_number: current_customer.phone_number,
                                      customer_location_type: "WILL ADD LATER",
                                      customer_latitude: current_customer.latitude,
                                      customer_longitude: current_customer.longitude,                                      
                                      service_id: @car.service.id,
                                      service_name: @car.service.name,
                                      car_id: @car.id,
                                      car_name: @car.name,
                                      car_time_duration: @car.time_duration,
                                      price: @car.price,
                                      car_details: @car.details)
  
    if @appointment.save
      redirect_to techs_path, notice:  "This service has been saved." 
    elsif 
      redirect_to tech_path, notice:  "Uh oh, an error has occurred." 
    end
  end

如果您需要更多详细信息,请告诉我。

我能想到一些方法可以用来避免这种 formhidden_field 膨胀:

  • 在用户会话中的控制器之间共享数据,非常类似于电子商务应用程序中的购物车。
  • 如果您希望保留应用程序的无状态性,请创建一个模型来临时存储这些信息;这样你只需要在 form.
  • 中包含一个 hidden_field
  • 使用 JavaScript 发出请求,将数据存储在本地对象中并在需要时将它们作为 JSON 传递(使用 AngularJS 很简单)。

无论您选择哪种方法,请记住,在 Web 应用程序中存储大量状态通常是一种代码味道。您可以随时重新考虑您的应用程序,这样您就不需要保留那么多上下文。

您可以将这些内容移动到回调中,并且仅通过表单传递 customer_id 和 car_id。这样车库实例就会知道它的客户和汽车 parents,你可以这样做:

class Garage < ActiveRecord::Base
  before_create :copy_stuff

  private

  def copy_stuff
    self.customer_street_address = customer.street_address
    self.car_name = car.name
    # and so on                   
  end
end

Is there a way I can avoid the hidden_field method of passing values in the view to a controller?

您可以通过将属性 disabled: true 添加到隐藏的输入字段标签来禁用 HTML/view 中的这些字段,以实现您的要求。

不确定语法是否准确,但例如应该是这样的

f.hidden_field :tech_id, value: @tech.id, disabled: true

为了解决我的问题,我对最初 post 的最新编辑说明如下:

EDIT 6/18 1:00 PM EST

  1. I've renamed my garages_controller to appointments_controller
  2. cars_controller no longer creates a new appointment (formally garages). A new appointment is created in the appointments_controller

只有 hidden_field 我路过 约会视图中的 car_id /new.html.erb <%= f.hidden_field :car_id, value: @car.id %>.

appointments_controller 中,我分配所有 car 属性执行以下操作。

def create
    @appointment = current_customer.appointments.build(appointment_params)
    @appointment.tech_id = @appointment.car.service.tech.id
    @appointment.price = @appointment.car.price
    @appointment.car_name = @appointment.car.name
    @appointment.car_details = @appointment.car.details

    if @appointment.save
        redirect_to appointments_path, notice:  "Thank you booking your appointment." 
    else
        redirect_to appointments_path, notice:  "Uh oh, an error has occurred. Please try again or contact us for further assistance" 
    end
end

谢谢大家的回复。 我早该知道的。 :(