Using Vue in controller specific javascripts with Webpacker in Rails 5.1+
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!
- Generate controller:
$ rails g controller Products show
- 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!