-Ermöglichen das Markieren von Posts. -Implementieren Sie eine Funktion (inkrementelle Suche), die jedes Mal automatisch sucht, wenn ein Zeichen in ein Tag eingegeben wird.
Mac OS Catalina 10.15.4 Ruby 2.6 Serie Schienen 6.0 Serie
Wie bei Gif in der obigen Abbildung können beim Eingeben von Tags empfohlene Tags basierend auf den in der Datenbank gespeicherten Tags angezeigt werden. Wenn wir die Tagging-Funktion basierend auf diesem Artikel implementieren können, kann die Tag-Suche usw. meiner Meinung nach leicht implementiert werden.
Befolgen Sie zur Implementierung die obigen Schritte.
Lassen Sie uns zunächst verschiedene Modelle vorstellen.
% rails g model tag
% rails g model post
% rails g model post_tag_relation
% rails g devise user
Lassen Sie uns die Validierung beschreiben, indem Sie jedes eingeführte Modell zuordnen.
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
Durch Setzen von "through :: Zwischentabelle" werden das Post-Modell und das Tag-Modell, die eine Viele-zu-Viele-Beziehung haben, zugeordnet. Als Einschränkung ist es erforderlich, die Zwischentabelle zu verknüpfen, bevor auf durch verwiesen wird. (Da der Code von oben gelesen wird, tritt ein Fehler auf, wenn Sie in der Reihenfolge has_many: posts über :: post_tag_relations → has_many: post_tag_relations schreiben.)
post_tag_relation
class PostTagRelation < ApplicationRecord
belongs_to :post
belongs_to :tag
end
user.rb
class User < ApplicationRecord
#<Kürzung>
has_many :posts, dependent: :destroy
validates :name, presence: true
Die Option has_many im Benutzermodell ist als abhängig :: zerstören gekennzeichnet, sodass beim Löschen der Benutzerinformationen des übergeordneten Elements auch dieser menschliche Beitrag gelöscht wird.
Darüber hinaus wird die Beschreibung (validiert: 〇〇, Anwesenheit: true), um zu verhindern, dass leere Daten im Post-Modell und im Tag-Modell gespeichert werden, gemeinsam in dem später zu erstellenden Formularobjekt angegeben, sodass dies jetzt nicht erforderlich ist. ..
Fügen Sie als Nächstes Spalten zum erstellten Modell hinzu. (Die Mindestanforderung ist die Spalte mit dem Tag-Namen. Ordnen Sie die anderen nach Ihren Wünschen an.)
Post-Migrationsdatei
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
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
post_tag_
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
Tag-Migrationsdatei Der Grund, warum die Eindeutigkeit: true auf die obige Namensspalte angewendet wird, wird eingeführt, um das Duplizieren von Tag-Namen zu verhindern. (Da davon ausgegangen wird, dass Tags mit demselben Namen häufig verwendet werden, fragen Sie sich möglicherweise, ob sie nicht als Tagging-Funktion funktionieren, wenn Sie Duplikate verhindern. Wie Sie vorhandene Tags in Posts widerspiegeln, wird später beschrieben. Einführung.) Beziehungsmigrationsdatei Dieses post_tag_relation-Modell dient als Zwischentabelle zwischen den post- und tag-Modellen, die eine Viele-zu-Viele-Beziehung haben. Benutzermigrationsdatei
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: ""
#<Kürzung>
Ich wollte den Benutzernamen verwenden, also habe ich eine Namensspalte hinzugefügt.
Vergessen Sie nicht, den folgenden Befehl auszuführen, nachdem Sie die Spalte bearbeitet haben.
% rails db:migrate
In dieser Implementierung möchten wir die Eingabewerte aus dem Post-Formular gleichzeitig in der Posts-Tabelle und der Tags-Tabelle speichern, sodass wir das Form-Objekt verwenden.
Erstellen Sie zunächst ein Formularverzeichnis im App-Verzeichnis und erstellen Sie darin eine posts_tag.rb-Datei. Definieren Sie dann eine Speichermethode, um die Werte in den Posts- und Tag-Tabellen gleichzeitig wie unten gezeigt zu speichern.
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
Stellen Sie als Nächstes das Routing so ein, dass der Index neu ausgeführt und Aktionen des Posts-Controllers erstellt werden.
routes.rb
resources :posts, only: [:index, :new, :create] do
collection do
get 'search'
end
end
Das Routing zu der in der Sammlung definierten Suchaktion wird von der inkrementellen Suchfunktion verwendet.
Generieren Sie eine Steuerung im Terminal.
% rails g controller posts
Der Code in der generierten Posts-Controller-Datei sieht folgendermaßen aus:
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
In der Aktion "Erstellen" wird der von posts_params empfangene Wert im Posts-Modell und in der Tags-Tabelle mithilfe der zuvor im Formularobjekt definierten Speichermethode gespeichert.
Bei der Suchaktion werden die Daten basierend auf den auf der JS-Seite erfassten Daten (der im Tag-Eingabeformular eingegebenen Zeichenfolge) mit der where + LIKE-Klausel aus der Tags-Tabelle abgerufen und mit reder json an JS zurückgegeben. (JS-Dateien werden später angezeigt.)
Aus diesem Grund ist die oben beschriebene Suchaktion nicht erforderlich, wenn Sie keine inkrementelle Suche implementieren.
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'>Timeline-Posting-Seite</h2>
</div>
<%= render "devise/shared/error_messages", resource: @post %>
<div class="post-area">
<div class="form-text-area">
<label class="form-text">Titel</label><br>
<span class="indispensable">Verpflichtend</span>
</div>
<%= f.text_field :title, class:"post-box" %>
</div>
<div class="long-post-area">
<div class="form-text-area">
<label class="form-text">Überblick</label>
<span class="indispensable">Verpflichtend</span>
</div>
<%= f.text_area :content, class:"input-text" %>
</div>
<div class="tag-area">
<div class="form-text-area">
<label class="form-text">Etikett</label>
<span class="indispensable">Verpflichtend</span>
</div>
<%= f.text_field :name, class: "text-box", autocomplete: 'off' %>
</div>
<div>[Empfohlenes Tag]</div>
<div id="search-result">
</div>
<div class="long-post-area">
<div class="form-text-area">
<label class="form-text">Veranstaltungsplan</label>
<span class="optional">Irgendein</span>
</div>
<div class="schedule-area">
<div class="date-area">
<label>Datum</label>
<%= f.date_field :date %>
</div>
<div class="time-area">
<label>Startzeit</label>
<%= f.time_field :time_first %>
<label class="end-time">Endzeit</label>
<%= f.time_field :time_end %>
</div>
</div>
</div>
<div class="register-btn">
<%= f.submit "Post",class:"register-blue-btn" %>
</div>
</div>
<% end %>
Da die in meiner Anwendungsimplementierung verwendete Ansichtsdatei fest eingefügt ist, ist der Code redundant, aber der Punkt ist, dass es kein Problem gibt, wenn der Inhalt des Formulars per @post usw. an das Routing gesendet werden kann.
:index.html.erb
<div class="registration-main">
<div class="form-wrap">
<div class='form-header'>
<h2 class='form-header-text'>Timeline-Listenseite</h2>
</div>
<div class="register-btn">
<%= link_to "Wechseln Sie zur Timeline-Buchungsseite", 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 'Bearbeiten', edit_post_path(post.id), class: "edit-btn" %>
<%= link_to 'Löschen', post_path(post.id), method: :delete, class: "delete-btn" %>
</div>
</div>
<div class="post-text">
<p>■ Übersicht</p>
<%= post.content %>
</div>
<div class="post-detail">
<% if post.time_end != nil && post.time_first != nil %>
<p>■ Zeitplan</p>
<div class="post-date">
<%= post.date %>
<%= post.time_first.strftime("%Uhr%M Minuten") %> 〜
<%= post.time_end.strftime("%Uhr%M Minuten") %>
</div>
<% end %>
<div class="post-user-tag">
<div class="post-user">
<% if post.user_id != nil %>
■ Gepostet von: <%= 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>
Dies ist ebenfalls redundant. Bitte beziehen Sie sich nur bei Bedarf auf ...
Hier spiele ich mit der JS-Datei.
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();
})
})
}
});
})
};
Ich verwende location.pathname.match, um den Code zu laden, wenn die neue Aktion im Posts-Controller ausgelöst wird.
Als grober Prozess in JS, ① Feuern Sie ein Ereignis mit Keyup ab und senden Sie den Eingabewert des Tag-Formulars an den Controller (um xhr. 〇〇) (2) Zeigen Sie das Vorhersage-Tag auf der Vorderseite basierend auf den Informationen an, die vom Controller unter xhr.onload zurückgegeben wurden. ③ Wenn Sie auf das Vorhersage-Tag klicken, wird dieses Tag im Formular angezeigt.
Damit ist die Implementierung der Tagging-Funktion und die Implementierung der inkrementellen Suche abgeschlossen. Es ist ein grober Artikel, aber danke, dass Sie bis zum Ende gelesen haben!