2015-10-07 13 views
7

CẬP NHẬT: Tôi đang cố gắng thêm/xóa các trường biểu mẫu vào một biểu mẫu lồng nhau liên quan đến nhiều mô hình. Tôi đã nhìn thấy "Các hình thức động" được railscast bởi Ryan Bates và tôi đã gọi this article bằng cách sử dụng Cocoon Gem. Sau bài viết đó đã làm mọi thứ hoạt động hoàn hảo ngoại trừ child_index. Child_index chỉ xuất hiện trên trường nhập :kid đầu tiên (:name) và các trường nhập :pet đầu tiên (:name:age). Sau đó, nó quay trở lại một mã thông báo xác thực cho các trường được thêm vào.Rails 4: Thêm child_index vào các trường biểu mẫu được thêm động (lồng nhau) với Cocoon Gem

Tôi đã xóa tất cả các phương thức JS và trình trợ giúp và thay vào đó tôi sử dụng một số phương thức Cocoon đã được xây dựng trong JS.

Tôi đã khắc phục sự cố khi nhấp vào "Thêm" sẽ thêm hai trường thay vì một trường bằng cách xóa = javascript_include_tag :cocoon khỏi tệp application.html.erb.

Tôi đã thử thêm jQuery và người trợ giúp biểu mẫu nhưng tôi không chắc chắn tôi đã nhập mã chính xác chưa.

(Tôi đã thay đổi các đối tượng mô hình để làm cho mối quan hệ rõ ràng hơn)

parent.rb file:

class Parent < ActiveRecord::Base 

has_many :kids 
has_many :pets, through: :kids # <<<<<< ADDED KIDS USING 'through:' 

kid.rb tập tin: file

class Kid < ActiveRecord::Base 

belongs_to :parent 
has_many :pets 
accepts_nested_attributes_for :pets, reject_if: :all_blank, allow_destroy: true 
validates :name, presence: true 

pet.rb :

class Pet < ActiveRecord::Base 

belongs_to :kid 

validates :name, presence: true 

validates :age, presence: true 

T của ông là tập tin _form.html.erb tôi:

<%= form_for @parent do |f| %> 
    <% if @parent.errors.any? %> 
    <div class="alert alert-danger"> 
    <h3><%= pluralize(@student.errors.count, 'Error') %>: </h3> 

     <ul> 
      <% @student.errors.full_messages.each do |msg| %> 
       <li><%= msg %></li> 
      <% end %> 
     </ul> 
    </div> 
<% end %> 

    <div class="inline"> 
    <div> 
     <%= f.fields_for :kids do |kid| %> 
     <%= render 'kid_fields', f: kid %> 
     <% end %> 
      <div> 
      <%= link_to_add_association "Add Kid", f, :kids, id: 'add_kid', 
      'data-association-insertion-method' => 'before', 
      'data-association-insertion-traversal' => 'closest' %> 
      </div> 
     <% end %> 
    </div> 


    </div> 
     <div class="form-actions"> 
      <%= f.submit 'Create Parent', class: 'btn btn-primary' %> 
     </div> 

<% end %> 

Đây là tập tin _kid_fields.rb tôi:

<div class="nested-fields"> 

    <div class="kid-fields inline"> 
     <%= f.hidden_field :_destroy, class: 'removable' %> 
     <%= f.text_field :name, class: 'form-control', placeholder: 'Kid's Name', id: 'kid-input' %> 
     <div> 
     <%= link_to_remove_association 'Remove Kid', f %> 
     </div> 


     <%= f.fields_for :pets do |pet| %> 
     <%= render 'pet_fields', f: pet %> 
     <% end %> 
     </div>  
     <div> 
     <%= link_to_add_association "Add Pet", f, :pets, id: 'add_pet', 
      'data-association-insertion-method' => 'before' %> 
     </div> 
    </div> 

Đây là tập tin _pet_fields.rb tôi:

<div class="nested-fields"> 
    <div class="pet-fields"> 
     <%= f.hidden_field :_destroy, class: 'removable' %> 
     <%= f.text_field :name, placeholder: 'Pet Name', id: 'pet-name-input' %> 
     <%= f.text_field :age, placeholder: 'Pet Age', id: 'pet-age-input' %> 
     <%= link_to_remove_association 'Remove Pet', f, id: 'remove_pet' %> 
    </div> 
    </div> 

Trả lời

7

khi Tôi nhấp vào "Xóa sinh viên" để xóa mọi trường phía trên liên kết đó

Đây là một vấn đề nổi tiếng với RailsCast cụ thể mà bạn đang theo dõi (nó đã lỗi thời). Có một here:

enter image description here

Vấn đề đi xuống đến các child_index của fields_for references.

Mỗi khi bạn sử dụng fields_for (đó là những gì bạn đang sao chép với chức năng javascript ở trên), nó gán id cho mỗi bộ trường mà nó tạo. Những ids được sử dụng trong các params để tách các thuộc tính khác nhau; chúng cũng được gán cho mỗi trường dưới dạng HTML "id" property.

Do đó, sự cố bạn gặp phải là vì bạn không cập nhật child_index mỗi khi bạn thêm trường mới, tất cả đều giống nhau.Và vì trình trợ giúp link_to_add_fields của bạn không cập nhật JS (IE cho phép bạn chắp thêm các trường với chính xác child_index), điều này có nghĩa là bất cứ khi nào bạn "xóa" một trường, nó sẽ chọn tất cả chúng.


Khắc phục sự cố này là đặt child_index (Tôi sẽ giải thích bên dưới).

Tôi muốn cung cấp cho bạn mã mới hơn là chọn các công cụ lỗi thời của bạn thành thật.

tôi đã viết về vấn đề này ở đây (mặc dù nó có thể được đánh bóng một chút): Rails accepts_nested_attributes_for with f.fields_for and AJAX

Có đá quý mà làm điều này cho bạn - một gọi là Cocoon là rất phổ biến, mặc dù không phải là một "plug and play" giải pháp nhiều người nghĩ rằng nó là.

Tuy nhiên, cách tốt nhất là biết điều đó tất cả các công trình, ngay cả khi bạn lựa chọn để sử dụng một cái gì đó giống như Cocoon ...


fields_for

Để hiểu được giải pháp, bạn phải nhớ rằng Rails tạo HTML biểu mẫu.

Bạn biết điều này có thể; nhiều người thì không.

Điều quan trọng là bởi vì khi bạn nhận ra rằng HTML hình thức phải tuân thủ tất cả các hạn chế áp đặt bởi HTML, bạn sẽ hiểu rằng Rails không phải là ảo thuật rất nhiều folks dường như để suy nghĩ.

Cách để tạo ra một hình thức "lồng" (mà không Add/Remove) chức năng như sau:

#app/models/student.rb 
class Student < ActiveRecord::Base 
    has_many :teachers 
    accepts_nested_attributes_for :teachers #-> this is to PASS data, not receive 
end 

#app/models/teacher.rb 
class Teacher < ActiveRecord::Base 
    belongs_to :student 
end 

Something quan trọng cần lưu ý là accepts_nested_attributes_for của bạn nên được trên mô hình mẹ. Đó là, các mô hình bạn đang đi qua dữ liệu (không phải là dữ liệu một nhận):

thuộc tính lồng nhau cho phép bạn lưu các thuộc tính trên các hồ sơ liên quan qua phụ huynh

#app/controllers/students_controller.rb 
class StudentsController < ApplicationController 
    def new 
     @student = Student.new 
     @student.teachers.build #-> you have to build the associative object 
    end 

    def create 
     @student = Student.new student_params 
     @student.save 
    end 

    private 

    def student_params 
     params.require(:student).permit(:x, :y, teachers_attributes: [:z]) 
    end 
end 

Với những đối tượng xây dựng, bạn có thể sử dụng chúng trong hình thức của bạn:

#app/views/students/new.html.erb 
<%= form_for @student do |f| %> 
    <%= f.fields_for :teachers |teacher| %> 
     <% # this will replicate for as many times as you've "built" a new teacher object %> 
     <%= teacher.text_field ... %> 
    <% end %> 
    <%= f.submit %> 
<% end %> 

Đây là một sta biểu mẫu ndard sẽ gửi dữ liệu đến bộ điều khiển của bạn, sau đó đến mô hình của bạn. Phương thức accepts_nested_attributes_for trong mô hình sẽ chuyển các thuộc tính lồng nhau cho mô hình phụ thuộc.

-

Điều tốt nhất để làm việc này là để lưu ý các id cho các trường lồng nhau đoạn mã trên tạo ra. Tôi không có bất kỳ ví dụ nào trong tay; nó sẽ cho bạn thấy các lĩnh vực lồng nhau có tên như teachers_attributes[0][name] vv

Điều quan trọng cần lưu ý là [0] - đây là child_index đóng một vai trò quan trọng trong chức năng bạn cần.


động

Bây giờ cho các hình thức năng động.

Phần đầu tiên tương đối đơn giản ... xóa trường là trường hợp xóa trường khỏi DOM. Chúng tôi có thể sử dụng child_index cho rằng, vì vậy đầu tiên chúng ta cần phải biết làm thế nào để thiết lập các chỉ số con vv vv vv ...

#app/models/Student.rb 
class Student < ActiveRecord::Base 
    def self.build #-> non essential; only used to free up controller code 
     student = self.new 
     student.teachers.build 
     student 
    end 
end 

#app/controllers/students_controller.rb 
class StudentsController < ApplicationController 
    def new 
     @student = Student.build 
    end 

    def add_teacher 
     @student = Student.build 
     render "add_teacher", layout: false 
    end 

    def create 
     @student = Student.new student_params 
     @student.save 
    end 

    private 

    def student_params 
     params.require(:student).permit(:x, :y, teachers_attributes: [:z]) 
    end 
end 

Bây giờ cho các quan điểm (lưu ý bạn phải chia mẫu của bạn vào partials):

#app/views/students/new.html.erb 
<%= form_for @student do |f| %> 
    <%= f.text_field :name %> 
    <%= render "teacher_fields", locals: {f: f} %> 
    <%= link_to "Add", "#", id: :add_teacher %> 
    <%= f.submit %> 
<% end %> 

#app/views/_teacher_fields.html.erb 
<%= f.fields_for :teachers, child_index: Time.now.to_i do |teacher| %> 
    <%= teacher.text_field ....... %> 
    <%= link_to "Remove", "#", id: :remove_teacher, data: {i: child_index} %> 
<% end %> 

#app/views/add_teacher.html.erb 
<%= form_for @student, authenticity_token: false do |f| %> 
    <%= render partial "teacher_fields", locals: {f:f} 
<% end %> 

Điều này nên hiển thị các biểu mẫu khác nhau cho bạn, bao gồm fields_for. Lưu ý rằng child_index: Time.now.to_i - điều này đặt một ID duy nhất cho mỗi fields_for, cho phép chúng tôi phân biệt giữa mỗi trường theo nhu cầu của bạn.

Làm này động sau đó đi xuống đến JS:

#config/routes.rb 
resources :students do 
    get :add_teacher, on: :collection #-> url.com/students/get_teacher 
end 

Sử dụng tuyến đường này cho phép chúng tôi gửi một yêu cầu Ajax (để có được một lĩnh vực mới):

#app/assets/javascripts/.....coffee 
$ -> 

    #Add Teacher 
    $(document).on "click", "#add_teacher", (e) -> 
     e.preventDefault(); 

     #Ajax 
     $.ajax 
     url: '/students/add_teacher' 
     success: (data) -> 
      el_to_add = $(data).html() 
      $('#subscribers').append(el_to_add) 
     error: (data) -> 
      alert "Sorry, There Was An Error!" 

    #Remove Teacher 
    $(document).on "click", "#remove_teacher", (e) -> 
     e.preventDefault(); 

     id = $(this).data("i") 
     $("input#" + i).remove() 
+1

Would Tôi chỉ làm điều tương tự cho lớp như tôi đã làm cho Sư phụ? Tôi có một phiên bản hơi làm việc mặc dù tôi _do_ cần phải có một child_index tham gia một nơi nào đó bởi vì tôi nghĩ rằng nó chỉ là chỉ định một thẻ xác thực hơn là một id. – BB123

+0

Vì vậy, tôi đã theo [ví dụ này] (https://hackhands.com/building-has_many-model-relationship-form-cocoon/) bằng [Cocoon Gem] (https://github.com/nathanvda/cocoon) và mọi thứ dường như hoạt động tốt, ngoại trừ khi tôi nhấp vào liên kết "Thêm", nó thêm hai trường thay vì chỉ một. Tôi nghĩ rằng nó có thể là một cái gì đó đang xảy ra với bộ điều khiển của tôi nhưng tôi không chắc chắn. Tôi cũng không sử dụng bất kỳ phương thức JS hoặc Trình trợ giúp nào. Mối quan tâm chính của tôi là các trường không cho tôi một child_index. Thay vào đó nó vẫn cho tôi những gì tôi nghĩ là một mã thông báo xác thực (một loạt các số ngẫu nhiên). @richpeck – BB123

+0

Nó có thể sẽ là một vấn đề turbolinks. Hãy thử làm mới trang và thực hiện lại - nếu nó tiếp tục gửi hai yêu cầu, chúng tôi sẽ phải tìm ra các ràng buộc JS của bạn –

-1
add this in your js.coffe file 
$(document).on 'click', 'form .remove_', (event) -> 
$(this).prev('input[type=hidden]').val('1') 
$(this).closest('fieldset').hide() 
event.preventDefault() 

$(document).on 'click', 'form .add_teacher', (event) -> 
event.preventDefault() 
time = new Date().getTime() 
regexp = new RegExp($(this).data('id'), 'g') 
$(this).before($(this).data('fields').replace(regexp, time)) 
+0

Bạn cần giải thích cách triển khai giải pháp và chủ yếu là logic để làm theo để khắc phục sự cố. Không chỉ sao chép và dán mã. https://github.com/ryanb/railscasts-episodes/blob/master/episode-196/revised/questionnaire-after/app/assets/javascripts/surveys.js.coffee –

Các vấn đề liên quan