3

3/13更新:
我做了一個小樣本項目,我的模型,控制器邏輯和一些形式的版本。添加多個嵌套通過複選框Rails的4個屬性(也許多種形式)



我建立一個形式,用戶可以在其中添加「任務」和「里程碑」在一起。 (即Task ='Vacuum'在Milestone ='clean House'裏面)。它基本上是一個任務/子任務類型模型,父母是「里程碑」,孩子是「任務」。

這兩個任務和里程碑都屬於「項目」....所以我想通過嵌套形式添加任務和里程碑更新行動。我正在考慮的方式是爲每個@task_template實例創建一個表單並一次更新多個表單。

我的問題是,我也試圖通過表稱爲「MilestoneTemplates」和「TaskTemplates」動態設置「首發里程碑/任務」 ......

用戶拉起「添加里程碑/任務」頁面, ,這取決於他們的項目類型,他們在複選框旁邊看到一系列預構建任務(@task_templates)&里程碑(@milestone_templates)。用戶然後選中他們想要添加的任務或里程碑旁邊的複選框。這應該創建一個預置的@ task_template.name用戶特定的任務,@ task_template.description ...等

我不能讓這甚至創造1.我用Rails 4,我想我有正確設置我的strong_params。下面是我對這個地方:

型號:

class Task < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :project 
    belongs_to :milestone 

class Milestone < ActiveRecord::Base 
belongs_to :project 
belongs_to :user 
has_many :tasks, dependent: :destroy, inverse_of: :milestone 
accepts_nested_attributes_for :tasks, allow_destroy: true 

class Project < ActiveRecord::Base 
has_many :milestones, dependent: :destroy 
has_many :tasks, dependent: :destroy 
accepts_nested_attributes_for :tasks, allow_destroy: true 
accepts_nested_attributes_for :milestones, allow_destroy: true 

#the "Starter Milestones & Tasks" 

class MilestoneTemplate < ActiveRecord::Base 
    has_many :task_templates, dependent: :destroy, inverse_of: :milestone_template 

class TaskTemplate < ActiveRecord::Base 
    belongs_to :milestone_template, inverse_of: :task_templates 

控制器:

class ProjectsController < ApplicationController 

def new_milestones 
@project = Project.find(params[:p]) 
@project.milestones.build 
@project.tasks.build 
@milestones_templates = MilestoneTemplate.where(template_id: @project.template_id) 
end 

def create_milestones 
@project.milestone_ids = params[:project][:milestones] 
@project.task_ids = params[:project][:tasks] 
@milestone = Milestone.new 
@task = Task.new 
@template = Template.find(@project.template_id) 
    if @project.update_attributes(project_params) 
    redirect_to view_milestones_path(p: @project.id) 
    flash[:notice] = "Successfully Added Tasks & Milestones" 
    else 
    redirect_to new_milestones_path(p: @project.id) 
    format.json { render json: @project.errors, status: :unprocessable_entity } 
    end 
end 

def project_params 
     params.require(:project).permit(:id, :name, 
     milestones_attributes: [:id, {:milestone_ids => []}, {:ids => []}, {:names => []}, :project_id, :user_id, 
      :name, :description, :due_date, :rank, :completed, :_destroy, 
     tasks_attributes: [:id, {:task_ids => []}, {:names => []}, {:ids => []}, :milestone_id, :project_id,  
      :user_id, :name, :description, :due_date, :rank, :completed, :_destroy]]) 
end 
end 

形式測試1:

<%= form_for @project, url: create_milestones_path(p: @project.id) do |f| %> 
    <label>Milestones</label><br> 
    <div class="row"> 
     <%= hidden_field_tag "project[names][]", nil %> 
     <% @milestones_templates.each do |m| %> 
     <%= check_box_tag "project[names][]", m.name, @milestones_templates.include?(m), id: dom_id(m)%> 
     <%= label_tag dom_id(m), m.name %> 

      <%= hidden_field_tag "project[milestone][names][]", nil %> 
      <% m.task_templates.each do |t| %> 
      <%= check_box_tag "project[milestone][names][]", t.name, m.task_templates.include?(t), id: dom_id(t) %> 
      <%= label_tag dom_id(t), t.name %> 
      <% end %> 
     <% end %> 
    </div> 
<%= f.submit %> 

表測試2(試圖提交陣列表格):

<label>Milestones</label><br> 
    <%= hidden_field_tag "project[milestone_ids][]", nil %> 
    <% @milestones_templates.each do |m| %> 
    <div> 
     <%= f.fields_for :milestones do |fm|%> 
     <%= check_box_tag "project[milestone_ids][]", @milestones_templates.include?(m), id: dom_id(m) %> 
     <%= label_tag dom_id(m), m.name %></div> 
     <%= hidden_field_tag :name, m.name %> 
     <%= hidden_field_tag "project[milestone][task_ids][]", nil %> 

     <% m.task_templates.each do |t| %> 
     <%= fm.fields_for :tasks do |ft| %> 
       <%= check_box_tag "project[milestone][task_ids][]", t.name, m.task_templates.include?(t), id: dom_id(t)%> 
       <%= label_tag dom_id(t), t.name %> 
     <% end %> 
     <% end %> 
     <% end %> 
    <% end %> 
    </div> 

根據xcskier56在評論中的請求,我已經從Chrome檢查器中添加了我的POST代碼。正如你所看到的,表格甚至不會調用Tasks,只是父級里程碑。里程碑的形式顯示出來,但任務不....

project[formprogress]:2 
project[milestone_ids][]: 
project[milestone][names]:true 
name:Milestone 1 
project[milestone][task_ids][]: 
project[milestone][names]:true 
name:Milestone 2 
project[milestone][task_ids][]: 
project[milestone][names]:true 
name:Milestone 3 
project[milestone][task_ids][]: 
project[milestone][names]:true 
name:Milestone 4 
project[milestone][task_ids][]: 
+0

請求發送到服務器的結構是什麼樣的?在(在鉻)形式數據下查看檢查器中的網絡事件。 – xcskier56 2015-03-11 22:12:21

+0

我已經更新了我的帖子和帖子請求。謝謝。 – NothingToSeeHere 2015-03-12 15:25:28

回答

5

我一直沒能測試該代碼自己,但我已經實現類似的代碼,所以思路應該是正確的。

這裏的技巧是使用each_with_index,然後將該索引傳遞給您的fields_for調用。通過這種方式,您通過複選框添加的每個額外的milestone_id將與以前的顯着不同。你可以找到here的另一個例子。

使用這種方法,您的表單應該是這個樣子:

<%= form_for @project do |f| %> 
    <% @milestones_templates.each_with_index do |milestone, index| %> 
    <br> 
    <%= f.fields_for :milestones, index: index do |fm| %> 
     <%= fm.hidden_field :name, value: milestone.name %> 
     <!-- Create a checkbox to add the milestone_id to the project --> 
     <%= fm.label milestone.name %> 
     <%= fm.check_box :milestone_template_id,{}, milestone.id %> 
     <br> 
     <% milestone.task_templates.each_with_index do |task, another_index| %> 
     <%= fm.fields_for :tasks, index: another_index do |ft| %> 
      <!-- Create a checkbox for each task in the milestone --> 
      <%= ft.label task.name %> 
      <%= ft.check_box :task_ids, {}, task.id %> 
     <% end %> 
     <% end %> 
     <br> 
    <% end %> 
    <% end %> 
    <br> 
<%= f.submit %> 
<% end %> 

# Working strong parameters. 
params.require(:project).permit(:name, :milestones => [:name, :milestone_ids, :tasks => [:task_ids] ]) 

這應該輸出與每個那些的task_template_ids嵌套的milestone_template_ids。

編輯:我忘了,如果你看一下文檔中,check_boxes需要另一個PARAM中間f.checkbox :task_ids, task.id =>f.checkbox :task_ids, {}, task.id

現在對於答案的肉。雖然這個表單確實有效,但給了足夠的手腳,我認爲你可以通過rails來自動更新你的項目並通過嵌套屬性創建你想要的所有東西,我不認爲這是一個好設計。

什麼是一個更好的設計是使用建設者類。它只是一個PORO(簡單的老紅寶石對象)。這將允許你做什麼是圍繞建設者寫好測試。所以你可以放心,它會一直工作,而且對Rails的一些改變並沒有打破它。

下面是一些僞代碼,讓你去:

ProjectsController << ApplicationController 

    def update 
    @project = Project.find(params[:id]) 
    # This should return true if everything works, and 
    result = ProjectMilestoneBuilder.perform(@project, update_params) 
    if result == false 
     # Something went very wrong in the builder 
    end 
    if result.errors.any? 
     #handle success 
    else 
     # handle failure 
     # The project wasn't updated, but things didn't explode. 
    end 
    end 

    private 

    def update_params 
    params.require(:project).permit(:name, :milestones => [:name, :milestone_ids, :tasks => [:task_ids] ]) 
    end 
end 

在/lib/project_milestone_builder.rb

class ProjectMilestoneBuilder 
    def self.perform(project, params) 
    milestone_params = params[:project][:milestones] 
    milestone_params.each do |m| 
     # Something like this 
     # Might be able to use nested attributes for this 
     # Milestone.create(m) 
    end 

    return project.update_attributes(params) 
    end 
end 

在/spec/lib/project_milestone_builder_spec.rb

descibe ProjectMilestoneBuilder do 
    # Create a template and project 
    let(:template) {FactoryGirl.create :template} 
    let(:project) {FactoryGirl.create :project, template: template} 

    # Create the params to update the project with. 
    # This will have to have dynamic code segments to get the appropriate milestone_template_ids in there 
    let(:params) { "{project: {milestones ..." }) 

    descibe '#perform' do 
    let(:result) { ProjectMilestoneBuilder.perform(project, params) } 
    it {expect(result.id).to eq project.id} 
    # ... 
    end 
end 

有了這個模式,你將得到一個非常好的封裝,可以輕鬆測試的類,它可以完成任務真是你期望它做的事情。快樂的編碼。

+0

我有一個未定義的方法'合併'爲18:Fixnum錯誤....但我做了一個示例應用程序與您的代碼和種子數據來玩。我還在應用中製作了兩個版本的表單,一個是:milestone_ids,另一個是項目[milestone_ids] [],它是:https://github.com/e-wing/sample-task-app – NothingToSeeHere 2015-03-13 15:11:03

+0

I一直在處理你的代碼,我對構建器感到非常興奮......但是當我把更新後的task_ids =>放到那裏時,我無法打開沒有錯誤的事件....事件。錯誤說「未定義的方法'milestone_template_id'爲#<里程碑:0x007fbff836a410>」...我想你打算把'milestone_id'....但它給出同樣的錯誤。 – NothingToSeeHere 2015-03-16 15:19:19

+1

我使用了milestone_template_id,因爲那就是我們需要傳遞給服務器的東西。真的,我們正在選擇里程碑和任務模板,將它們傳遞給構建者,然後從那裏創建合適的里程碑和任務。我真的不明白這個錯誤的任何原因。里程碑將milestone_template_id作爲列。 IDK。祝你好運! – xcskier56 2015-03-17 03:08:02