Search code examples
ruby-on-railsruby-on-rails-4rubygemshashtagacts-as-taggable-on

Rails - acts-as-taggable-on gem - How to change a tag's CSS style depending on it's tag count


Tried all sorts of different methods - just can't seem to solve this issue - your help would be amazing!

When a User creates a Post they can tag it via the acts-as-taggable-on gem. If the User writes a tag 2 times or more across Posts I am trying to make that tag then have a different CSS style than the other tags used.

For example, if a user creates three posts: http://imgur.com/zQEWcRo

  • Post1 tagged with "#foo #bar #foobar"
  • Post2 tagged with "#foobar #placeholder"
  • Post3 tagged with "#foobar #foo"

I am trying to get the #foobar to have a different CSS style than the other tags (#foo #bar #placeholder) across all posts (& preferably move #foobar to be the first hashtag in the sequence)

new.html.erb

<%= form_for @post, html: { multipart: true } do |f| %>
 <%= f.text_field :tag_list %>
 <%= f.submit %>
<% end %>

posts_controller.rb

class PostsController < ApplicationController

  def index
    if params[:tag]
     @posts = Post.tagged_with(params[:tag]).order(created_at: :desc).page(params[:page]).per_page(2)
   else 
     @posts = Post.all.order(created_at: :desc).page(params[:page]).per_page(2)
   end 
 end 

index.html.erb

<div id="posts">
 <%= render @posts %>
</div>

<%= will_paginate @posts %>

_post.html.erb

<div class="post">
 <%= raw post.tag_list.map {|t| link_to t, tag_path(t)}.join(' ') %>
</div> 

<% post.things.order(:order).each do |thing| %>
 <%= thing.try(:text) %>
<% end %>

Schema

ActiveRecord::Schema.define(version: 20160227154831) do

 create_table "posts", force: :cascade do |t|
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
 end

create_table "taggings", force: :cascade do |t|
 t.integer  "tag_id"
 t.integer  "taggable_id"
 t.string   "taggable_type"
 t.integer  "tagger_id"
 t.string   "tagger_type"
 t.string   "context",       limit: 128
 t.datetime "created_at"
end

add_index "taggings", ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "taggings_idx", unique: true
add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context"

create_table "tags", force: :cascade do |t|
 t.string  "name"
 t.integer "taggings_count", default: 0
end

add_index "tags", ["name"], name: "index_tags_on_name", unique: true

create_table "things", force: :cascade do |t|
 t.text     "text"
 t.datetime "created_at",         null: false
 t.datetime "updated_at",         null: false
end

end

Solution

  • This is actually rather easy. Each acts-as-taggable-on tag has a taggings count.

    For example:

    post.tags.order("taggings_count DESC")
    

    for post 1 of your example will give you this:

    <ActsAsTaggableOn::Tag id: some_id, name: "foobar", taggings_count: 3>,  
    <ActsAsTaggableOn::Tag id: some_id, name: "foo", taggings_count: 2>,
    <ActsAsTaggableOn::Tag id: some_id, name: "bar", taggings_count: 1>
    

    Now you have your tags sorted by number of occurrence and you can also display a different class depending on taggings_count.

    Edit:

    This is how you could render the tags for example:

    <div class="post">
      <%= raw post.tags.order("taggings_count DESC").map {|t| link_to t.name, tag_path(t.name), class: css_class_for_tag(t.taggings_count)}.join(' ') %>
    </div> 
    

    You will then need to define the helper method css_class_for_tag, for example like this:

    def css_class_for_tag count
      case count
        when 0
          'new_tag'
        else
          'used_tag' 
      end
    end