TokenField

Build Status

This gem add helper for form_for to Ruby on Rails form_for helper for token input with jquery token input plugin for has_many and belongs_to association

http://railscasts.com/episodes/258-token-fields

http://loopj.com/jquery-tokeninput/

https://github.com/foohey/jquery-tokeninput-rails

Supported Ruby on Rails versions:

  • ~> 3.2.1
  • ~> 4.0.1
  • ~> 4.1.1
  • ~> 4.2.1

helper will render standard text field input with javascript. javascript will change standard input to token field input

Installation

Add this line to your application's Gemfile:

gem 'jquery-tokeninput-rails' # dependency
gem 'token_field'

And then execute:

$ bundle

Usage

Javascript include

//= require jquery.tokeninput

Stylesheet include

*= require token-input-facebook

belongs_to with token_input

usage for tree

we have a model Category like this

class Category < ActiveRecord::Base
  attr_accessible :name, :parent_id

  has_many :categories, :foreign_key => :parent_id
  belongs_to :parent, :foreign_key => :parent_id, :class_name => "Category"

  # format that token input use
  def to_token
   {:id => id, :name => name}
  end
end

we have action for responding for token input ajax calls, simple autocomplete

class CategoriesController < ApplicationController

  # action for autocomplete
  def token
   categories = Category.where("categories.name like ?", "%#{params[:q]}%")

   respond_to do |format|
     format.json { render :json => categories.map(&:to_token) }
   end
  end

  # rest of the class
end

in routes we have route for token ajax call

MyApplication::Application.routes.draw do
  resources :categories do
    collection do
     get :token # route for token -> token_categories_path
    end
  end
end

then in view we call token_field with param :model => :category

<%= form_for @category do |f| %>
   <%= f.token_field :parent_id, :model => :category, :token_url => token_categories_path %>
<% end %>

in case model is in namespace for example MyApp you should use :model like this

<%= form_for @category do |f| %>
   <%= f.token_field :parent_id, :model => "MyApp::Category", :token_url => token_categories_path %>
<% end %>

if you need dynamically evaluated url of token input, you can pass inline javascript function to :token_url options but with :token_url_is_function set to true

<%= form_for @category do |f| %>
   <%= f.token_field :parent_id, :model => :category, :token_url => "function(){ return '/hello' }", :token_url_is_function => true %>
<% end %>

also own separate function are supported

<%= form_for @category do |f| %>
  <%= f.token_field :parent_id, :model => :category, :token_url => "myDynamicUrl", :token_url_is_function => true %>
<% end %>

<script type="text/javascript">
  function myDynamicUrl() {
    return "/my-url/";
  }
</script>

if there would be model Parent, we can omit :model parameter. for example in Product model like this

class Product < ActiveRecord::Base
  belongs_to :category
end

we can use this code in view

<%= form_for @product do |f| %>
  <%= f.token_field :category_id, :token_url => token_categories_path %>
<% end %>

helper will allow you to enter only one element.

has_many with token_input

We can use token_input also for mapping categories to product we will use ActiveRecord method category_ids which be default return array of ids from association in model we have to change category_ids= method like this

class Product < ActiveRecord::Base
  has_many :product_has_categories
  has_many :categories, :through => :product_has_categories

  alias_method :category_ids_old=, :category_ids=
  def category_ids=(ids)
    ids = ids.split(",").map(&:to_i) if ids.is_a?(String)
    self.category_ids_old=ids
  end

  # rest of the class...
end

in view you will use attribute category_ids. token input will expected more than one category. so you can enter more than one category.

<%= form_for @product do |f| %>
  <%= f.token_field :category_ids, :token_url => token_categories_path %>
<% end %>

if you want to use multiple token inputs on page, and id of element would be the same, you can user option :append_to_id.

<%= form_for @product do |f| %>
   <%= f.token_field :category_ids, :token_url => token_categories_path, :append_to_id => :id %>
<% end %>

if @product.id is present and for example "3" it will use this id and generate id of html input like this "product_categories_ids_3" if @product.id is nil id of html input will be "product_categories_ids"

other value for :append_id option will be used like this

<%= form_for @product do |f| %>
  <%= f.token_field :category_ids, :token_url => token_categories_path, :append_to_id => 5 %>
<% end %>

id of html input will be "product_categories_ids_5"

SimpleForm support

if you use simple_form in your project, TokenInput field will be loaded. you can use it in view like this

<%= simple_form_for(@item) do |f| %>
  <%= f.input :category_id, :as => :token, :token_url => token_categories_path %>
<% end %>

all options for token_field helper can be used in simple form helper as well.

Updating from 1.x to 2.0

  • Add #to_token method to model you are using
  • Remove .token_json method, we don't use it anymore
  • require dsl helper for tests (see below)

Testing

add support for helpers in rspec

require 'token_field/capybara/dsl'

RSpec.configure do |config|
  config.include TokenField::Capybara::Dsl
end

test helpers for capybara

filling token input

fill_in "category_parent_id", :with => new_parent.id # standard input
fill_in_token "category_parent_id", :with => "wood"
fill_in_token "category_parent_id", :with => "wood", :waiting_call => lambda { sleep(4) }

clearing token input

clear_token "category_parent_id"

project is covered by integration tests. using rspec, capybara and selenium how to run test

bundle # install dependency
rake db:create
rake db:migrate RAILS_ENV=test
rspec spec/

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

Licence

This project rocks and uses MIT-LICENSE.