acts_as_taggable_on Tags added twice

非 Y 不嫁゛ 提交于 2019-12-13 16:24:08

问题


I have a RoR app that allows users to tag items in their collections. I use the tag-it.js Jquery plugin and use Ajax calls to add and remove the tags in the ItemsController. My problem is that each tag is added twice so that when I do @item.tags.each, all the tags are shown twice.

ItemsController:

  def add_tag 
    @collection = current_user.collections.find(params[:collection_id])    
    @item = @collection.items.find(params[:id])
    @item.tag_list.add(params[:tag])   
    current_user.tag(@item, :with => @item.tag_list.to_s, :on => :tags)          
    @item.save   

    render nothing: true 
  end 

  def remove_tag 
    @item = current_user.items.find_by_id(params[:id])       
    @item.tag_list.remove(params[:tag]) 
    current_user.tag(@item, :with => @item.tag_list.to_s, :on => :tags)          
    @item.save 

    render nothing: true 
  end 

Javascript that handles the AJAX tagging with Tag-it.js:

$('#item_tags').tagit({
      onTagAdded: function(event, tag) {          
       var add_url = $('#item_tags').attr("data-add-url");              
        $.ajax({
          url: add_url,                      
          data: {tag: tag.text().substring(0, tag.text().length-1)},                                   
        })             
      }, 
      onTagRemoved: function(event, tag) {
        var remove_url = $('#item_tags').attr("data-remove-url"); 
        $.ajax({
          url: remove_url,  
          type: 'DELETE',                        
          data: {tag: tag.text().substring(0, tag.text().length-1)},                                  
        })
      },
      tagSource: function(search, showChoices) {
        var autocomplete_url = $('#item_tags').attr("data-auctocomplete-url");             
        $.ajax({
          url: autocomplete_url,        
          data: {term: search.term},                              
          success: function(choices) {
            showChoices(choices);
          }
        })           
      }
});

item#_form view where the user adds / removes tags:

<ul id='item_tags' class='tagit' data-remove-url="<%= remove_tag_collection_item_path %>" data-add-url="<%= add_tag_collection_item_path %>" data-auctocomplete-url="/collections/<%=@collection.id %>/items/autocomplete_tag_name"> 
      <% @item.tags.each do |tag| %>   
        <li><%= tag.name %></li>            
      <% end %>                   
</ul>

I must note that it is necessary to have tag ownership (by current_user) so that the Jquery auto complete only completes based on current user's previous tags and not all users. I think the problem is that I have to add the tag to the tag_list and then add the tag_list to the user item tagging. I can't find a way around this because the current_user.tag() method seems to overwrite the previous item tags when current_user.tag() is called so I have to add the new tag to the previous tags to preserve them.

Additionally when I submit the item#_form, I need to somehow have the update method ignore the tags attribute because it's trying to save them to the item but they're already saved with an AJAX call so I get this error:

ActiveRecord::AssociationTypeMismatch in ItemsController#update
ActsAsTaggableOn::Tag(#82082080) expected, got String(#72294010)

Thanks in advance.

PS. Here is how I got the auto complete working in the ItemsController:

def get_autocomplete_items(parameters)
    tags = current_user.owned_tags.named_like(parameters[:term])   
end

回答1:


You were right:

I think the problem is that I have to add the tag to the tag_list and then add the tag_list to the user item tagging. I can't find a way around this because the current_user.tag() method seems to overwrite the previous item tags when current_user.tag() is called so I have to add the new tag to the previous tags to preserve them.

The .tag method over-writes existing tags with the given list. So if you want to add new tags it seems you need to append your new tags to the existing tags, and then pass in that new list. However, .tag_list.add actually also creates tags.

So, when you were doing this:

  @item.tag_list.add(params[:tag])   
  current_user.tag(@item, :with => @item.tag_list.to_s, :on => :tags)          

You were indeed adding the new tag twice.

And when I was doing this:

tag_list = profile.tag_list // oops!
tag_list.add(tags)
self.tag(profile, with: tag_list, on: :tags)

I was creating a reference to the tag_list, and then calling add on it. What we needed to do was this:

tag_list = profile.tags.map(&:name)

To make an array of the tag names for the object we are tagging. Then we can call add on the copy of the list, no problem! No more duplicate tags.

I'm glad I encountered your question, as it led me to an answer that worked for me. However, I would be even more pleased if there was just a nice way of doing this provided by the library. I wasn't able to find a good way just by reading the docs.




回答2:


You were doing all right, but, just one little mistake.

Here tag_list is getting duplicate tags, one with Owner and other without Owner

Your @item.tag_list.add(params[:tag]) line is adding tags without Owner

And current_user.tag(@item, :with => @item.tag_list.to_s, :on => :tags) line is adding same tag with Owner

And later, when you save the object, since tag_list is behaving as reference to un-ownered tags of object, both get saved

Following code shall work fine.

  def add_tag 
    @collection = current_user.collections.find(params[:collection_id])    
    @item = @collection.items.find(params[:id])
    tag_list = @item.all_tag_list.dup # Reference to original tag_list avoided
    # Also instead of "tag_list", use "all_tag_list" method, as "tag_list" only return tags without owner
    # Link(go to last line og Tag Ownership topic) : "https://github.com/mbleigh/acts-as-taggable-on#tag-ownership"
    tag_list.add(params[:tag])   
    current_user.tag(@item, :with => tag_list.to_s, :on => :tags)          
    @item.save   

    render nothing: true 
  end 

  def remove_tag 
    @item = current_user.items.find_by_id(params[:id])
    tag_list = @item.all_tag_list.dup # Reference to original tag_list avoided      
    tag_list.remove(params[:tag]) 
    current_user.tag(@item, :with => tag_list.to_s, :on => :tags)          
    @item.save 

    render nothing: true 
  end 

IMPROVED VERSION

def add_owned_tag 
    @some_item = Item.find(params[:id])
    owned_tag_list = @some_item.all_tag_list - @some_item.tag_list
    owned_tag_list += [(params[:tag])]
    @tag_owner.tag(@some_item, :with => stringify(owned_tag_list), :on => :tags)
    @some_item.save   
end

def stringify(tag_list)
    tag_list.inject('') { |memo, tag| memo += (tag + ',') }[0..-1]
end

def remove_owned_tag 
    @some_item = Item.find(params[:id])
    owned_tag_list = @some_item.all_tag_list - @some_item.tag_list
    owned_tag_list -= [(params[:tag])]
    @tag_owner.tag(@some_item, :with => stringify(owned_tag_list), :on => :tags)
    @some_item.save   
end



回答3:


Take care with onTagAdded event fired on page load. See events sample here http://aehlke.github.com/tag-it/examples.html

//-------------------------------
// Tag events
//-------------------------------
var eventTags = $('#eventTags');
eventTags.tagit({
     availableTags: sampleTags,
     onTagRemoved: function(evt, tag) {
        console.log(evt);
        alert('This tag is being removed: ' + eventTags.tagit('tagLabel', tag));
    },
    onTagClicked: function(evt, tag) {
        console.log(tag);
        alert('This tag was clicked: ' + eventTags.tagit('tagLabel', tag));
    }
 }).tagit('option', 'onTagAdded', function(evt, tag) {
    // Add this callbackafter we initialize the widget,
    // so that onTagAdded doesn't get called on page load.
    alert('This tag is being added: ' + eventTags.tagit('tagLabel', tag));
 });


来源:https://stackoverflow.com/questions/10725048/acts-as-taggable-on-tags-added-twice

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!