with tag rails form_with form fields_for ruby-on-rails-3 form-for

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.