2012-03-19 15 views
39

Estoy usando Simple Form aquí, pero esto también es un problema con los formularios normales de Rails. Cuando se usan rutas poco profundas, form_for necesita diferentes argumentos dependiendo del contexto en que se usa.Al usar rutas poco profundas, las diferentes rutas requieren una forma diferente_para los argumentos

Ejemplo: Para editar (http://localhost:3000/notes/2/edit), _form.html.erb necesita tener simple_form_for(@note). Pero para crear una nueva nota (http://localhost:3000/customers/2/notes/new) _form.html.erb necesita simple_form_for([@customer, @note]). Si cualquiera recibe los argumentos incorrectos, obtendré un error de método no encontrado.

¿Cuál es la mejor manera de lidiar con esto?

  • Pude hacer dos formas distintas, pero eso parece desordenado.
  • Tengo que establecer @customer para el enlace posterior, pero podría usar una variable diferente en el formulario (por ejemplo, @customer_form) y simplemente no configurarlo en los métodos de edición y actualización, pero eso es inconsistente y un poco confuso, ya que Tendría que establecer tanto @customer_form como @customer en el nuevo método.
  • Pude hacer lo que this guy hizo y dividir el formulario en varios archivos. Parece la mejor opción hasta ahora, pero realmente no me gusta mucho, ya que no puede simplemente abrir _form.html.erb y ver qué está sucediendo.

¿Son estas mis únicas opciones?

ejemplo es el siguiente:

config/routes.rb

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

rutas rastrillo | grep nota

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/notas/_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/notas/new.html.erb

<h1>New note</h1> 

<%= render 'form' %> 

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

app/views/notes/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

Consulte la solución de Eric a continuación. La solución es tan simple como no definir '@ customer' en su acción de edición. – Andrew

Respuesta

30

Si el primer objeto de la matriz que pasa el generador de formularios es nil, Rails CORREGIRÁ al segundo objeto solamente. Por este motivo, simplemente no configure su objeto@customeren la acción de edición de su controlador. Si necesita acceder al objeto del cliente, llámelo a través del @note.

Si está utilizando el mismo parcial para nuevo y edita, querrá establecer @note.customer en la nueva acción del controlador (@customer no se establecerá al editar).

Creo que así es como el equipo de Rails quería que funcionara.

+0

Esta es la respuesta correcta. –

+0

A veces, la solución es tan simple que no es obvia. – Andrew

+0

pero ¿cómo podrías hacer @ note.customer ?? Intenté acceder al objeto primario (@customer) del niño (@note) usando @ note.customer, pero solo devolvería nil clase ... – Sardonic

9

Esto es lo que ocurrió:

app/helpers/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 -%> 

No sé que es la mejor solución, pero parece que funciona bien.

+4

Esto parece una solución decente, pero realmente me gustaría que Rails tuviera algo preparado para manejar esto. Después de todo, cualquiera que use rutas poco profundas sin duda debe enfrentar este problema tan pronto como creen su primera forma, ¿verdad? – imderek

+1

@imderek Vea la solución de Eric a continuación. No hay necesidad de un método de ayuda personalizado. – Andrew

+1

Una ventaja que tiene esta solución sobre Eric es un acoplamiento más bajo con el controlador. Con esta solución, puede asignar cualquier variable de instancia que desee. –

22

Me gustaría ofrecer una ligera modificación a la solución de Santiago:

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

lugar de depender de la acción del controlador se llama 'nuevo' - aunque es probable que sea el 95% del tiempo - - Esto solo comprueba si el niño es un nuevo registro.

+0

Me encontré con esto hace un momento y cambié el cheque a 'params [" # {parent.class.name.downcase} _id "]. Nil? == falso', pero esto se ve más limpio. – James

+0

¡Bravo, esta es una excelente solución! – Stenerson

+0

¡GRACIAS! me salvó un montón de dolores de cabeza –

Cuestiones relacionadas