Let's create sample rails application:
rails new sample
Add gon, select2-rails, acts-as-taggable-on gems to Gemfile. We will use that in our example. It won't hurt to remove turbolinks gem to avoid some side effects. And we should just run
bundle install --without production
//= require turbolinks from
We should create controllers, models and views for posts, so we could work with them:
bin/rails g scaffold posts title:string content:text
And, of course, database migrations:
bin/rails generate acts_as_taggable_on:migration
We will use
class Post < ActiveRecord::Base acts_as_taggable end
Our sandbox is ready now.
Editing tags with select2 and page-specific assets
Let's add tags field to an edit form:
# app/views/posts/_form.html.erb # ... <div class="field"> <%= f.label :tag_list %><br> <%= f.text_field :tag_list %> </div> # ...
We should add tags to the list of permitted attributes in
post_params method of our
# app/controllers/posts_controller.rb # ... def post_params params.require(:post).permit(:title, :content, :tag_list) end # ...
Tags editing already works now. Of course, it would be pretty handy to have a dropdown with a list of existing tags. So, let's make it look well by using select2 and we will use gon in conjunction with jbuilder (comes with rails 4 by default) to get the list of existing tags.
# app/assets/stylesheets/application.css /* *= require_self *= require select2 *= require_tree ./application */
Note, that I created a separate subfolder
application for application assets. Thanks to it, we will be able to separate asset groups, e.g. place admin assets to
We have an interesting thing here. Tags list is required just for one view file and select2 should be initialized only in
- inline js directly in view file,
- execute js only if container has specific id or class name,
- execute js, only if there is some particular element on the page.
There are pros and cons for those approaches:
- inline js looks ugly;
- js compiled into a signle file will load faster because of caching;
- js in a single file will execute some of checks on every page.
I think, I can propose prettier approach and here it is.
First of all, let's create
And it's pretty easy to include this script in our view file:
We should configure assets precompilation to make it work in production:
# config/environments/production.rb # ... config.assets.precompile += %w(specific/*)
It seems awkward to me that this is not included into assets pipeline by default.
I see some advantages of that approach:
- files are in a separate folder and well structured;
- code is executed only on the page where it is required;
- thus, there won't be tons of checks for some particular id/class/element presence on other pages.
Passing list of tags with gon and jbuilder
So, everything should work now. But wait! Where does the list of tags come from? The answer is: gon and jbuilder.
Let's add the following lines to the controller:
Note, that we pass the list of tags not only for
edit actions, but for
update actions also. In case of validation error these pages do not redirect to, but just render
edit views. That means, that we need tags list there.
We should create jbuilder template which renders json required for us
# app/views/posts/_form.json.jbuilder json.tags @tags
And include gon in the main layout before all other js includes:
# app/views/layouts/application.html.erb <%= include_gon %>