--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.
-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 |
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)
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]) |
--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.
--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
--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})"
--The : tags
added in the model is linked to the Post model.
--Tag_list is an array, so get it with each statement.
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
--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
--Tag-it is a jQuery plugin that provides a tagging UI.
Clone
→ Download ZIP
.Gemfile
gem 'jquery-ui-rails' # Tag-it uses jQuery UI
--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
:
--Page refresh fires tag-it event. --Change the display of placeholder for each input ↓ ↓
display: none;
to hide it.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
--By Ajax communication, pass the DB data to ʻavailableTags` of Tag-it option.
--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
--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