2012-03-19 44 views
39

Tôi đang sử dụng Biểu mẫu đơn giản tại đây, nhưng đây cũng là vấn đề với biểu mẫu Rails thông thường. Khi sử dụng các tuyến đường nông, form_for cần các đối số khác nhau tùy thuộc vào ngữ cảnh được sử dụng.Khi sử dụng các tuyến đường nông, các tuyến đường khác nhau yêu cầu các đối số form_for khác nhau

Ví dụ: Để chỉnh sửa (http://localhost:3000/notes/2/edit), _form.html.erb cần phải có simple_form_for(@note). Nhưng để tạo ghi chú mới (http://localhost:3000/customers/2/notes/new) _form.html.erb cần simple_form_for([@customer, @note]). Nếu nhận được các đối số sai, tôi sẽ nhận được lỗi không tìm thấy phương thức.

Cách tốt nhất để giải quyết vấn đề này là gì?

  • Tôi có thể tạo hai biểu mẫu riêng biệt, nhưng điều đó có vẻ lộn xộn.
  • Tôi phải đặt @customer cho liên kết ngược, nhưng tôi có thể sử dụng biến khác trong biểu mẫu (ví dụ: @customer_form) và không đặt trong phương thức chỉnh sửa và cập nhật, nhưng điều đó không nhất quán và hơi khó hiểu, vì Tôi phải đặt cả @customer_form và @customer theo phương thức mới.
  • Tôi có thể làm những gì this guy đã làm và chia nhỏ biểu mẫu trên nhiều tệp. Nó trông giống như lựa chọn tốt nhất cho đến nay, nhưng tôi không thực sự thích nó nhiều, vì bạn không thể chỉ cần mở _form.html.erb và xem những gì đang xảy ra.

Đây có phải là các tùy chọn duy nhất của tôi không?

Ví dụ sau:

config/routes.rb

Billing::Application.routes.draw do 
    resources :customers, :shallow => true do 
    resources :notes 
    end 
end 

tuyến cào | grep lưu ý

customer_notes GET /customers/:customer_id/notes(.:format)   notes#index 
        POST /customers/:customer_id/notes(.:format)   notes#create 
new_customer_note GET /customers/:customer_id/notes/new(.:format)  notes#new 
     edit_note GET /notes/:id/edit(.:format)      notes#edit 
       note GET /notes/:id(.:format)       notes#show 
        PUT /notes/:id(.:format)       notes#update 
        DELETE /notes/:id(.:format)       notes#destroy 

app/views/ghi chú/_form.html.erb

#      v----------------------------- Right here 
<%= simple_form_for (@note), html: { class: 'form-vertical'} do |f| %> 
    <%= f.input :content %> 

    <%= f.button :submit %> 
<% end -%> 

app/views/ghi chú/new.html.erb

<h1>New note</h1> 

<%= render 'form' %> 

<%= link_to 'Back', customer_path(@customer) %> 

ứng dụng/lượt xem/ghi chú/edit.html.erb

<h1>Editing note</h1> 

<%= render 'form' %> 

<%= link_to 'Show', @note %> 
<%= link_to 'Back', customer_path(@customer) %> 

app/controllers/notes_controller.rb

class NotesController < ApplicationController 

def show 
    @note = Note.find(params[:id]) 
    @customer = Customer.find(@note.customer_id) 

    respond_to do |format| 
    format.html 
    format.json {render json: @note } 
    end 
end 

    # GET /notes/new 
    # GET /notes/new.json 
    def new 
    @note = Note.new 
    @customer = Customer.find(params[:customer_id]) 

    respond_to do |format| 
     format.html # new.html.erb 
     format.json { render json: @note } 
    end 
    end 

    # GET /notes/1/edit 
    def edit 
    @note = Note.find(params[:id]) 
    @customer = Customer.find(@note.customer_id) 
    end 

    # POST /notes 
    # POST /notes.json 
    def create 
    @customer = Customer.find(params[:customer_id]) 
    @note = @customer.notes.build(params[:note]) 

    respond_to do |format| 
     if @note.save 
     format.html { redirect_to @customer, notice: 'Note was successfully created.' } 
     format.json { render json: @note, status: :created, location: @note } 
     else 
     format.html { render action: "new" } 
     format.json { render json: @note.errors, status: :unprocessable_entity } 
     end 
    end 
    end 

    # PUT /notes/1 
    # PUT /notes/1.json 
    def update 
    @note = Note.find(params[:id]) 
    @customer = Customer.find(@note.customer_id) 

    respond_to do |format| 
     if @note.update_attributes(params[:note]) 
     format.html { redirect_to @customer, notice: 'Note was successfully updated.' } 
     format.json { head :no_content } 
     else 
     format.html { render action: "edit" } 
     format.json { render json: @note.errors, status: :unprocessable_entity } 
     end 
    end 
    end 

    # DELETE /notes/1 
    # DELETE /notes/1.json 
    def destroy 
    @note = Note.find(params[:id]) 
    @note.destroy 

    respond_to do |format| 
     format.html { redirect_to :back } 
     format.json { head :no_content } 
    end 
    end 
end 
+0

Xem giải pháp của Eric bên dưới. Giải pháp đơn giản như không xác định '@ customer' trong hành động chỉnh sửa của bạn. – Andrew

Trả lời

30

Nếu đối tượng đầu tiên trong mảng bạn vượt qua trình tạo biểu mẫu là nil, Rails sẽ chỉ POST cho đối tượng thứ hai. Vì lý do này chỉ cần không đặt đối tượng@customercủa bạn trong tác vụ chỉnh sửa của bộ điều khiển. Nếu bạn cần truy cập vào đối tượng khách hàng, hãy gọi nó qua số @note.

Nếu bạn đang sử dụng cùng một phần cho mới và chỉnh sửa, bạn sẽ muốn đặt @note.customer trong hành động mới của bộ điều khiển (@customer sẽ không được đặt khi chỉnh sửa).

Tôi nghĩ đây là cách nhóm Rails dự định hoạt động.

+0

Đây là câu trả lời đúng. –

+0

Đôi khi giải pháp đơn giản đến nỗi không rõ ràng. – Andrew

+0

nhưng làm thế nào bạn có thể làm @ note.customer ?? Tôi đã cố gắng truy cập đối tượng cha mẹ (@customer) từ con (@note) bằng cách sử dụng @ note.customer, nhưng nó sẽ chỉ trả lại lớp nil ... – Sardonic

9

Đây là những gì tôi đã đưa ra:

app/giúp đỡ/application_helper.rb

module ApplicationHelper 

    # Public: Pick the correct arguments for form_for when shallow routes 
    # are used. 
    # 
    # parent - The Resource that has_* child 
    # child - The Resource that belongs_to parent. 
    def shallow_args(parent, child) 
    params[:action] == 'new' ? [parent, child] : child 
    end 

end 

app/views/notes/_form.html.erb

<%= simple_form_for shallow_args(@customer, @note), html: { class: 'form-vertical'} do |f| %> 
    <%= f.input :content %> 

    <%= f.button :submit %> 
<% end -%> 

Tôi không biết rằng đó là giải pháp tốt nhất, nhưng có vẻ như nó không hoạt động.

+4

Điều này trông giống như một giải pháp tốt, nhưng tôi thực sự muốn Rails có cái gì đó nướng trong để xử lý này. Sau khi tất cả, bất cứ ai sử dụng các tuyến đường nông cạn chắc chắn phải gặp phải vấn đề này ngay sau khi họ tạo ra hình thức đầu tiên của họ, phải không? – imderek

+1

@imderek Xem giải pháp của Eric bên dưới. Không cần một phương thức trợ giúp tùy chỉnh. – Andrew

+1

Một lợi thế mà giải pháp này có trên Eric là khớp nối thấp hơn với bộ điều khiển. Với giải pháp này, bạn được tự do gán bất kỳ biến thể hiện nào bạn muốn. –

22

tôi muốn cung cấp một sửa đổi nhỏ để James' giải pháp:

# app/helpers/application_helper.rb 
def shallow_args(parent, child) 
    child.try(:new_record?) ? [parent, child] : child 
end 

Thay vì dựa vào các hành động điều khiển được gọi là 'mới' - mặc dù nó có thể sẽ là 95% thời gian - - điều này chỉ kiểm tra nếu đứa trẻ là một kỷ lục mới.

+0

Tôi thực sự chạy vào điều này một chút trước và thay đổi kiểm tra để 'params [" # {parent.class.name.downcase} _id "]. Nil? == false', nhưng điều này có vẻ sạch hơn. – James

+0

Bravo, đây là một giải pháp tuyệt vời! – Stenerson

+0

CẢM ƠN BẠN! đã cứu tôi rất nhiều đau đầu –

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