[JAVA] [Ruby on Rails] So implementieren Sie die Tagging- / inkrementelle Suchfunktion für Posts (ohne Gem)

Vorwort vor dem Markieren

Zusammenfassung dieses Artikels

-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.

Entwicklungsumgebung

Mac OS Catalina 10.15.4 Ruby 2.6 Serie Schienen 6.0 Serie

Abgeschlossenes Bild der Tagging-Funktion

demo

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.

Ablauf der Implementierung der Tagging-Funktion

  1. Erstellen Sie Tag, Post, PostTagRelation, Benutzermodell
  2. Bearbeiten Sie Migrationsdateien für verschiedene Modelle
  3. Führen Sie das Formularobjekt ein
  4. Routing-Einstellungen
  5. Erstellen Sie einen Posts-Controller und eine Aktionsdefinition
  6. Erstellen Sie eine Ansichtsdatei
  7. Implementierung der inkrementellen Suche (JavaScript)

Befolgen Sie zur Implementierung die obigen Schritte.

1. Erstellen Sie Tag, Post, PostTagRelation, Benutzermodell

er-figure

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. ..

2. Bearbeiten Sie Migrationsdateien für verschiedene Modelle

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

3. Führen Sie das Formularobjekt ein

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

4. Routing-Einstellungen

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.

5. Erstellen Sie einen Posts-Controller und eine Aktionsdefinition

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.

6. Erstellen Sie eine Ansichtsdatei

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 ...

7. Implementierung der inkrementellen Suche (JavaScript)

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!

Recommended Posts

[Ruby on Rails] So implementieren Sie die Tagging- / inkrementelle Suchfunktion für Posts (ohne Gem)
Implementieren Sie eine verfeinerte Suchfunktion für mehrere Modelle ohne Rails5-Juwel.
[Für Anfänger von Rails] Mehrfachsuchfunktion ohne Gem implementiert
Verwendung von Ruby on Rails
[Ruby on Rails] So vermeiden Sie das Erstellen unnötiger Routen für die Entwicklung
[Ruby on Rails] Suchfunktion (nicht ausgewählt)
So implementieren Sie Suchfunktionen in Rails
Validierungseinstellungen für die Ruby on Rails-Anmeldefunktion
[Rails] So implementieren Sie einen Unit-Test eines Modells
[Für Anfänger] So implementieren Sie die Löschfunktion
So implementieren Sie Paginierung in GraphQL (für Ruby)
[Ruby on Rails] Wie schreibe ich eine Enumeration auf Japanisch?
Rails / Ruby: So erhalten Sie HTML-Text für Mail
[Ruby on Rails] So ändern Sie den Spaltennamen
[Ruby On Rails] So setzen Sie die Datenbank in Heroku zurück
(Ruby on Rails6) So erstellen Sie ein Modell und eine Tabelle
[Ruby on Rails] Suchfunktion (Modell, Methodenauswahlformel)
Erklärung von Ruby on Rails für Anfänger ④ ~ Informationen zu Benennungsregeln und zur Verwendung von form_Tag ~
[Rails] So implementieren Sie Scraping
[Ruby on Rails] Implementieren Sie die Anmeldefunktion von add_token_to_users mit API
So zeigen Sie Diagramme in Ruby on Rails an (LazyHighChart)
[Ruby on Rails] Paging-Funktion eingeführt
Ich möchte eine Browsing-Funktion mit Ruby on Rails hinzufügen
So lösen Sie OpenSSL :: SSL :: SSLError: SSL_connect, das in Ruby paypal-sdk-rest gem auftritt
[R Spec on Rails] So schreiben Sie Testcode für Anfänger von Anfängern
So stellen Sie Bootstrap auf Rails bereit
[Ruby on Rails] CSV-Ausgabefunktion
[Ruby on Rails] Implementierung der Kommentarfunktion
[Rails] So implementieren Sie die Sternebewertung
[Ruby on Rails] DM, Chat-Funktion
(Ruby on Rails6) Erstellen Sie eine Funktion zum Bearbeiten des veröffentlichten Inhalts
[Ruby on Rails] So melden Sie sich nur mit Ihrem Namen und Passwort mit dem Gem-Gerät an
[Ruby on Rails] Beim ersten Anmelden ・ So teilen Sie den Bildschirm mit jQuery in zwei Hälften
[Rails] Implementierungsverfahren der Funktion zum Markieren von Posts ohne Gem + die Funktion zum Eingrenzen und Anzeigen von Posts nach Tags
So erstellen Sie eine Abfrage mithilfe von Variablen in GraphQL [Verwenden von Ruby on Rails]
So erstellen Sie eine Ruby on Rails-Entwicklungsumgebung mit Docker (Rails 6.x)