[RUBY] [Rails] Tag management function (using acts-as-taggable-on)

--Record when the tagging function is implemented. I was confused by the error, so as a memorandum. --acts-as-taggable-on (gem), Tag-it (jQuery plugin) is used for UI.

Alt text

environment

[Tagging function: Registration / Delete / Display]

1. Introducing gem

-acts-as-taggable-on GitHub If you follow it, a migration error will occur.

Gemfile


gem 'acts-as-taggable-on', '~> 6.0'

Terminal


% bundle install
         :
#Install the migration file! Message is displayed, follow it ↓
  % rake acts_as_taggable_on_engine:install:migrations
    #6 migration files are created
    Copied migration 20200905143628_acts_as_taggable_on_migration.acts_as_taggable_on_engine.rb from acts_as_taggable_on_engine
    Copied migration 20200905143629_add_missing_unique_indices.acts_as_taggable_on_engine.rb from acts_as_taggable_on_engine
    Copied migration 20200905143630_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb from acts_as_taggable_on_engine
    Copied migration 20200905143631_add_missing_taggable_index.acts_as_taggable_on_engine.rb from acts_as_taggable_on_engine
    Copied migration 20200905143632_change_collation_for_tag_names.acts_as_taggable_on_engine.rb from acts_as_taggable_on_engine
    Copied migration 20200905143633_add_missing_indexes_on_taggings.acts_as_taggable_on_engine.rb from acts_as_taggable_on_engine

#In case of mySQL, it is necessary to execute ↓ before executing migration (for initial setting)
% rake acts_as_taggable_on_engine:tag_names:collate_bin
% rails db:migrate   #Still, an error occurs(gem bug?)

--Fixed the migration file.

rb:xxxxxxxxx_add_missing_unique_indices.acts_as_taggable_on_engine.rb


         :
AddMissingUniqueIndices.class_eval do
  def self.up
    # add_index ActsAsTaggableOn.tags_table, :name, unique: true

    # remove_index ActsAsTaggableOn.taggings_table, :tag_id if index_exists?(ActsAsTaggableOn.taggings_table, :tag_id)
    # remove_index ActsAsTaggableOn.taggings_table, name: 'taggings_taggable_context_idx'
    # add_index ActsAsTaggableOn.taggings_table,
    #           [:tag_id, :taggable_id, :taggable_type, :context, :tagger_id, :tagger_type],
    #           unique: true, name: 'taggings_idx'
  end

  def self.down
    # remove_index ActsAsTaggableOn.tags_table, :name

    # remove_index ActsAsTaggableOn.taggings_table, name: 'taggings_idx'

    # add_index ActsAsTaggableOn.taggings_table, :tag_id unless index_exists?(ActsAsTaggableOn.taggings_table, :tag_id)
    # add_index ActsAsTaggableOn.taggings_table, [:taggable_id, :taggable_type, :context], name: 'taggings_taggable_context_idx'
  end
end

rb:xxxxxxxxxx_add_missing_taggable_index.acts_as_taggable_on_engine.rb


         :
AddMissingTaggableIndex.class_eval do
  def self.up
    # add_index ActsAsTaggableOn.taggings_table, [:taggable_id, :taggable_type, :context], name: 'taggings_taggable_context_idx'
  end

  def self.down
    # remove_index ActsAsTaggableOn.taggings_table, name: 'taggings_taggable_context_idx'
  end
end

** [Created tables and columns] **

tags table taggings table
name(Tag name) tag_id(tags table id)
taggings_count(Number of registered tags) taggable_type
taggable_id
tagger_type
tagger_id
content

Example: acts-as-taggable-on method

[Acquisition and search]

Method etc. meaning
tags_on(:tags) Get a list of tags for all articles
most_used Tags with a large number of registrations
least_used Tags with a small number of registrations
most_used(10) Obtained from tags with a large number of registrations. The default is 20
least_used(10) Obtained from tags with a small number of registrations. The default is 20
tag_counts Data of all tags
named("Tag name") Perfect matching
named_any(["Tag name 1", "Tag name 2",..]) Perfect matching(and)
named_like("Tag name") Partial Match
named_like_any(["Tag name 1", "Tag name 2",..]) Partial Match(or)

Example)Tag search


class User < ActiveRecord::Base
  acts_as_taggable_on :tags, :skills
  scope :by_join_date, order("created_at DESC")
end
  User.tagged_with(params[:tag])       #Get User data associated with tags
  User.tagged_with("Tag 1")[0].id       # 1
  User.tagged_with("Tag 1,Tag 2")[0].id #It can be a comma-separated string instead of an array.
  #Do you include it?
  User.tagged_with("Tag 1").by_join_date
  User.tagged_with("Tag 1").by_join_date.paginate(page: params[:page], per_page: 20)
  #Perfect matching(AND search)
  User.tagged_with(["Tag 1", "Tag 2"], match_all: true)
  #Condition match(OR search)
  User.tagged_with(["Tag 1", "Tag 2"], any: true)
  #Exclusion(Search for things that do not include)
  User.tagged_with(["Tag 1", "Tag 2"], exclude: true)

[Registration or deletion]

Method etc. meaning
tag_list.add("Tag 1", "Tag 2", ..) add to
tag_list = 'Tag 1,Tag 2, ..' Overwrite
tag_list.remove("Tag 1", "Tag 2", ..) Delete
save Save([id: 1, name: "Tag 1", taggings_count: 1],[id: 2, name: "Tag 2", taggings_count: 1])

2. Model (association)

--Add an association to the model you want to tag.

Post model


acts_as_taggable   # acts_as_taggable_on :Omission of tags

#three)Multiple settings are possible ↓
acts_as_taggable_on :skills, :interests  # @post.skill_You will be able to use list etc.

3. Controller

--Added : tag_list to the strong parameter for tag registration. --Added actions for tag display.

posts controller


def index
  @posts = Post.all
  @tags = Post.tag_counts_on(:tags).most_used(20)    #Tag list display
end

def show
  @post = Post.find(params[:id])
  @tags = @post.tag_counts_on(:tags)    #Display of tags associated with posts
end
      :
private
      :
def post_params
  params.require(:post).permit(:title, :content, :tag_list)
end

4. View

4-1. Tagging form

--If you separate it with , (default), it will be divided into multiple tags.

Set up a form for tagging on the post page(haml)


- form_for @post do |f|
    :
  = f.label :tag_list
  = f.text_field :tag_list, value: @post.tag_list.join(',')

-#three)When you want to select with the check box of the tag
- @tags.each do |tag|
  = f.check_box :tag_list, { multiple: true }, "#{tag.name}", nil
  = f.label " #{tag.name}(#{tag.taggings_count})"

4-2. Tag display

--The : tags added in the model is linked to the Post model. --Tag_list is an array, so get it with each statement. Alt text

Display of tags(haml)


- if @tags.present?
  - @tags.each do |tag|  #Get 20 in the order of the number of registrations with the controller(@tags)
    = link_to "#{tag.name}(#{tag.taggings_count})", tags_path(tag.name)
- else
 %p There are no registered tags

[Tag search]

--Click the tag (link) to display a list of related posts on the posts # index page.

posts controller


def index
       :
  @tags = Post.tag_counts_on(:tags).order('count DESC')     #All tags(Get tags column from Post model in descending order)
  if @tag = params[:tag]   #For tag search
    @post = Post.tagged_with(params[:tag])   #Posts associated with tags
  end
end

--tagged_with ("tag name"): A method for narrowing down the search. Get the clicked tag information and search by tagged_with ("tag name"). You can get posts with the same tag.

Tag with link (haml)


- @tags.each do |tag|
  = link_to "#{tag.name}(#{tag.taggings_count})", posts_path(tag: tag.name)

Display of post list linked to tag (haml)


- if @post.present?
  %h1 #{@tag}Posts related to
  - @post.each do |post|
    = post.user.name
    = post.name

[Prepare the UI (Tag-it)]

--Tag-it is a jQuery plugin that provides a tagging UI.

1. Introduction

  1. From tag-it GitHub, click CloneDownload ZIP.
  2. Unzip the file and store it in the JS and CSS directories. ** ・ ** jquery.tagit.css, tagit.ui-zendesk.css in the css folder → app / assets / stylesheets ** ・ ** tag-it.js, tag-it.min.js in js folder → assets / javascripts
  3. Introduced gem.

Gemfile


gem 'jquery-ui-rails'  # Tag-it uses jQuery UI

2. Settings

--Tag-it, settings for loading jQuery UI.

application.js


//= require jquery
//= require jquery_ujs
//= require jquery-ui
//= require tag-it
//= require_tree .

// turbolinks(The role of speeding up page loading)Deleted because jQuery does not fire unless the page is reloaded(Invalidation)。

application.scss


@import "reset";
@import "font-awesome-sprockets";
@import "font-awesome";
@import "jquery.tagit";       //Pay attention to the order of description
@import "tagit.ui-zendesk";   //If you do not write this later, the tag deletion (x) will not be displayed
        :

3. Load tag-it (jQuery)

--Page refresh fires tag-it event. --Change the display of placeholder for each input ↓ ↓ Alt text

tag-Load it (jQuery)


//Tag on page refresh-it ignite
$(document).ready(function() {
  $(".tag_form").tagit({  //Specified selector(This time,:tag_list text_field )To, tag-Reflect it
    tagLimit:10,         //Maximum number of tags
    singleField: true,   //Tag uniqueness
 // availableTags: ['ruby', 'rails', ..]You can set a list to auto-complete(* Arrangement is ok).. This time, pass the value of DB by Ajax communication(See below)。
  });
  let tag_count = 10 - $(".tagit-choice").length    //Count registered tags
  $(".ui-widget-content.ui-autocomplete-input").attr(
    'placeholder','after' + tag_count + 'You can register individually');
})

//Change placeholder by tag input
$(document).on("keyup", '.tagit', function() {
  let tag_count = 10 - $(".tagit-choice").length    //It's the same as ↑, so it's better to summarize.
  $(".ui-widget-content.ui-autocomplete-input").attr(
  'placeholder','after' + tag_count + 'You can register individually');
});

  //three:How to rewrite placeholder
    $(".input").attr('placeholder','Rewritten text');
  //three:How to delete placeholder
    $(".input").removeAttr('placeholder');

--When the event is fired with $ (selector) .tagit () (jQuery), on haml, ** ・ ** name attribute: post [tag_list] is added to the tag input form (text_field) (* post_params (controller) allows **: tag_list **, so you can register tags. ). id name and class name are also added. ** ・ ** ul and li are added in the input form.

Change of tag input form due to tagit event firing(haml)


.input_form
  = f.text_field :tag_list, value: @post.tag_list.join(","), class: "tag_form tagit-hidden-field" name: "post[tag_list]" id: "post_tag_list"  #class name, name and id name are added in tagit event
  -#Added in tagit event ↓ ↓
  %ul.tagit.ui-widget.ui-widget-content.ui-corner-all
    %li.tagit-new
      = f.text_field, class: "ui-widget-content ui-autocomplete-input", autocomplete: "off", placeholder: "You can register 10 more"                  # autocomplete="off"Disable autofill

4. Tag incremental search

--By Ajax communication, pass the DB data to ʻavailableTags` of Tag-it option. Alt text

4-1. Routing

--I want to communicate with Ajax on the new registration (without id information) and edit page (with id information). --I don't plan to get it in html, so I'll format json.

routes.rb


  resources :posts, expect: [:index] do
    get 'get_tag_search', on: :collection, defaults: { format: 'json' }
    get 'get_tag_search', on: :member, defaults: { format: 'json' }
  end

4-2. Controller

--Get all records in the Tags table where the name column starts with params [: key] (*: key is the input value defined by jQuery. Get the thing sent by Ajax).

posts controller


def get_tag_search
  @tags = Post.tag_counts_on(:tags).where('name LIKE(?)', "%#{params[:key]}%")
end

4-3. jbuilder --Get only the @tags name column defined in the controller.

rb:views/posts/get_tag_search.json.jbuilder


json.array! @tags do |tag|
  json.name tag.name
end
  # [{name: "Tag name 1"}, {name: "Tag name 2"}, ..]I'm getting it with the type of

4-4. jQuery --Get the input value of the tag form and send it with Ajax → DB search with the controller → Get the desired data with jbuilder → Pass it to availableTags of Tag-it with jQuery.

jQuery(Ajax communication part)


$(document).on("keyup", '.tagit', function() {
           :
  //Get a list of tags with Ajax
  let input = $(".ui-widget-content.ui-autocomplete-input").val();  //Store the input value in the variable input
  $.ajax({
    type: 'GET',
    url: 'get_tag_search',    //The url set in the routing
    data: { key: input },     //Input value:Pass it to the controller as a key
    dataType: 'json'
  })

  .done(function(data){
    if(input.length) {               //Only when there is an input value
      let tag_list = [];             //Prepare an empty array
      data.forEach(function(tag) {   //Store the name of the acquired data in an array
        tag_list.push(tag.name);     //Add one by one. tag_list = ["Tag name 1", "Tag name 2", ..]
      });
      $(".tag_form").tagit({
        availableTags: tag_list
      });
    }
  })
});

Recommended Posts

[Rails] Tag management function (using acts-as-taggable-on)
[rails] tag ranking function
[Rails] Implementation of tag function using acts-as-taggable-on and tag input completion function using tag-it
Add a tag function to Rails. Use acts-as-taggable-on
Tag function using acts-as-taggable-on on Devise My memo
Ajax bookmark function using Rails
Create a filtering function using acts-as-taggable-on
[Rails] Create an evaluation function using raty.js
Multiple image upload function using Rails Carrierwave
[Rails 6] Ranking function
[Rails] Category function
Rails follow function
Rails linked_to tag
[Rails] Notification function
[Rails] Implementation of search function using gem's ransack
[Rails 6] Implementation of inquiry function using Action Mailer
Create authentication function in Rails application using devise
[Rails] Implementation of image enlargement function using lightbox2
Implement star rating function using Raty in Rails6
[Rails] Schedule management using Full Calendar Yesterday's implementation
[Rails] I made a draft function using enum
[Note] Summary of rails login function using devise ①
[Rails] Test of star evaluation function using Raty [Rspec]
[Rails] Implement search function
[Rails] Implemented hashtag function
[Rails] Implementation of multi-layer category function using ancestry "Preparation"
[Rails] Implementation of multi-layer category function using ancestry "seed"
Rails search function implementation
Rails learning How to implement search function using ActiveModel
Try to implement tagging function using rails and js
[Rails] Implementation of multi-layer category function using ancestry "Editing form"
Ruby on Rails Email automatic sending function setting (using gmail)
[Rails] Implementation of multi-layer category function using ancestry "Creation form"
[Rails] Implementation of tagging function using intermediate table (without Gem)
[Rails] Implement event end function (logical deletion) using paranoia (gem)
[Rails] I tried to implement "Like function" using rails and js
[Rails] Implement community membership application / approval function using many-to-many associations
Implement application function in Rails
Rails fuzzy search function implementation
[Rails] Implement User search function
Introduced graph function with rails
Try using view_component with rails
SNS authentication using Rails google
Implement follow function in Rails
Japaneseize using i18n with Rails
[Rails] Japanese localization using rails-i18n
[Rails 6] Implementation of search function
[Rails] Implementation of category function
Rails ~ Understanding the message function ~
[Rails] Test code using Rspec
Organize Rails routing using draw
Implement category function using ancestory
[Rails] (Supplement) Implemented follow function
Login function implementation with rails
[Rails] EC site cart function
Error when using rails capybara
[Rails] Implementation of tutorial function
[Rails] Try using Faraday middleware
Detailed tips when using Rails
[Rails] Implement image posting function
[Rails] Implementation of like function