Lorsque vous utilisez des routes peu profondes, différentes routes nécessitent des arguments form_for différents.

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?

  • Je pourrais faire deux formulaires distincts, mais cela semble désordonné.
  • Je dois configurer @customer pour le lien arrière, mais je pourrais utiliser une variable différente dans le formulaire (par exemple, @customer_form) et ne pas le définir dans les méthodes d’édition et de mise à jour, mais c’est incohérent et légèrement déroutant, doivent définir à la fois @customer_form et @customer dans la nouvelle méthode.
  • Je pourrais faire ce que ce gars a fait et diviser la forme à travers plusieurs fichiers. Cela ressemble à la meilleure option jusqu’à présent, mais je ne l’aime pas beaucoup, car vous ne pouvez pas simplement ouvrir _form.html.erb et voir ce qui se passe.

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.