Using Vue in controller specific javascripts with Webpacker in Rails 5.1+

Jiří Procházka
Code Rocket
Published in
4 min readAug 18, 2017

Since new version 5.1 Rails are playing nice with modern javascripts. It is suggesting itself to use Webpack as a module bundler and Yarn as a package manager to keep up with latest frontend ‘hype’ (using ES6 etc.).

I’ve spent some time playing around and put my favorite frontend library Vue into existing project. I like Vue very much as it is a really lightweight lib and it is incrementally adoptable (it means you can start using Vue just on places you want - no need to rewrite the whole frontend from scratch. It is playing nicely with other libraries as jQuery as well).

My aim

I wanted to use Vue in views and controller specific javascripts on place where I usually would use jQuery or core javascript.

This is not about SPA (single page applications) nor using .vue components.

Example

Here is a small chunk of code to give you an idea what I mean:

# app/views/products/show.html.erb
<ul id="product-categories">
<li v-for="c in cats"><%= image_tag "cat.png" %> {{ c.name }}</li>
</ul>
# products.js
var users = new Vue({
el: '#product-categories',
data: {
cats: []
},
created() {
this.cats.push({ name: "Books" })
this.cats.push({ name: "Magazines" })
}
});

Let’s get hands dirty. I mean it

To get this working there is a lot of things to orchestrate:

  • Install Webpacker gem to get Webpack in play
  • Tell Webpacker to load Vue
  • Install vue-turbolinks Yarn package to get Vue working with Turbolinks
  • Change the path to where $ rails g controller .. command generates javascript files to the path where Webpacker expects them to be
  • Call $ yarn install automatically when the rails project is initiated

Installing Webpacker

Well, installing this gem is as easy as installing any other. Just check you have the correct version of Rails, Node.js and Yarn installed before you start.

If you are starting a new project, you can create a Rails project with Webpacker directly by $ rails new myapp --webpack .
If you are adding Webpacker into the already existing project as I was, just put this into your Gemfile: gem 'webpacker' and call $ bundle install.

When the gem is installed, initialize it with:

$ rails webpacker:install

This will create a new folder for you app/javascript (watch out, do not mingle it with app/assets/javascripts!). This is the folder where we will put all our javascript code into and where we will point the rails g controller .. command later to.

So for example instead of app/assets/javascripts/product.js file (which will be not created anymore) ProductsController specific javascript will be created to app/javascript/packs/products.js and that is the file where we will put our Vue code.

Now it is the time to insert Webpack javascript into the page with:

# app/views/layouts/application.html.erb
<head>
...
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag params[:controller] %>
</head>

Notice the new helper javascript_pack_tag which is inserting Webpack javascript for a particular controller into the page.
As a parameter it is taking a controller name, so for controller ProductController it is loading just app/javascript/packs/products.js file, for CategoriesController it will load app/javascript/packs/categories.js file and so on.

Loading Vue

To install Vue, Vue Loader and Vue Template Compiler into the project call:

$ rails webpacker:install:vue 

It will create example files:

app/javascript/packs/app.vue
app/javascript/packs/application.js
app/javascript/packs/hello_vue.js

Go them through if you want, but we won’t need this and you can delete them.

Make Vue and Turbolinks friends

Vue and Turbolinks are not good friends from the start (more on this here), but there is an easy fix — install a Yarn package called vue-turbolinks:

$ ./bin/yarn add vue-turbolinks

Later we will import this module in our javascript files.

Change the path where are the js files generated to

When you call rails g controller Products controller specific javascript file is generated into app/assets/javascripts/products.js. But we will not use this file anymore because Webpack is not able to bundle files in this location. Webpack bundles files in app/javascript folder.

Of course we can move such file each time we generate a new controller, but it is not necessary. Rails, as always, give us a way to override the convention.

$ rails g generator rails/webpacker_assets

It will generate several files, but the important for us is lib/generators/rails/webpacker_assets/webpacker_assets_generator.rb. Change the file like this:

class Rails::WebpackerAssetsGenerator < Rails::Generators::NamedBase
def create_assets_file
create_file "app/javascript/packs/#{file_name}.js", <<-FILE
your content
FILE
create_file "app/assets/stylesheets/#{file_name}.scss", <<-FILE
your content
FILE
end
end

Notice the method call create_file “app/javascript/packs/#{file_name}.js", <<-FILE on the third row. Here we are telling generator where to generate javascript files to. For other files (stylesheets) we keep it as it was before.

Calling ‘yarn install’ when initiating project

It would be nice if starting rails with $ rails s command Yarn would install all packages and dependencies automatically. It is easy as creating one file config/initializers/yarn.rb and inserting this one line inside:

system ‘yarn install’ if Rails.env.development? || Rails.env.test?

And that is it! Now let’s tray!

  1. Generate controller: $ rails g controller Products show
  2. Insert some code to view:
# app/views/products/show.html.erb
<ul id="product-categories">
<li v-for="c in cats"><%= image_tag "cat.png" %> {{ c.name }}</li>
</ul>

3. Create Vue object and fill some data:

import Vue from 'vue'
import TurbolinksAdapter from 'vue-turbolinks'
Vue.use(TurbolinksAdapter)
document.addEventListener('turbolinks:load', () => {
var element = document.getElementById('users-list')
if (element != null) {
var users = new Vue({
el: element,
data: {
cats: []
},
created() {
this.cats.push({ name: "Books" })
this.cats.push({ name: "Magazines" })
}
})
}
})

Here we are importing vue-turbolinks as I promised above and creating Vue object on turbolinks:load event. More about this here.

4. Run Rails: $ rails s

5. Run Webpack dev server: $ bin/webpack-dev-server which is serving webpack files on port :8080 (more detail info on this here).

Now when navigating to the proper route in browser you should see the list populated by categories.

If you want to support us, please hit the ❤ (recommend) button so also others can see this. Thanks!

--

--