Daniel May
Daniel May

Reputation: 2262

How to customise form_for in Rails with different routes?

Problem domain

Routing

In my original approach, I had something like:

  resources :projects do
    resources :milestones
  end

So all the RESTful routes looked like: projects/PROJ_ID/milestones/ID

This was getting a bit long for my liking and you don't need the Project ID all the time: only just when you create the Milestone.

Now, the routes look like:

  resources :projects do
    resources :milestones, :only => [:new, :create]
  end

  resources :milestones, :except => [:new, :create]

When I create a new Milestone (first route):

With other Milestone operations (second route):

Problem: Rendering __form.html.erb

All is fine, except when I get to the views for new.html.erb and edit.html.erb for Milestone. These render _form.html.erb.

Original approach (all resources nested)

<%= form_for([@project, @milestone]) do |f| %>
  ...
<% end %>

Due to nested resource, we need to have Project and Milestone as parameters.

The same code above works fine for new and edit. because the original approach assumed Project/Milestone nested routing for all REST operations.

Current approach (only create/new nested)

We want _form.html.erb to look like this when we are creating a new Milestone:

<%= form_for([@project, @milestone]) do |f| %>
  ...
<% end %>

And for all other REST operations on a Milestone, we want _form.html.erb to look like this:

<%= form_for(@milestone) do |f| %>
  ...
<% end %>

So ...

Current solution is not pretty:

It's not DRY, it's messy and it feels like there should be a more elegant way.

Would appreciate any input. Very much still coming up to speed with Rails. Thanks in advance ;)

Upvotes: 2

Views: 450

Answers (2)

redronin
redronin

Reputation: 723

I would normally put the contents of the form in the partial, and write out the form_for in each file.

update:

<%= form_for @milestone do |f| %>
  <%= render :partial => 'form', :locals => {:f => f} %>
  <%= f.submit "Update my object" %>
<% end %>

create:

<%= form_for [@project, @milestone] do |f| %>
  <%= render :partial => 'form', :locals => {:f => f} %>
  <%= f.submit "Create Object" %>
<% end %>

as it's usually the form fields don't change, but button text, form ids, etc., etc. might be different. Also gives you flexibility to add additional fields, text., etc.

Upvotes: 2

Mikhail Nikalyukin
Mikhail Nikalyukin

Reputation: 11967

You can pass variable with you resource to _form partial. For example:
New:

render "form", :resource => [@project, @milestone]

Edit:

render "form", :resource => @milestone

Inside the form

<%= form_for resource do |f| %>
  ...
<% end %>

Upvotes: 4

Related Questions