How to use jquery-Tokeninput and Acts-as-taggable-

2019-01-21 15:05发布

This is how you use autocomplete with jQuery Tokeninput and ActsAsTaggableOn.

In my situation i am using a nested form but it shouldnt matter. Everything below is code that works.

Code

Product Model:

attr_accessible :tag_list # i am using the regular :tag_list
acts_as_taggable_on :tags # Tagging products

Products Controller:

  #1. Define the tags path
  #2. Searches ActsAsTaggable::Tag Model look for :name in the created table.
  #3. it finds the tags.json path and whats on my form.
  #4. it is detecting the attribute which is :name for your tags.

def tags 
  @tags = ActsAsTaggableOn::Tag.where("tags.name LIKE ?", "%#{params[:q]}%") 
  respond_to do |format|
    format.json { render :json => @tags.map{|t| {:id => t.name, :name => t.name }}}
  end
end

Routes:

# It has to find the tags.json or in my case /products/tags.json
get "products/tags" => "products#tags", :as => :tags

Application.js:

$(function() {
  $("#product_tags").tokenInput("/products/tags.json", {
    prePopulate:       $("#product_tags").data("pre"),
    preventDuplicates: true,
    noResultsText:     "No results, needs to be created.",
    animateDropdown:   false
  });
});

Form:

<%= p.text_field :tag_list,
                 :id => "product_tags",
                 "data-pre" => @product.tags.map(&:attributes).to_json %>

Issue 1(SOLVED)


Must have the line:

format.json { render :json => @tags.collect{|t| {:id => t.name, :name => t.name }}}

Note - You can use @tags.map here as well and you dont have to change the form either.

Below are the 2 issues on why you needed to do this:

I have the following Tag: {"id":1,"name":"Food"}. When I save a Product, tagged "Food", it should save as ID: 1 when it searches and finds the name "Food". Currently, it saves a new Tag with a new ID that references the "Food" ID, i.e. {"id":19,"name":"1"}. Instead, it should be finding the ID, showing the name, and doing a find_or_create_by so it doesn't create a new Tag.


Issue 2(SOLVED)


When I go to products/show to see the tags by doing <%= @product.tag_list %>. The name appears as "Tags: 1", when it really should be "Tags: Food".

How can I fix these issues?

6条回答
戒情不戒烟
2楼-- · 2019-01-21 15:38

I had problems with editing the tags if for example the model failed to validate,

I changed

<%= p.text_field :tag_list,
             :id => "product_tags",
             "data-pre" => @product.tags.map(&:attributes).to_json %>

to

<%= p.text_field :tag_list, 
             :id => "product_tags", 
             "data-pre" => @product.tag_list.map {|tag| {:id => tag, :name => tag } }.to_json %>

If the form failed to validate on first submission, it was creating tags as the ID's of the tags it had created on subsequent submissions.

查看更多
女痞
3楼-- · 2019-01-21 15:44

Two notes: if you're getting the tags changed by numbers on the POST request, use:

tokenValue:        "name"

And if you're trying to add non-existent tags, use (undocumented):

allowFreeTagging:  true
查看更多
兄弟一词,经得起流年.
4楼-- · 2019-01-21 15:49

I don't know if this is the entirety of your error, but you are not hitting the proper URL with the tokenInput plugin.

This

$("#product_tag_list").tokenInput("/products/tags.json"), {

should be

$("#product_tag_list").tokenInput("/products.json"), {

As I said, I don't know if this is the only problem you are having, but if you change this, does it work?

EDIT:

I have never used ActsAsTaggableOn. Does it create a Tag model for you to use?

From the looks of it on github, if you wanted to query all tags, you might have to use its namespace as opposed to just Tag, meaning ActsAsTaggableOn::Tag. For example, you can see how they access Tags directly in some of the specs.

查看更多
做个烂人
5楼-- · 2019-01-21 15:58

You should define a route in your routes.rb which should handle products/tags path. You can define it like:

get "products/tags" => "products#tags", :as => :tags

Thus should give you a tags_path helper which should evaluate to /products/tags. This should get rid of the errors you mentioned in the question. Be sure to add this route before defining resources :product in your routes.rb

Now onto acts-as-taggable-on, I haven't used this gem, but you should look at method all_tag_counts documentation. Your ProductsController#tags method will need some changes on the following lines. I am not sure if its exactly what would be required, as I use Mongoid and can't test it out.

def tags
  @tags = Product.all_tag_counts.(:conditions => ["#{ActsAsTaggableOn::Tag.table_name}.name LIKE ?", "%#{params[:q]}%"])
  respond_to do |format|
    format.json { render :json => @tags.collect{|t| {:id => t.name, :name => t.name } }
  end  
end
查看更多
我欲成王,谁敢阻挡
6楼-- · 2019-01-21 16:01

little add-on:

If you want to create the tags on the fly, you could do this in your controller:

 def tags
    query = params[:q]
    if query[-1,1] == " "
      query = query.gsub(" ", "")
      Tag.find_or_create_by_name(query)
    end

    #Do the search in memory for better performance

    @tags = ActsAsTaggableOn::Tag.all
    @tags = @tags.select { |v| v.name =~ /#{query}/i }
    respond_to do |format|
      format.json{ render :json => @tags.map(&:attributes) }
    end
  end

This will create the tag, whenever the space bar is hit.

You could then add this search setting in the jquery script:

noResultsText: 'No result, hit space to create a new tag',

It's a little dirty but it works for me.

查看更多
闹够了就滚
7楼-- · 2019-01-21 16:01

There is a bug in Application.js code. There is an extra ) after "/products/tags.json". Remove the extra ). The code should be:

$("#product_tags").tokenInput("/products/tags.json", {
    prePopulate:       $("#product_tags").data("pre"),
    preventDuplicates: true,
    noResultsText:     "No results, needs to be created.",
    animateDropdown:   false
});
查看更多
登录 后发表回答