J’utilise Simple Form ici, mais c’est aussi un problème avec les formulaires Rails normaux. Lors de l’utilisation de routes peu profondes, form_for a besoin d’arguments différents selon le contexte dans lequel il est utilisé.
Exemple: Pour éditer ( http://localhost:3000/notes/2/edit
), _form.html.erb doit avoir simple_form_for(@note)
. Mais pour créer une nouvelle note ( http://localhost:3000/customers/2/notes/new
), _form.html.erb nécessite simple_form_for([@customer, @note])
. Si l’un ou l’autre reçoit des arguments incorrects, j’obtiendrai une méthode non trouvée d’erreur.
Quelle est la meilleure façon de gérer cela?
Sont-ce mes seules options?
Exemple suivant:
config / routes.rb
Billing::Application.routes.draw do resources :customers, :shallow => true do resources :notes end end
routes de râteau | note 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
app / views / notes / new.html.erb
New note
app / views / notes / edit.html.erb
Editing note
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
Si le premier object du tableau que vous transmettez est le constructeur de formulaire, Nails ne sera affiché que sur le second object. Pour cette raison , ne définissez simplement pas votre object @customer
dans l’action d’édition de votre contrôleur . Si vous avez besoin d’accéder à l’object client, appelez-le via @note
.
Si vous utilisez le même partiel pour le nouveau et l’édition, vous devrez définir @note.customer
dans la nouvelle action du contrôleur ( @customer
ne sera pas défini lors de l’édition).
Je pense que c’est comme ça que l’équipe Rails a voulu que ça marche.
Je voudrais apporter une légère modification à la solution de James:
# app/helpers/application_helper.rb def shallow_args(parent, child) child.try(:new_record?) ? [parent, child] : child end
Au lieu de compter sur l’action du contrôleur appelée “nouvelle” – bien que cela sera probablement 95% du temps – cela vérifie simplement si l’enfant est un nouvel enregistrement.
Voici ce que j’ai imaginé:
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 -%>
Je ne sais pas que c’est la meilleure solution, mais cela semble fonctionner correctement.