form_for-with-custom-route-parameters:-the-worst-ever

update 12/3/2016: I think this is still annoying, but probably could tackle it much faster than I did the first time

There doesn’t seem to be anything about this in the rails world.

I rolled my own blog on rails (#bigMistake!). Rolling my own blog is something I vowed never to do, but I did because I didn’t want to configure custom DNS along with matching a wordpress or ghost site’s CSS styling to my rails site. CSS styling sometimes makes me want to hurt people. But I think I’ve finally nailed it, but it was really annoying. Hi Here’s the problem.

www.clashprogress.com/posts/4
is NOT search engine friendly.

The solution to this is to create a database field called slug and to then customize your parameters to read from slug. Super easy.

resources :posts, param: :slug

This makes my routes super friendly, with whatever I type in the slug field.

www.clashprogress.com/posts/4 becomes
www.clashprogress.com/posts/awesome-post-about-barbarians

Google freakin LOVES me. Rails freakin HATES me.

At this point I’ve got a restful posts controller and routes working correctly with the normal params :id. But now I’ve updated my routes to run off of params: :slug. It all starts going bad when I go to create a new post. I type localhost:3000/posts/new into my browser and I get the following error….

= form_for(@post, url: post_path(@post)) do

No route matches {:action=>"show", :controller=>"posts", :slug=>nil} missing required keys: [:slug]

So now I’m all of a sudden hitting the show controller action because my routes have been updated. It’s a combination of my @post = Post.new() in my controller. I didn’t instantiate a slug. But the other problem is it wants to hit update endpoint and not create. That’s because when I updated my routes, it changed how the routes worked. If I instantiate a slug in my controller rails will puke when I hit submit.

I want rails to work all it’s magic on both create and update. I want 1 form, not two.

I ended up doing this to my form tag.

- if params["action"] == "new"
  = formfor(@post, url: postspath(@post)) do |f|
    = render "postsformfields", locals: {f: f}
- else
  = formfor(@post, url: postpath(@post.slug)) do |f|
    = render "postsformfields", locals: {f: f}

I then created a partial called _posts_form_fields.html.haml for all of my form fields.

= locals[:f].label(:title)
= locals[:f].text_field(:title)
= locals[:f].label(:slug)
= locals[:f].text_field(:slug)
= locals[:f].submit

And when you pass in information to a partial, to access it you need to use locals[:param_name] to access it.

Post Content