Dans le développement de l'application, j'ai ajouté une fonction de catégorie en utilisant un joyau appelé ascendance, donc je l'ai résumé.
Installez l'ascendance.
gemfile
gem 'ancestry'
Ensuite, créez un modèle de catégorie.
rails g model category
Décrivez has_ancestry.
app/models/category.rb
class Category < ApplicationRecord
has_many :posts
has_ancestry
end
Décrivez-le dans le fichier de migration comme suit. Pour l'index, cliquez ici (https://qiita.com/seiya1121/items/fb074d727c6f40a55f22)
db/migrate/20XXXXXXXXXXXX_create_categories.rb
class CreateCategories < ActiveRecord::Migration[6.0]
def change
create_table :categories do |t|
t.string :name, index: true, null: false
t.string :ancestry, index: true
t.timestamps
end
end
end
Je décrirai la catégorie dans la feuille de calcul Google. La colonne A est id, la colonne B est le nom (nom de la catégorie) et la colonne C est l'ascendance (valeur numérique qui distingue les parents et les descendants). Vous pouvez enregistrer les données en suivant les étapes de Fichier → Télécharger → valeurs séparées par des virgules (feuille courante .csv).
Placez le fichier csv téléchargé dans le dossier db.
Décrivez comme suit dans le fichier seeds.rb.
db/seeds.rb
require "csv"
CSV.foreach('db/category.csv') do |row|
Category.create(:id => row[0], :name => row[1], :ancestry => row[2])
end
Lorsque vous exécutez la commande rails db: seed dans le terminal, le fichier csv est lu et l'enregistrement DB est automatiquement généré. Après foreach, spécifiez le fichier que vous souhaitez lire. La description ci-dessous sera le nom du modèle.create (nom de la colonne => colonne que vous souhaitez charger). row [0] → Une colonne est id ligne [1] → la colonne B est le nom (nom de la catégorie) ligne [2] → La colonne C est l'ascendance (un nombre qui distingue les parents et les descendants)
Définissez le routage des catégories enfants et petits-enfants au format json.
config/routes.rb
Rails.application.routes.draw do
~Abréviation~
resources :posts do
collection do
get 'top'
get 'get_category_children', defaults: { format: 'json' }
get 'get_category_grandchildren', defaults: { format: 'json' }
get 'name_search'
end
~Abréviation~
end
Définissez une catégorie parente pour le contrôleur de messages. Comme il est utilisé à plusieurs endroits, il est défini à l'aide de before_action.
app/controllers/posts_controller.rb
def set_parents
@parents = Category.where(ancestry: nil)
end
Définissez des méthodes pour les catégories enfants et petits-enfants dans le contrôleur de messages.
app/controllers/posts_controller.rb
def get_category_children
@category_children = Category.find("#{params[:parent_id]}").children
end
def get_category_grandchildren
@category_grandchildren = Category.find("#{params[:child_id]}").children
end
Créez un fichier json.jbuilder et convertissez-le en données json.
ruby:app/views/posts/get_category_children.json.jbuilder
json.array! @category_children do |child|
json.id child.id
json.name child.name
end
ruby:app/views/posts/get_category_grandchildren.json.jbuilder
json.array! @category_grandchildren do |grandchild|
json.id grandchild.id
json.name grandchild.name
end
Définissez le comportement lors de la sélection d'une catégorie avec javascript.
:app/javascript/category_post.js
$(function(){
function appendOption(category){
var html = `<option value="${category.id}">${category.name}</option>`;
return html;
}
function appendChildrenBox(insertHTML){
var childSelectHtml = "";
childSelectHtml = `<div class="category__child" id="children_wrapper">
<select id="child__category" name="post[category_id]" class="serect_field">
<option value="">---</option>
${insertHTML}
</select>
</div>`;
$('.append__category').append(childSelectHtml);
}
function appendGrandchildrenBox(insertHTML){
var grandchildSelectHtml = "";
grandchildSelectHtml = `<div class="category__child" id="grandchildren_wrapper">
<select id="grandchild__category" name="post[category_id]" class="serect_field">
<option value="">---</option>
${insertHTML}
</select>
</div>`;
$('.append__category').append(grandchildSelectHtml);
}
$('#item_category_id').on('change',function(){
var parentId = document.getElementById('item_category_id').value;
if (parentId != ""){
$.ajax({
url: '/posts/get_category_children/',
type: 'GET',
data: { parent_id: parentId },
dataType: 'json'
})
.done(function(children){
$('#children_wrapper').remove();
$('#grandchildren_wrapper').remove();
var insertHTML = '';
children.forEach(function(child){
insertHTML += appendOption(child);
});
appendChildrenBox(insertHTML);
if (insertHTML == "") {
$('#children_wrapper').remove();
}
})
.fail(function(){
alert('Échec de l'obtention de la catégorie');
})
}else{
$('#children_wrapper').remove();
$('#grandchildren_wrapper').remove();
}
});
$('.append__category').on('change','#child__category',function(){
var childId = document.getElementById('child__category').value;
if(childId != ""){
$.ajax({
url: '/posts/get_category_grandchildren',
type: 'GET',
data: { child_id: childId },
dataType: 'json'
})
.done(function(grandchildren){
$('#grandchildren_wrapper').remove();
var insertHTML = '';
grandchildren.forEach(function(grandchild){
insertHTML += appendOption(grandchild);
});
appendGrandchildrenBox(insertHTML);
if (insertHTML == "") {
$('#grandchildren_wrapper').remove();
}
})
.fail(function(){
alert('Échec de l'obtention de la catégorie');
})
}else{
$('#grandchildren_wrapper').remove();
}
})
});
Affichez la boîte de sélection de catégorie sur la nouvelle page de publication.
ruby:app/views/posts/new.html.erb
<div class="append__category">
<div class="category">
<div class="form__label">
<div class="weight-bold-text lavel__name ">
Catégorie
</div>
<div class="lavel__Required">
<%= f.collection_select :category_id, @parents, :id, :name,{ include_blank: "Veuillez sélectionner"},class:"serect_field", id:"item_category_id" %>
</div>
</div>
</div>
</div>
app/controllers/posts_controller.rb
def top
respond_to do |format|
format.html
format.json do
if params[:parent_id]
@childrens = Category.find(params[:parent_id]).children
elsif params[:children_id]
@grandChilds = Category.find(params[:children_id]).children
elsif params[:gcchildren_id]
@parents = Category.where(id: params[:gcchildren_id])
end
end
end
end
En javascript, nous obtenons la catégorie enfant et la catégorie petit-enfant qui appartiennent à la catégorie parent sur laquelle se trouve la souris.
:app/javascript/category.js
$(document).ready(function () {
//Afficher la catégorie parent
$('#categoBtn').hover(function (e) {
e.preventDefault();
e.stopPropagation();
$('#tree_menu').show();
$('.categoryTree').show();
}, function () {
//Je n'ose rien écrire
});
//Afficher les catégories d'en-tête de manière asynchrone
function childBuild(children) {
let child_category = `
<li class="category_child">
<a href="/posts/${children.id}/search"><input class="child_btn" type="button" value="${children.name}" name= "${children.id}">
</a>
</li>
`
return child_category;
}
function gcBuild(children) {
let gc_category = `
<li class="category_grandchild">
<a href="/posts/${children.id}/search"><input class="gc_btn" type="button" value="${children.name}" name= "${children.id}">
</a>
</li>
`
return gc_category;
}
//Afficher la catégorie parent
$('#categoBtn').hover(function (e) {
e.preventDefault();
e.stopPropagation();
timeOut = setTimeout(function () {
$('#tree_menu').show();
$('.categoryTree').show();
}, 500)
}, function () {
clearTimeout(timeOut)
});
//Afficher les catégories enfants
$('.parent_btn').hover(function () {
$('.parent_btn').css('color', '');
$('.parent_btn').css('background-color', '');
let categoryParent = $(this).attr('name');
timeParent = setTimeout(function () {
$.ajax({
url: '/posts/top',
type: 'GET',
data: {
parent_id: categoryParent
},
dataType: 'json'
})
.done(function (data) {
$(".categoryTree-grandchild").hide();
$(".category_child").remove();
$(".category_grandchild").remove();
$('.categoryTree-child').show();
data.forEach(function (child) {
let child_html = childBuild(child)
$(".categoryTree-child").append(child_html);
});
$('#tree_menu').css('max-height', '490px');
})
.fail(function () {
alert("Veuillez sélectionner une catégorie");
});
}, 400)
}, function () {
clearTimeout(timeParent);
});
//Afficher la catégorie des petits-enfants
$(document).on({
mouseenter: function () {
$('.child_btn').css('color', '');
$('.child_btn').css('background-color', '');
let categoryChild = $(this).attr('name');
timeChild = setTimeout(function () {
$.ajax({
url: '/posts/top',
type: 'GET',
data: {
children_id: categoryChild
},
dataType: 'json'
})
.done(function (gc_data) {
$(".category_grandchild").remove();
$('.categoryTree-grandchild').show();
gc_data.forEach(function (gc) {
let gc_html = gcBuild(gc)
$(".categoryTree-grandchild").append(gc_html);
let parcol = $('.categoryTree').find(`input[name="${gc.root}"]`);
$(parcol).css('color', 'white');
$(parcol).css('background-color', '#b1e9eb');
});
$('#tree_menu').css('max-height', '490px');
})
.fail(function () {
alert("Veuillez sélectionner une catégorie");
});
}, 400)
},
mouseleave: function () {
clearTimeout(timeChild);
}
}, '.child_btn');
//Lors de la sélection d'une catégorie de petits-enfants
$(document).on({
mouseenter: function () {
let categoryGc = $(this).attr('name');
timeGc = setTimeout(function () {
$.ajax({
url: '/posts/top',
type: 'GET',
data: {
gcchildren_id: categoryGc
},
dataType: 'json'
})
.done(function (gc_result) {
let childcol = $('.categoryTree-child').find(`input[name="${gc_result[0].parent}"]`);
$(childcol).css('color', 'white');
$(childcol).css('background-color', '#b1e9eb');
$('#tree_menu').css('max-height', '490px');
})
.fail(function () {
alert("Veuillez sélectionner une catégorie");
});
}, 400)
},
mouseleave: function () {
clearTimeout(timeGc);
}
}, '.gc_btn');
//Boutons de la page de liste des catégories
$('#all_btn').hover(function (e) {
e.preventDefault();
e.stopPropagation();
$(".categoryTree-grandchild").hide();
$(".categoryTree-child").hide();
$(".category_grandchild").remove();
$(".category_child").remove();
}, function () {
//En n'écrivant rien, seule l'action qui s'écarte de l'élément parent est propagée.
});
//Masquer la catégorie(0 du menu des catégories.Il disparaît lorsque le curseur est supprimé pendant 8 secondes ou plus)
$(document).on({
mouseleave: function (e) {
e.stopPropagation();
e.preventDefault();
timeChosed = setTimeout(function () {
$(".categoryTree-grandchild").hide();
$(".categoryTree-child").hide();
$(".categoryTree").hide();
$(this).hide();
$('.parent_btn').css('color', '');
$('.parent_btn').css('background-color', '');
$(".category_child").remove();
$(".category_grandchild").remove();
}, 800);
},
mouseenter: function () {
timeChosed = setTimeout(function () {
$(".categoryTree-grandchild").hide();
$(".categoryTree-child").hide();
$(".categoryTree").hide();
$(this).hide();
$('.parent_btn').css('color', '');
$('.parent_btn').css('background-color', '');
$(".category_child").remove();
$(".category_grandchild").remove();
}, 800);
clearTimeout(timeChosed);
}
}, '#tree_menu');
//Traitement des boutons de catégorie
$(document).on({
mouseenter: function (e) {
e.stopPropagation();
e.preventDefault();
timeOpened = setTimeout(function () {
$('#tree_menu').show();
$('.categoryTree').show();
}, 500);
},
mouseleave: function (e) {
e.stopPropagation();
e.preventDefault();
clearTimeout(timeOpened);
$(".categoryTree-grandchild").hide();
$(".categoryTree-child").hide();
$(".categoryTree").hide();
$("#tree_menu").hide();
$(".category_child").remove();
$(".category_grandchild").remove();
}
}, '.header__headerInner__nav__listsLeft__item');
});
Définissez la fenêtre de sélection de catégorie sur l'écran supérieur.
ruby:app/views/posts/top.html.erb
<div class="item-categories">
<h2>
Liste des catégories
</h2>
<%= link_to posts_path, class: "category-button", id: 'categoBtn' do %>
Recherche par catégorie
<% end %>
<div id="tree_menu">
<ul class="categoryTree">
<% @parents.each do |parent| %>
<li class="category_parent">
<%= link_to search_post_path(parent) do %>
<input type="button" value="<%= parent.name %>" name="<%= parent.id %>" class="parent_btn">
<% end %>
</li>
<% end %>
</ul>
<ul class="categoryTree-child">
</ul>
<ul class="categoryTree-grandchild">
</ul>
</div>
</div>
Une action de recherche est définie à l'aide de membre pour distinguer les catégories par identifiant.
config/routes.rb
resources :posts do
~Abréviation~
member do
get 'search'
end
~Abréviation~
end
La catégorie sur laquelle vous avez cliqué est conditionnelle selon qu'il s'agit d'une catégorie parente, d'une catégorie enfant ou d'une catégorie petit-enfant.
app/controllers/posts_controller.rb
def search
@category = Category.find_by(id: params[:id])
if @category.ancestry == nil
category = Category.find_by(id: params[:id]).indirect_ids
if category.empty?
@posts = Post.where(category_id: @category.id).order(created_at: :desc)
else
@posts = []
find_item(category)
end
elsif @category.ancestry.include?("/")
@posts = Post.where(category_id: params[:id]).order(created_at: :desc)
else
category = Category.find_by(id: params[:id]).child_ids
@posts = []
find_item(category)
end
end
def find_item(category)
category.each do |id|
post_array = Post.where(category_id: id).order(created_at: :desc)
if post_array.present?
post_array.each do |post|
if post.present?
@posts.push(post)
end
end
end
end
end
ruby:app/views/posts/search.html.erb
<div class="item-categories">
<h2>
Liste des catégories
</h2>
<%= link_to posts_path, class: "category-button", id: 'categoBtn' do %>
Recherche par catégorie
<% end %>
<div id="tree_menu">
<ul class="categoryTree">
<% @parents.each do |parent| %>
<li class="category_parent">
<%= link_to search_post_path(parent) do %>
<input type="button" value="<%= parent.name %>" name="<%= parent.id %>" class="parent_btn">
<% end %>
</li>
<% end %>
</ul>
<ul class="categoryTree-child">
</ul>
<ul class="categoryTree-grandchild">
</ul>
</div>
</div>
https://qiita.com/k_suke_ja/items/aee192b5174402b6e8ca https://qiita.com/Sobue-Yuki/items/9c1b05a66ce6020ff8c1 https://qiita.com/dr_tensyo/items/88e8ddf0f5ce37040dc8 https://qiita.com/ATORA1992/items/bd824f5097caeee09678 https://qiita.com/misioro_missie/items/175af1f1678e76e59dea https://qiita.com/Rubyist_SOTA/items/49383aa7f60c42141871
Recommended Posts