ruby-on-rails-3 - tag - select form rails
Al usar rutas poco profundas, las diferentes rutas requieren diferentes formas_para argumentos (3)
Esto es lo que se me 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é si es la mejor solución, pero parece funcionar bien.
Estoy usando Simple Form aquí, pero este 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.
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?
- Podría hacer dos formas separadas, pero eso parece desordenado.
- Tengo que configurar @customer para el backlink, 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 tiene que establecer tanto @customer_form como @customer en el nuevo método.
- Podría hacer lo que hizo este tipo y dividir el formulario entre múltiples 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?
El siguiente ejemplo:
config / routes.rb
Billing::Application.routes.draw do
resources :customers, :shallow => true do
resources :notes
end
end
rutas de rastrillo | nota grep
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 / notes / _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 / notes / 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
Me gustaría ofrecer una pequeña modificación a la solución de James:
# app/helpers/application_helper.rb
def shallow_args(parent, child)
child.try(:new_record?) ? [parent, child] : child
end
En lugar de confiar en que la acción del controlador se denomine "nueva", aunque probablemente sea el 95% de las veces, esto solo comprueba si el hijo es un nuevo registro.
Si el primer objeto de la matriz que pasa el generador de formularios es nil
, Rails CORREGIRÁ al segundo objeto solamente. Por esta razón, simplemente no configure su objeto @customer
en la acción de edición de su controlador . Si necesita acceder al objeto del cliente, @note
través de @note
.
Si usa 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.