Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails 来自具有多条记录的SimpleForm的Params哈希对于一条记录的结构与对于两条记录的结构不同_Ruby On Rails_Ruby_Simple Form_Simple Form For - Fatal编程技术网

Ruby on rails 来自具有多条记录的SimpleForm的Params哈希对于一条记录的结构与对于两条记录的结构不同

Ruby on rails 来自具有多条记录的SimpleForm的Params哈希对于一条记录的结构与对于两条记录的结构不同,ruby-on-rails,ruby,simple-form,simple-form-for,Ruby On Rails,Ruby,Simple Form,Simple Form For,我挣扎着把它理解成一个标题 我正在使用SimpleForm构建一个包含一个或多个字段集的批量编辑页面——在已构建但尚未保存的ActiveRecord模型集合中,每个记录对应一个字段集 我的表单如下所示: = simple_form_for :courses, method: :patch do |f| - @courses.each do |course| = field_set_tag do = f.simple_fields_for 'courses[]', cour

我挣扎着把它理解成一个标题

我正在使用SimpleForm构建一个包含一个或多个字段集的批量编辑页面——在已构建但尚未保存的ActiveRecord模型集合中,每个记录对应一个字段集

我的表单如下所示:

= simple_form_for :courses, method: :patch do |f|
  - @courses.each do |course|
    = field_set_tag do
      = f.simple_fields_for 'courses[]', course do |c|
        = c.input :title
        .row
          .medium-6.columns
            = c.input :start_date, as: :string, input_html: { class: 'input-datepicker' }
          .medium-6.columns
            = c.input :end_date, as: :string, input_html: { class: 'input-datepicker' }
  = f.submit 'Save', class: 'primary button'
"courses"=>{"courses"=>[{"title"=>"Course Y", "start_date"=>"2017-09-26", "end_date"=>"2017-07-31"}]}
"courses"=>{"courses"=>{"1"=>{"title"=>"Course X", "start_date"=>"2018-01-16", "end_date"=>"2018-07-30"}, "2"=>{"title"=>"Course Y", "start_date"=>"2017-09-26", "end_date"=>"2018-07-30"}}}
一条记录的params散列如下所示:

= simple_form_for :courses, method: :patch do |f|
  - @courses.each do |course|
    = field_set_tag do
      = f.simple_fields_for 'courses[]', course do |c|
        = c.input :title
        .row
          .medium-6.columns
            = c.input :start_date, as: :string, input_html: { class: 'input-datepicker' }
          .medium-6.columns
            = c.input :end_date, as: :string, input_html: { class: 'input-datepicker' }
  = f.submit 'Save', class: 'primary button'
"courses"=>{"courses"=>[{"title"=>"Course Y", "start_date"=>"2017-09-26", "end_date"=>"2017-07-31"}]}
"courses"=>{"courses"=>{"1"=>{"title"=>"Course X", "start_date"=>"2018-01-16", "end_date"=>"2018-07-30"}, "2"=>{"title"=>"Course Y", "start_date"=>"2017-09-26", "end_date"=>"2018-07-30"}}}
对于一个数组,而对于两个记录,它如下所示:

= simple_form_for :courses, method: :patch do |f|
  - @courses.each do |course|
    = field_set_tag do
      = f.simple_fields_for 'courses[]', course do |c|
        = c.input :title
        .row
          .medium-6.columns
            = c.input :start_date, as: :string, input_html: { class: 'input-datepicker' }
          .medium-6.columns
            = c.input :end_date, as: :string, input_html: { class: 'input-datepicker' }
  = f.submit 'Save', class: 'primary button'
"courses"=>{"courses"=>[{"title"=>"Course Y", "start_date"=>"2017-09-26", "end_date"=>"2017-07-31"}]}
"courses"=>{"courses"=>{"1"=>{"title"=>"Course X", "start_date"=>"2018-01-16", "end_date"=>"2018-07-30"}, "2"=>{"title"=>"Course Y", "start_date"=>"2017-09-26", "end_date"=>"2018-07-30"}}}
使用字符串整数键控哈希

当我尝试使用强参数时,这会成为一个问题。经过多次黑客攻击,我最终得到了这段代码,它可以处理多条记录,但在只提交一条记录时失败:

ActionController::Parameters
  .new(courses: params[:courses][:courses].values)
  .permit(courses: [:title, :start_date, :end_date])
  .require(:courses)
如果
参数丢失或值为空,则失败:courses
突出显示上面的
.require(:courses)

通过协调单个记录案例与多个记录案例,“解决”了该问题:

if params[:courses][:courses].is_a?(Array)
  params[:courses][:courses] = { '1': params[:courses][:courses][0] }
end

但感觉应该有一种更简单的方法

有没有更好的方法来编写这个用例的表单?我是否错过了一个具有强参数的技巧

我使用的是
rails5.0.5
simple\u form3.5.0


“但感觉应该有一种更简单的方法。”

是的,使用ajax发送单独的创建/更新请求。这可以对用户透明地完成,并提供更简单的代码和更好的用户体验

Rails为提供了
字段,\u并且
接受嵌套的属性
,这些属性可用于在单个请求中创建/更新多个子记录和父记录。但它确实需要一个将记录分组在一起的关联,即使在这种情况下,当涉及到验证时也会变得非常复杂

您希望将其设置为每个记录都有一个单独的表单:

- courses.each do |c|
  = render partial: 'courses/_form', course: c
表格中没有任何内容:

# courses/_form.haml.erb
= simple_form_for course, remote: true, html: { 'data-type' => 'json', class: 'course_form'} do |f|
  = c.input :title
  .row
    .medium-6.columns
      = c.input :start_date, as: :string, input_html: { class: 'input-datepicker' }
    .medium-6.columns
      = c.input :end_date, as: :string, input_html: { class: 'input-datepicker' }
  = f.submit 'Save', class: 'primary button'
我们不使用
js.erb
模板,而是使用
'data-type'=>'json'
并编写我们自己的处理程序,因为它更容易找到正确的表单:

$(document).on('ajax:success', '.course_form', function(event, xhr, status){
  var $form = $(this);
  alert('Course created');
  if (this.method.post) {
    // changes form to update instead.
    this.method = 'patch';
    this.action = xhr.getResponseHeader('Location');
  } 
});

$(document).on('ajax:error', '.course_form', function(event, xhr, status){
  var $form = $(this);
  // @todo display errors
});
创建控制器非常简单:

class CoursesController
  def create
    @course = Course.new(course_params)
    respond_to do |format|
      if @course.save(course_params)
        format.json { head :created, location: @course }
      else
        format.json do
          render json: {
            errors: @course.errors.full_messages
          }
        end
      end
    end
  end

  def update
    @course = Course.find(params[:id])
    respond_to do |format|
      if @course.update(course_params)
        format.json { head :ok }
      else
        render json: {
          errors: @course.errors.full_messages
        }
      end
    end
  end
end

保留您的表单,将强参数更改为:

params.require(:courses).permit(
  courses: [
    :id,
    :title,
    :start_date, 
    :end_date
  ]
)
使用此代码参数时,不应使用索引键,
@courses
只是一个数组:

# CoursesController
def new
  @courses = []
  # creating 3 items for example
  3.times do
    @courses << Course.new
  end
end

def create
  errors = false
  @courses= []
  # keep courses in the array for showing errors
  courses_params[:courses].each do |params|
    course = Course.new(params)
    @courses << course 
    unless course.valid?
      errors = true
    end
  end
  if errors
    render :new
  else
    # if no errors save and redirect
    @courses.each(&:save)
    redirect_to courses_path, notice: 'courses created'
  end
end
#课程控制器
def新
@课程=[]
#例如,创建3个项目
3.5倍

@courses事实证明,如果表单由现有的记录填充,那么“courses[]”的
f.simple_fields_…
方法仅为该字段集提供了一个ID,并且只有在这种情况下才使用映射到课程哈希的字符串ID的params结构。对于“新鲜”记录,没有ID,课程散列放在普通数组中

这段代码是在从一年到另一年“滚动”课程的背景下运行的——复制以前的课程并更改日期。这意味着每个字段集都有原始课程的ID

提交表单时,将创建一条新记录,并使用新属性进行验证,而正是这条没有ID的新记录重新填充了表单。“只在提交一门课程时才会发生”这件事是一种转移注意力的做法——这是测试场景的产物


值得注意的是:
f.simple_fields_for'courses[]”……
为新记录创建一个数组,并为现有记录的属性创建一个哈希映射ID。

有点像是在黑暗中拍摄的,但是如果你将
=f.simple_fields_替换为'courses[],course do | c |
,则使用
=f.simple_fields|u for:courses,courses do | c |
,这可能每次都会导致相同类型的参数散列。“但感觉应该有一种更简单的方法来实现。”-是的,使用ajax发送单独的创建/更新请求。但是您可以使用
.fetch
而不是
.require
,但最终还是会导致一个无法维护的混乱局面。@TomAranda我想我们已经尝试过了-它只会导致提交页面上的最后一个字段集。这是一个不错的方法,谢谢。我不认为我会用它来完成这项工作,但它在将来肯定会有用。这将是一个很好的地方做编辑的方式!遗憾的是,强参数代码仅在哈希数组的情况下有效。对于ID到属性哈希的哈希,它是无效的。无论如何谢谢你!