-Autoriser les messages à être étiquetés. -Mettre en œuvre une fonction (recherche incrémentale) qui recherche automatiquement chaque fois qu'un caractère est saisi dans une balise.
Mac OS Catalina 10.15.4 série ruby 2.6 rails série 6.0
Comme Gif dans la figure ci-dessus, lorsque vous commencez à saisir des balises, les balises recommandées peuvent être affichées en fonction des balises enregistrées dans le DB. Si nous pouvons implémenter la fonction de balisage basée sur cet article, je pense que nous pouvons facilement implémenter la recherche de balises et ainsi de suite.
Suivez les étapes ci-dessus pour mettre en œuvre.
Tout d'abord, introduisons différents modèles.
% rails g model tag
% rails g model post
% rails g model post_tag_relation
% rails g devise user
En l'état, décrivons la validation en associant chaque modèle introduit.
post.rb
class Post < ApplicationRecord
has_many :post_tag_relations
has_many :tags, through: :post_tag_relations
belongs_to :user
end
tag.rb
class Tag < ApplicationRecord
has_many :post_tag_relations
has_many :posts, through: :post_tag_relations
validates :name, uniqueness: true
end
En définissant "through :: table intermédiaire", le modèle Post et le modèle Tag, qui ont une relation plusieurs-à-plusieurs, sont associés. En guise de mise en garde, il est nécessaire de lier la table intermédiaire avant de référencer par through. (Puisque le code est lu par le haut, si vous écrivez dans l'ordre has_many: posts, through :: post_tag_relations → has_many: post_tag_relations, une erreur se produira.)
post_tag_relation
class PostTagRelation < ApplicationRecord
belongs_to :post
belongs_to :tag
end
user.rb
class User < ApplicationRecord
#<réduction>
has_many :posts, dependent: :destroy
validates :name, presence: true
L'option has_many dans le modèle User est étiquetée depend :: destroy de sorte que lorsque les informations utilisateur de l'élément parent sont supprimées, cette publication humaine est également supprimée.
De plus, la description (valide: 〇〇, présence: true) pour empêcher l'enregistrement des données vides dans le modèle Post et le modèle Tag sera spécifiée collectivement dans l'objet de formulaire à créer ultérieurement, ce n'est donc pas nécessaire maintenant. ..
Ensuite, ajoutez des colonnes au modèle créé. (L'exigence minimale est la colonne du nom de la balise, alors arrangez les autres comme vous le souhaitez.)
fichier de post-migration
class CreatePosts < ActiveRecord::Migration[6.0]
def change
create_table :posts do |t|
t.string :title, null: false
t.text :content, null: false
t.date :date
t.time :time_first
t.time :time_end
t.integer :people
t.references :user, foreign_key: true
t.timestamps
end
end
end
La raison pour laquelle l'utilisateur est référencé en tant que clé externe dans le fichier de post-migration est d'afficher le nom d'utilisateur dans la liste de publication ultérieurement.
fichier de migration de balise
class CreateTags < ActiveRecord::Migration[6.0]
def change
create_table :tags do |t|
t.string :name, null: false, uniqueness: true
t.timestamps
end
end
end
Application de l'unicité: true à la colonne de nom ci-dessus est introduite pour éviter les noms de balises en double. (Comme il est supposé que les balises portant le même nom seront utilisées plusieurs fois, vous vous demandez peut-être si cela ne fonctionnera pas comme fonction de balisage si vous évitez la duplication, mais comment refléter les balises existantes dans les articles sera décrite plus tard. Apparaîtra.)
post_tag_fichier de migration de relation
class CreatePostTagRelations < ActiveRecord::Migration[6.0]
def change
create_table :post_tag_relations do |t|
t.references :post, foreign_key: true
t.references :tag, foreign_key: true
t.timestamps
end
end
end
Ce modèle post_tag_relation joue le rôle d'une table intermédiaire entre le modèle de publication et le modèle de balise, qui a une relation plusieurs-à-plusieurs.
fichier de migration utilisateur
class DeviseCreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
## Database authenticatable
t.string :name, null: false
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
#<réduction>
Je voulais utiliser le nom d'utilisateur, j'ai donc ajouté une colonne de nom.
N'oubliez pas d'exécuter la commande suivante après avoir édité la colonne.
% rails db:migrate
Dans cette implémentation, nous voulons enregistrer les valeurs d'entrée du formulaire de publication dans la table des publications et la table des balises en même temps, nous utiliserons donc l'objet Form.
Tout d'abord, créez un répertoire de formulaires dans le répertoire de l'application et créez-y un fichier posts_tag.rb. Ensuite, définissez une méthode de sauvegarde pour enregistrer les valeurs dans la table des publications et la table des balises en même temps, comme indiqué ci-dessous.
posts_tag.rb
class PostsTag
include ActiveModel::Model
attr_accessor :title, :content, :date, :time_first, :time_end, :people, :name, :user_id
with_options presence: true do
validates :title
validates :content
validates :name
end
def save
post = Post.create(title: title, content: content, date: date, time_first: time_first, time_end: time_end, people: people, user_id: user_id)
tag = Tag.where(name: name).first_or_initialize
tag.save
PostTagRelation.create(post_id: post.id, tag_id: tag.id)
end
end
Ensuite, définissez le routage pour exécuter les actions index, new et create du contrôleur de publications.
routes.rb
resources :posts, only: [:index, :new, :create] do
collection do
get 'search'
end
end
Le routage vers l'action de recherche définie dans la collection est utilisé par la fonction de recherche incrémentielle.
Générez un contrôleur dans le terminal.
% rails g controller posts
Le code dans le fichier de contrôleur de messages généré ressemble à ceci:
posts_controller.rb
class PostsController < ApplicationController
before_action :authenticate_user!, only: [:new]
def index
@posts = Post.all.order(created_at: :desc)
end
def new
@post = PostsTag.new
end
def create
@post = PostsTag.new(posts_params)
if @post.valid?
@post.save
return redirect_to posts_path
else
render :new
end
end
def search
return nil if params[:input] == ""
tag = Tag.where(['name LIKE ?', "%#{params[:input]}%"])
render json: {keyword: tag}
end
private
def posts_params
params.require(:post).permit(:title, :content, :date, :time_first, :time_end, :people, :name).merge(user_id: current_user.id)
end
end
Dans l'action de création, la valeur reçue par posts_params est enregistrée dans le modèle Posts et la table Tags à l'aide de la méthode de sauvegarde définie précédemment dans l'objet Form.
Dans l'action de recherche, basée sur les données acquises côté JS (la chaîne de caractères saisie dans le formulaire d'entrée de balise), les données sont extraites de la table des balises avec la clause where + LIKE et renvoyées à JS avec json plus rouge. (Les fichiers JS apparaîtront plus tard.)
C'est pourquoi l'action de recherche ci-dessus n'est pas nécessaire si vous n'implémentez pas de recherche incrémentielle.
new.html.erb
<%= form_with model: @post, url: posts_path, class: 'registration-main', local: true do |f| %>
<div class='form-wrap'>
<div class='form-header'>
<h2 class='form-header-text'>Page de publication de la chronologie</h2>
</div>
<%= render "devise/shared/error_messages", resource: @post %>
<div class="post-area">
<div class="form-text-area">
<label class="form-text">Titre</label><br>
<span class="indispensable">Obligatoire</span>
</div>
<%= f.text_field :title, class:"post-box" %>
</div>
<div class="long-post-area">
<div class="form-text-area">
<label class="form-text">Aperçu</label>
<span class="indispensable">Obligatoire</span>
</div>
<%= f.text_area :content, class:"input-text" %>
</div>
<div class="tag-area">
<div class="form-text-area">
<label class="form-text">marque</label>
<span class="indispensable">Obligatoire</span>
</div>
<%= f.text_field :name, class: "text-box", autocomplete: 'off' %>
</div>
<div>[Balise recommandée]</div>
<div id="search-result">
</div>
<div class="long-post-area">
<div class="form-text-area">
<label class="form-text">Événement programmé</label>
<span class="optional">Tout</span>
</div>
<div class="schedule-area">
<div class="date-area">
<label>Date</label>
<%= f.date_field :date %>
</div>
<div class="time-area">
<label>Heure de début</label>
<%= f.time_field :time_first %>
<label class="end-time">Heure de fin</label>
<%= f.time_field :time_end %>
</div>
</div>
</div>
<div class="register-btn">
<%= f.submit "Publier",class:"register-blue-btn" %>
</div>
</div>
<% end %>
Le fichier de vue utilisé dans l'implémentation de mon application étant solidement collé, le code est redondant, mais le fait est qu'il n'y a pas de problème si le contenu du formulaire peut être envoyé au routage par @post etc.
:index.html.erb
<div class="registration-main">
<div class="form-wrap">
<div class='form-header'>
<h2 class='form-header-text'>Page de liste chronologique</h2>
</div>
<div class="register-btn">
<%= link_to "Accéder à la page de publication de la chronologie", new_post_path, class: :register_blue_btn %>
</div>
<% @posts.each do |post| %>
<div class="post-content">
<div class="post-headline">
<div class="post-title">
<span class="under-line"><%= post.title %></span>
</div>
<div class="more-list">
<%= link_to 'Éditer', edit_post_path(post.id), class: "edit-btn" %>
<%= link_to 'Effacer', post_path(post.id), method: :delete, class: "delete-btn" %>
</div>
</div>
<div class="post-text">
<p>■ Aperçu</p>
<%= post.content %>
</div>
<div class="post-detail">
<% if post.time_end != nil && post.time_first != nil %>
<p>■ Calendrier</p>
<div class="post-date">
<%= post.date %>
<%= post.time_first.strftime("%H heure%M minutes") %> 〜
<%= post.time_end.strftime("%H heure%M minutes") %>
</div>
<% end %>
<div class="post-user-tag">
<div class="post-user">
<% if post.user_id != nil %>
■ Publié par: <%= link_to "#{post.user.name}", user_path(post.user_id), class:'user-name' %>
<% end %>
</div>
<div class="post-tag">
<% post.tags.each do |tag| %>
#<%= tag.name %>
<% end %>
</div>
</div>
</div>
</div>
<% end %>
</div>
</div>
Ceci est également redondant, veuillez donc vous référer uniquement si nécessaire ...
Ici, je joue avec le fichier JS.
tag.js
if (location.pathname.match("posts/new")){
window.addEventListener("load", (e) => {
const inputElement = document.getElementById("post_name");
inputElement.addEventListener('keyup', (e) => {
const input = document.getElementById("post_name").value;
const xhr = new XMLHttpRequest();
xhr.open("GET", `search/?input=${input}`, true);
xhr.responseType = "json";
xhr.send();
xhr.onload = () => {
const tagName = xhr.response.keyword;
const searchResult = document.getElementById('search-result')
searchResult.innerHTML = ''
tagName.forEach(function(tag){
const parentsElement = document.createElement('div');
const childElement = document.createElement("div");
parentsElement.setAttribute('id', 'parents')
childElement.setAttribute('id', tag.id)
childElement.setAttribute('class', 'child')
parentsElement.appendChild(childElement)
childElement.innerHTML = tag.name
searchResult.appendChild(parentsElement)
const clickElement = document.getElementById(tag.id);
clickElement.addEventListener('click', () => {
document.getElementById("post_name").value = clickElement.textContent;
clickElement.remove();
})
})
}
});
})
};
J'utilise location.pathname.match pour charger le code lorsque la nouvelle action dans le contrôleur de messages se déclenche.
En tant que processus approximatif dans JS, ① Déclenchez un événement avec keyup et envoyez la valeur d'entrée du formulaire de balise au contrôleur (environ xhr. 〇〇) (2) Affichez la balise de prédiction au recto en fonction des informations renvoyées par le contrôleur sous xhr.onload. ③ Lorsque vous cliquez sur l'étiquette de prédiction, cette étiquette sera reflétée dans le formulaire.
Ceci termine la mise en œuvre de la fonction de balisage et la mise en œuvre de la recherche incrémentielle. C'est un article approximatif, mais merci d'avoir lu jusqu'au bout!
Recommended Posts