wyc
wyc

Reputation: 55293

Removing tags from this `tag_names` attribute when then are removed form the input field?

I have a Tag model:

class Tag < ActiveRecord::Base
  has_many :taggings, :dependent => :destroy  
  has_many :posts, :through => :taggings
  has_many :subscriptions
  has_many :subscribed_users, :source => :user, :through => :subscriptions
end

and a Post model:

class Post < ActiveRecord::Base
  attr_accessible :title, :content, :tag_names

  belongs_to :user

  has_many :taggings, :dependent => :destroy
  has_many :tags, :through => :taggings

  attr_writer :tag_names
  after_save :assign_tags

  def tag_names
    @tag_names || tags.map(&:name).join(' ')
  end

  private

  def assign_tags
    return if @tag_names.blank?
    @tag_names.split(" ").each do |name|
      tag = Tag.find_or_create_by_name(name)
      self.tags << tag unless tags.include?(tag)
    end
  end
end

The assign_tags method stores the saved tags in :tag_names and displays it in an input field in this fashion: tag1 tag2 tag3 (values are separated by spaces).

views/posts/_form.html.erb:

  <div class="field">
    <%= f.label :tag_names %><br />  
    <%= f.autocomplete_field :tag_names, autocomplete_tag_name_posts_path, :"data-delimiter" => ' '%>
  </div>

As you can see, assign_tags, enables me to add/push tags to tag_names. But if I delete a tag from the input field and save. The tag will still show up.

Any suggestions to help me solve this issue?

(Maybe tag_names should just empty itself before the tags are pushed in. But no idea how to code that).

Upvotes: 0

Views: 44

Answers (1)

user1186625
user1186625

Reputation:

Before assigning tags, you'll want to clear the tags for that item. e.g.

def assign_tags
  self.tags = []
  return if @tag_names.blank?
  @tag_names.split(" ").each do |name|
    tag = Tag.find_or_create_by_name(name)
    self.tags << tag unless tags.include?(tag)
  end
end

Update:

Or, a method that will hit the database a bit less:

def assign_tags
  ntags = []
  @tag_names.to_s.split(" ").each do |name|
    ntags << Tag.find_or_create_by_name(name)
  end
  self.tags = ntags
end

If you wanted to get really crazy with the write-less-do-more paradigm, you could do:

def assign_tags
  self.tags = @tag_names.to_s.split(" ").inject([]) do |arr, name|
    arr << Tag.find_or_create_by_name(name)
    arr
  end
end

Alternatively, I'd recommend looking into the acts-as-taggable-on gem.

Upvotes: 1

Related Questions