Shiva Kumar 's Blog

Start small,Think Big

Tagging with jquery tokeninput and ActsAsTaggableOn gem

Tagging  has become important part of most the application,so that we can easily filter out the post or content according to the category or  type.
Today im gonna run you through how to build a tagging system.Which i recently implemented using jquery tokeninput and ActsAsTaggableOn gem .Tokeninput is jquery plugin which uses to select mulitple item from predefined list using autocompletion.And implementing tagging in rails is easy with ActsAsTaggableOn gem .Lets get started,first download the jquery-tokeninput and unzip it and add the jquery.tokeninput.js to your project app/assets/javascript and in styles folder you will find list of css styles copy it to the app/assets/stylesheets . And in your application.js include the added the javascript file //= require jquery.tokeninput and include the stylesheet to your application.css like this //= require ‘token-input-facebook’ Once your done let add the ActsAsTaggableOn gem to your gem file gem ‘acts-as-taggable-on’ and do a bundle install and do rails g acts_as_taggable_on:migration rake db:migrate which will create a two table called Tag and Tagging So the setup is done now lets get our hands dirty .. I wanted tagging  for Post .So in post.rb model add this acts_as_taggable and i want to track which user is tagging a post .In User.rb   acts_as_tagger Once your done lets add a field for user to enter the tags in posts _form.html.haml file

 %tr %td %label Tags (separated by commas) %br/ %td = form.text_field :tag_list,:id => "tag_list","data-load" =>  @posts.tags.map(&:attributes).to_json 

So we have added a text field for a user to enter tags separate by comma.Now we need to save the tag when the form is submitted so in your create action we set a tag_list and pass values to it ..And I also have added a data-load attribute which i will explain in a while .In your posts_controller.rb in create action

 def create @post =Post.new(params[:post]) respond_to do |format| if @post.save @post.tag_list={:tag_list => params[:post][:tag_list],:user => current_user} format.html { redirect_to(@post, :notice => 'Post was successfully created.') } else format.html { render :action => "new" } end end end

We have create a setter for post because instead of creating tagging on the fly .We create only when the form is been submitted.In hash we pass the tag_list params and current user .In Model Post.rb we see if the argument is hash, from the hash we get the user value and and tag_list .We gsub the tag_list param and see if the tag is already present if not we create the tag ,$1 is the first instance of the matched value .If the entered query is present in database it shows in autocomplete box If tag is not present which show with quotes which mean create a new tag .Tag name is saved in Tag table and in tagging table we store the value of the tag_id,taggable_id that is the post id and taggable_type which the model name here its Post and tagger_id which  will be the user_id

 attr_accessible :tag_list def tag_list=(arguments) return if !arguments.is_a?(Hash) user = arguments[:user] list = arguments[:tag_list] list.gsub!(/<<<(.+?)>>>/) { ActsAsTaggableOn::Tag.find_or_create_by_name(name: $1).name } if list user.tag(self, :with => list, on:  "tags") if list && user end 

This is for creating but how will be show the tag created while editing the form .For which the jquery tokeniput gives prePopulate option to pre load the data . Now that data attribute will help us to preload the data “data-load” =>  @posts.tags.map(&:attributes).to_json In your application.js file we add jquery tokeninput to give autocomplete functionality and select multiple tags

$('#tag_list').tokenInput("/tags.json", { tokenValue: "name", prePopulate: $("#tag_list").data("load"), theme: "facebook", preventDuplicates: true , noResultsText: 'No result, Hit Enter to create a new tag' })

We create a separate a controller for showing the list of tag from out database as json value because tokeninput accepts json value .In routes add a route for tag and create a separate tags controller routes.rb

resources :tags ,:only => [:index]

tags_controller.rb

 def index respond_to do |format| format.json{ render :json => tag_tokens(params[:q]) } end end 

In your application controller we create a method tag_token we find all the tag and check if the typed query is present in the database if its just added to the text field .If it not present we create a tag and send it as params when creating a form application_controller.rb

 def tag_tokens(query) @tags = ActsAsTaggableOn::Tag.all @tags = @tags.select { |v| v.name =~ /#{query}/i } if @tags.empty? [{id: "<<<#{query}>>>", name: "\"#{query}\""}] else @tags.map(&:attributes) end end 

A big thanks to ryan bates railscast episode which helped me in implements this and if you have any suggestion or in any ways to improve this code.Let me know in the comments.

, , , , , , ,

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">