Giving super powers to Rails nested forms with Vue.js

It's the exponential fields bug :(

Update [2017–02–04]: I just published a live example of my code, you can also checkout it in github.

Update [2017–03–17]: Read part 2.

Probably a lot of people already saw/read @rbates episode about nested model form and get inspired by it. I'm one of them \o/

I always use accepts_nested_attributes_for in Rails and @rbates approach always worked well for me. I even tried to use some gems, cocoon and nested_form_fields, inspired by this method, but the way they were conceived didn't fit well for me. I will not explain why, first because I already forgot :), second because I'm not interested in trying to generalize the idea.

In my day-to-day work I use @rbates approach, the ActiveType gem, and some jQuery to add/remove fields, but from now on I will add vue.js to my toolbox! This I can explain why, and I’m writing this because I think that you could like it too.

First, what is my problem? Simple, I have a table with two dimensions: categories x deadlines. To make it simple, go see it working:

If you take a look at the HTML/CoffeScript it’s fairly standard nested fom. The CoffeeScript logic is to add new lines/columns and update the inputs with right indexes for categories_attributes, listener_deadlines_attributes and listener_deadline_values_attributes. Not that complicated, isn’t is? Err… I’m not sure about this… :)

The ugly comes now. This table contains deadlines for listeners, but I also need deadlines for presenters, but categories are common for both. Yeah, it’s a kind of conference system, I’m glad you asked! ;)

I though about some options, always adding a second table:

  1. Extract the fields from the table, edit them elsewhere, and use only labels for both tables;
  2. Only allow to add/delete lines from one table, making the other one read-only, facilitating the sync task;
  3. Allow add/delete from both tables, and also allow the user to change the input content, keeping both in sync;
  4. Redesign all the interface as a wizard (a friend sugestion given after I started implementing 3, and discarded due laziness)

The option 3 is the one that pleased me more and I believed that it’s implementation, with jQuery, would be easy, but once I started drawing an approach in my mind, I began to believe that my code would tend to get even worse :/

So, I decided to take a second look in vue.js. The first time I looked at it I though: “This looks like backbone.js, why would anyone use this instead of backbone?”. For my surprise, in this second look, I was enlightened: “This is much simpler than backbone, let's give it a try!”

There is already some people using vue.js as an alternative to Rails nested forms, the most popular appear to be the post written by Ryan McMahon, but I want exactly the opposite: give super powers to nested forms, aka dynamic behaviour :)

Why I like nested forms so much? They allow a really simple interaction with the server and I can benefit from ActiveRecord validations. Using other approach means write code to send request, verify if there is any error, do this, do that, blah blah blah… I just want to render the same page with the errors outputted by the form helpers. I find this interaction so simple, and so powerful, that I want to keep it. I just want to add some dynamic behaviour!

Okay, talk is cheap, show me the code:

I just find the final result wonderful! It's incredible more simple and more focused. Vue.js allowed me to concentrate much more in the interaction between user and data.

There are some processing that I'm still doing in JavaScript, like update the index used by the nested fields, but I consider this a small annoyance, compared to the improvement in code clarity. I also believe that these annoyances can be eliminated with some tweaks in Rails form helpers, but for now I'm over!

Happy coding!