使用rails和angularjs更新深层嵌套模型

时间:2015-07-15 06:42:16

标签: ruby-on-rails angularjs

我已经非常研究过这个问题,没有专门针对深度嵌套模型的解决方案。我想出了以下修复方法,想知道是否有更好的方法。

我试图将其简化为几乎可以解释问题和我的解决方案。

问题是在rails中存在以下ActiveRecord模型:

深层嵌套对象树

class Template
has_many :sections, dependent: :destroy
accepts_nested_attributes_for :sections
has_many :columns, through: :sections, dependent: :destroy
has_many :fields, through: :columns, dependent: :destroy

class Section
belongs_to :template
has_many :columns, dependent: :destroy
accepts_nested_attributes_for :columns

class Column
belongs_to :section
has_many :fields, dependent: :destroy
accepts_nested_attributes_for :fields

class Field
belongs_to :column‏

并且在角度上,目标是将单个ngResource $ resource调用发送到'templates /:id'并更新整个子链。 (在模板创建过程中,每个链的一部分都是先创建的。当模板最终确定时,需要进行统一更新。)

### ClassFactory.coffee ###
#
# Create a connection to the API
# ...
    return $resource('api/:class/:id', { format: 'json' }, {...})
# ...

### EditTemplateController.coffee ###
#
# Create angular template resource, save it, and redirect to editing view
# ...
    $scope.template = new ClassFactory({class: 'templates'})
    $scope.template.$save({class: 'templates'}, (res)->
        $location.path('templates/'+res.id+'/edit')
    )
#
# Update the angular object
# ...
    $scope.saveTemplate = ->
        $scope.template.$update({class: 'templates', id: $scope.template.id})
#...

### templates_controller.rb ###
#
# Create a single DB interaction with deliberately declared parameters and nested *_attributes parameters
# ...
  def update
    params[:template][:sections_attributes] = params[:sections]
    params[:template][:sections_attributes].each do |paramSection|
      paramSection[:columns_attributes] = paramSection[:columns]
      paramSection[:columns_attributes].each do |paramColumn|
        paramColumn[:fields_attributes] = paramColumn[:fields]
      end
    end
    template = current_user.templates.find(params[:id])
    template.update_attributes(allowed_params)
    head :no_content
  end

  private
    def allowed_params
      params.require(:template).permit(
        :name, sections_attributes: [
          :id, :name, columns_attributes: [
            :id, :fields_attributes: [
              :id, :name, :value
            ]
          ]
        ]
    end
# ...

据我所知,修复方法是声明* _attributes,如上所示:

params[:template][:sections_attributes] = params[:sections]

因为角度无法发送rails正在寻找的参数格式。

这显然感觉像是一个hacky解决方案。使用angularjs时,没有更好的方法来处理深层嵌套的rails模型吗?

1 个答案:

答案 0 :(得分:1)

正如this Rails github issue中所讨论的,AngularJS $资源发送参数的方式与使用accepts_nested_attributes_for时Rails预期的方式相关,这是一个公认的问题。

根据该问题,并且在Rails修复中解决此问题之前,上面的示例中可以更改以将其部分分开以便更易于管理:

添加到任何模型使用accepts_nested_attributes_for

的rails控制器
class ModelController < ApplicationController
  nested_attributes_names = Model.nested_attributes_options.keys.map do |key| 
    key.to_s.concat('_attributes').to_sym
  end

  wrap_parameters include: Model.attribute_names + nested_attributes_names

  # ...
end

在保存模型之前,通过将嵌套的*_attributes声明移动到AngularJS控制器来清理Rails控制器更新方法:

$scope.saveTemplate = ->
    $scope.template.sections_attributes = $scope.template.sections
    $scope.template.sections_attributes.forEach((section)->
        section.columns_attributes = section.columns
        section.columns_attributes.forEach((column)->
            column.fields_attributes = column.fields
        )
    )
    $scope.template.$update({class: 'templates', id: $scope.template.id})

它并不漂亮,但在修补这个特定问题之前,似乎可以做的就是这一切。