Have you ever met problems with integrating the Jquery Token Input and the acts_as_taggable_on? Or are you gonna to implement these things together? Hold on! This article’ll help you: I’ll show you step by step how to do that with Rails 3.2.
Acts as taggable on
First of all, you should, of course, install acts_as_taggable_on. Add this in your gemfile:
And run these commands to install it:
1 2 3
This will install gem, create and run it’s migration to set your database properly. Next, you should attach tags to any existing model of your choice. In my case its the model Movie:
1 2 3 4 5
Interesting thing is that the
acts_as_taggable is just an alias for
acts_as_taggable_on :tags. So you can really use any other name for your tags of your own choice. Also make sure that you added
attr_accessible section to be able to save tags.
Not it’s time to create and then show tags. To do that you should add this simple code inside of your view with the form:
1 2 3 4
To show tags you can use method
tag_list which returns array of tags for a current object:
1 2 3 4 5 6 7
That should be enough to be able manage your tags with acts_as_taggable_on. Now it’s time to implement Jquery token input.
Jquery token input
To load jquery-token-input in your app you should add it’s js and css files in
vendor/assets and after that include these files inside of your js and css manifests. For example:
Please, note, that you css file may have another name (there are few themes available out the box) and it’s really okay.
Now I would like to show to you my final coffee’s code, which will do the job for my tags:
1 2 3 4 5 6 7
This code will grab and show new tags via ajax from the URL
/movies/tags.json, basing on user input. But really, a tag can’t consist of a single symbol, so I set minimum at 2 symbols to prohibit such bad behavior. Jquery token input will validate it for us.
To be able to create a new tag you should use
allowCustomEntry. Also, a movie can already have tags, so i upload data from the div using data-load tag (you can easily implement it since Rails 3.1) and pass it to the
prePopulate method. If you still not using Rails 3.1 then you can use instead of that gem gon, which I really love. And please, please upgrade Rails to the newer version!
Now it’s time to add the method in controller, which will return all founded movies tags (so app will return data on
/movies/tags.json call). First of all, add route with
1 2 3 4 5
Now I should add an actual method to the controller:
1 2 3 4 5 6 7
This code will do the job (it returns all movies tags). Note, that I use some tricks on the code above. I want to find tags of specific model (Movie), it makes sense, because I may have many tags for different models, and I want to separate model’s tags from each other. That is why I use
all_tag_counts which returns relation of tags (representing as array) for a
When the app gets all necessary tags via
all_tag_counts then it should filter them to find tags which’ll match user’s input. For that purpose I use the scope
by_tag_name which find data in received ActsAsTaggableOn’s relation. And finally, you should transform founded tags in the format, which Jquery token input will understand. If you would look up in the docs then you would see that js expects data in the very specific format:
1 2 3 4
by_tag_name scope has fired then I got such relation (as you remember):
So I should transform my data for Jquery Token Input and method
token_input_tags does it.
token_input_tags you should define the module in your
lib/ directory and after that include your module in the ActsAsTaggableOn, because the
all_tag_counts returns ActsAsTaggableOn’s relation.
Here is my module:
1 2 3 4 5 6 7 8 9 10 11
Here I define my scope and method with some help of the ActiveSupport (thank you, guys!). You may be surprised that I put in the id name of a tag, but it’s the only workaround which works for ActsAsTaggableOn (otherwise created tag will contain id instead of the name).
Now let’s tell Rails to find my libs. In config/application.rb add:
And finally, lets include our module in ActsAsTaggableOn. Create in initializers file
acts_as_taggable_on.rb and add in that file:
ActsAsTaggableOn is now having my scope and the defined method! Awesome!
But I didn’t finish. When you create few tags, save your model and then try to edit your model then you expect to see your existing tags. So, lets implement it.
As you remember, I has already defined such line of the code in my coffee’s file:
To load actual movies tags I should add my data tag in the movie’s edit form and then fill it with actual data. Here is updated form:
1 2 3 4
I didn’t just add data but also I changed the name of the field. It’s because when you create new tags then you get them in very specific format:
As you can see, new tags are wrapped in single quotes. App shouldn’t save these quotes, because user hasn’t printed them. To do that I define a virtual attribute in my model and override setter to save actual tags without quotes:
1 2 3 4 5 6 7 8 9 10
Please notice, that now you can and should remove
tag_list from attr_accessible.
All right, it’s time to populate our movies tags! For that purpose in movies_controller i added before_filter to call a new method to collect existing tags:
1 2 3 4 5 6 7 8 9
Now our app works, you can create new tags, add existing one, search them via ajax and even delete them. Thanks for your patience!
P.S. You can find working demo, it uses all these features in my github’s repo.