[Ruby on Rails] Ajoutez et supprimez des balises et affichez les messages (succès / erreur) en utilisant ajax.

environnement

Ruby 2.5.7 Rails 5.2.4

Bibliothèque

jQuery 1.12.4

supposition

Cette fois, je vais vous expliquer en utilisant la table Tag.

Tout d'abord, veuillez confirmer à l'avance que vous pouvez ajouter et supprimer des balises sans utiliser ajax.

Je ne parlerai pas de la localisation japonaise des messages d'erreur et des associations, donc si vous en avez besoin, faites-le vous-même.

turbolinks est désactivé. (Nous n'avons pas confirmé l'opération lorsqu'elle est activée.)

procédure

Comme le titre l'indique, nous utiliserons ajax pour ajouter / supprimer des balises et afficher des messages d'erreur dans les étapes suivantes.

  1. Paramètres de validation dans tag.rb
  2. Préparation de modèles partiels (views / layouts / _error_messages.html.erb, views / layouts / _flash_messages.html.erb, views / tags / _tag.html.erb)
  3. Créer un écran de liste de balises et un nouveau formulaire (vues / balises / index.html.erb)
  4. Préparez js.erb pour chaque action (vues / tags / create.js.erb, vues / tags / destroy.js.erb)
  5. Préparation de tags_controller.rb (: index ,: create ,: destroy)

En tant qu'image, lorsque vous appuyez sur le bouton d'envoi, l'action de création du contrôleur est exécutée. La méthode qui appelle create.js.erb est exécutée dans l'action de création JavaScript (jQuery) écrit dans create.js.erb se déclenche La manipulation DOM écrase l'élément de message d'erreur et l'élément de liste de balises (méthode jQuery .html ()) La même chose s'applique à l'action de destruction lors de la suppression.

Comme le flux du formulaire rempli

  1. Entrez une valeur dans le formulaire sur l'écran d'index. 1-1. Enregistrer une nouvelle balise si elle n’existe pas (message de réussite) 1-2. Si la balise existe, enregistrez l'échec (message d'erreur de validation) 1-3. Si ce champ est vide, l’enregistrement a échoué (message d’erreur de validation)

  2. Supprimer des balises individuelles 1-1. Afficher le message de réussite

Comme tous sont ajax, la page entière ne sera pas mise à jour.

1. Paramètres de validation dans tag.rb

tag.rb


class Tag < ApplicationRecord

  ...

  #Interdit les blancs et accorde l'unicité (dupliquer NG).
  validates :name, presence: true, uniqueness: true

end

2. Préparation de modèles partiels (layouts / _error_messages.html.erb, layouts / _flash_messages.html.erb, _tag.html.erb)

Les trois modèles partiels créés ici sont écrasés par les opérations DOM en fonction de l'action. Si vous écrivez dans jQuery, il sera redondant car vous ferez des opérations DOM ligne par ligne, utilisez donc des modèles partiels pour que jQuery décrit plus tard puisse être fait avec <% = render ...%> 1 ligne. Il est devenu.

Modèle partiel de message d'erreur de validation Si vous en avez déjà un, vous pouvez l'utiliser.

erb:layouts/_error_messages.html.erb


<% if model.errors.any? %>
  <div id="validation-errors">
    <p><%= model.errors.count %>Il y a des erreurs.</p>
    <ul class="error-messages">
      <% model.errors.full_messages.each do |message| %>
        <li class="error-message"><%= message %></li>
      <% end %>
    </ul>
  </div>
<% end %>

S'il y a une erreur de validation lors de l'écrasement par la manipulation DOM de jQuery, un message d'erreur sera affiché. Remplacez la variable locale «model» par «@ tag» (Tag.new) dans index.html.erb, qui sera décrite plus tard.

Ensuite, un modèle partiel pour les messages de réussite (messages flash) Si vous l'avez déjà, veuillez l'utiliser.

erb:layouts/_flash_messages.html.erb


<% flash.each do |key, value| %>
  <p class="alert alert-<%= key %>">
    <%= value %>
  </p>
<% end %>

Il est supposé obtenir du tableau avec chacun, mais ce n'est pas pour obtenir plusieurs messages flash, mais n'importe quelle touche (flash [key]) peut être réaffiche avec cette phrase Je vais. Il existe deux types de clés utilisées cette fois: [: success] et [: warning]. (Au fait, ces deux types sont également disponibles en bootstrap, j'ai donc choisi cette fois.) Si vous utilisez ces deux éléments, vous pouvez réécrire le modèle partiel comme suit.

erb:layouts/_flash_messages.html.erb


<% case flash.keys[0] %>
  <% when "success" %>
  <p class="alert alert-<%= flash.keys[0] %>">
    <%= flash[:success] %>
  </p>
  <% when "warning" %>
  <p class="alert alert-<%= flash.keys[0] %>">
    <%= flash[:warning] %>
  </p>
<% end %>

Les clés stockées dans la mémoire flash sont des tableaux, elles doivent donc être clés [0]. Modifiez le message flash à afficher en fonction du modèle de touches dans l'instruction de cas. La raison d'utiliser keys [0] dans la classe de l'élément p est de changer la conception du css en fonction de la clé (succès ou avertissement dans ce cas).

Cependant, cela le rend redondant car il est nécessaire d'augmenter le nombre de lignes à chaque fois que le nombre de types de clés (les noms de clés peuvent être donnés arbitrairement) augmente.

Par conséquent, cette fois, en utilisant l'instruction each, le problème est éliminé. De plus, en en faisant un modèle partiel, il peut être réutilisé dans d'autres vues ainsi que dans le message d'erreur de validation.

Modèle partiel pour afficher la liste des balises ensuite

erb:tags/_tag.html.erb


<div class="tags-index__tags--tag">
  <%= tag.name %>(<%= tag.menu_tag_ids.count %>)
  <%= link_to 'X', tag_path(tag), method: :delete, remote: true %>
</div>

Si vous n'utilisez pas de modèle partiel, vous devez écrire 4 lignes avec jQuery, ce qui sera décrit plus tard, et je veux gérer les opérations DOM avec jQuery aussi simplement que possible, donc j'en ai fait un modèle partiel. La variable locale «tag» est automatiquement remplacée en tant que variable locale individuelle «tag» dans le modèle partiel en spécifiant la variable d'instance «@ tags» (Tag.all) dans index.html.erb. (J'expliquerai en détail dans index.html.erb.) (<% = tag.menu_tag_ids.count%>) indique le nombre de menus pour lesquels la balise est utilisée à côté du nom de la balise. (Tableau de menu (omis) 1: Tableau d'étiquettes de menu multiple (tableau intermédiaire) (omis) Tableau d'étiquettes multiples: 1 (cette fois)) Pour <% = link_to'X ', tag_path (tag), method :: delete, remote: true%>, appuyer sur X déclenchera l'action de destruction. En définissant remote: true, il est spécifié qu'il s'agit d'une communication ajax. <détails>

Comment fonctionne "remote: true" </ summary> Dans une requête normale (lien), HTML est acquis, mais dans le cas d'une requête avec remote: true (data-remote = "true" ʻattribute après la conversion html), seul le fichier JS est acquis. .. <br> Dans le cas de Rails, il y a une balise qui lit js dans la balise` de layouts / application.html.erb, donc normalement lorsque HTML est acquis, js est également rechargé automatiquement. Cela ne s'applique pas si vous utilisez des turbolinks. </ datails>

3. Créer un écran de liste de balises et un nouveau formulaire (vues / balises / index.html.erb)

Nous allons également créer un écran de liste de balises et un nouveau formulaire de saisie.

erb:views/tags/index.html.erb


<div class="contents tags-index">
  <div class="tags-index--messages"><!--Zone d'affichage des messages--></div>
  <h2>Liste de balises</h2>
  <div class="tags-index__list" id="tags-index--tag-list">
    <%= render @tags %>
  </div>
  <div class="tags-index__form">
    <%= form_with model: @tag, url: tags_path(@tag) do |f| %>
      <%= f.text_field :name %>
      <%= f.submit %>
    <% end %>
  </div>
</div>

Le formulaire nouvellement ajouté est attaché au bas de la liste des balises.

Le nouveau formulaire d'inscription utilise form_with. form_with est optional car la valeur par défaut a l'attribut remote: true. (Si vous utilisez form_for, vous devez spécifier l'attribut remote: true.) Si vous n'utilisez pas ajax dans form_with, vous devez spécifier l'attribut local: true. L'assistant de formulaire indique à l'action uniquement JS si remote: true et HTML si local: true. De plus, étant donné que la méthode HTTP par défaut de l'assistant de formulaire est également POST, method :: post est également __optional __.

La variable d'instance «@ tags» de «<% = render @ tags%>« obtient «Tag.all» du côté du contrôleur décrit plus loin. C'est une manière peu familière d'écrire un modèle partiel, mais dans ce modèle partiel d'abréviation, _variable name.html.erb correspondant au nom de la variable est appelée, et dans ce modèle partiel, une variable locale (dans le modèle partiel dans ce cas Il peut être utilisé comme tag). Si vous l'obtenez sous forme de tableau comme @ tags` cette fois, il sera affiché à plusieurs reprises autant de fois que le numéro du tableau dans le modèle partiel. Cela peut être réécrit comme suit.

erb:views/tags/index.html.erb


<%= render partial: 'tag', locals: {tags: @tags} %>

erb:views/tags/_tag.html.erb


<% tags.each do |tag| %>
  <div class="tags-index__tags--tag">
    <%= tag.name %>(<%= tag.menu_tag_ids.count %>)
    <%= link_to 'X', tag_path(tag), method: :delete, remote: true %>
  </div>
<% end %>

La méthode de description cette fois est une abréviation de ceux-ci. [Rails Guide-Layout and Rendering](https://railsguides.jp/layouts_and_rendering.html#%E3%83%91%E3%83%BC%E3%82%B7%E3%83%A3%E3%83% AB% E3% 82% 92% E4% BD% BF% E7% 94% A8% E3% 81% 99% E3% 82% 8B)

4. Préparez js.erb pour chaque action (vues / tags / create.js.erb, vues / tags / destroy.js.erb)

Créez js (.erb) à appeler depuis le contrôleur. En décrivant js ici, l'opération DOM est effectuée par jQuery qui se déclenche lorsque create et destroy sont respectivement appelés depuis le contrôleur. Le .html () utilisé ici sera ajouté s'il n'y a pas d'élément, et sera réécrit s'il y a déjà un autre élément.

js (.erb) au moment de l'action de création

erb:views/tags/create.js.erb


<% if @tag.errors.any? %>
  $(".tags-index--messages").html("<p><%= j(render partial: 'layouts/error_messages', locals: {model: @tag}) %></p>")
<% else %>
  $(".tags-index--messages").html("<p><%= j(render partial: 'layouts/flash_messages', locals: {model: @tag}) %></p>")
  $("#tags-index--tag-list").html("<%= j(render @tags) %>")
  $("#tag_name").val('')
<% end %>

<% if @ tag.errors.any?%> Vérifie s'il y a une erreur dans @ tag lorsque le bouton d'envoi est enfoncé. <détails>

Différence entre "object.errors.any?" et "object.invalid?" </ Summary> Dans ce cas, puisque create.js.erb est appelé après que @ tag.save a été essayé du côté contrôleur, js.erb est dans un état où il peut être vu si une erreur s'est déjà produite dans @ tag. J'arrive. D'autre part, la méthode ʻinvalid? Valide manuellement l'objet non sauvegardé. Normalement, le contrôle de validation est automatiquement exécuté lorsque l'objet est sur le point d'être sauvegardé (lorsque .save est essayé), et cette fois l'action est divisée en fonction du succès ou de l'échec de .save, donc manuellement ʻinvalide? À nouveau. Plutôt que de le faire, il vaut mieux vérifier si une erreur s'est déjà produite au moment de .save (.errors.any?). </ détails>

Si une erreur se produit (espaces vides ou noms de balises en double) -Ajouter ou réécrire les messages d'erreur de validation (error_messages.html.erb) à $ (". Tags-index - messages ")

Si aucune erreur ne s'est produite (la sauvegarde a réussi) -Ajouter ou réécrire des messages flash (flash_message.html.erb) à $ (". Tags-index - messages ") -Récrire la liste des balises ($ (" # tags-index - tag-list ")) -Effacer le nom du tag saisi sous la forme ($ (" # tag_name "). Val (''))

Sera exécuté.

js (.erb) pendant l'action de destruction

erb:views/tags/destroy.js.erb


$(".tags-index--messages").html("<p><%= j(render partial: 'layouts/flash_messages', locals: {model: @tag}) %></p>")
$("#tags-index--tag-list").html("<%= j(render @tags) %>")

Identique à la création. -Ajouter ou réécrire un message flash lorsque la balise est supprimée ・ Réécriture de la liste des tags

Sera exécuté.

"Nécessité de partial:" Si vous devez ajouter une option en utilisant render (`locals: {tag: @tag}` dans ce cas), le nom du fichier (chemin) Vous devez spécifier `partial:` avant. [Rails Guide-Layout and Rendering](https://railsguides.jp/layouts_and_rendering.html#%E3%83%91%E3%83%BC%E3%82%B7%E3%83%A3%E3%83% AB% E3% 82% 92% E4% BD% BF% E7% 94% A8% E3% 81% 99% E3% 82% 8B)

5. Préparation de tags_controller.rb (: index ,: create ,: destroy)

Je décrirai les actions nécessaires cette fois sur le contrôleur

tags_controller.rb


class TagsController < ApplicationController

  def index
    @tags = Tag.all
    @tag = Tag.new
  end

  def create
    @tags = Tag.all
    @tag = Tag.new(tag_params)

    respond_to do |format|
      if @tag.save
        format.js { flash.now[:success] = "Je l'ai sauvé." }
      else
        format.js
      end
    end
  end

  def destroy
    @tags = Tag.all
    @tag = Tag.new
    Tag.find(params[:id]).destroy
    flash.now[:warning] = "Il a été supprimé."
  end

  private
  def tag_params
    params.require(:tag).permit(:name)
  end

end

Cette fois, form_with de create et link_to de destroy spécifient chacun remote: true (dans le cas de form_with, il est omis car c'est la valeur par défaut), donc les deux ne demandent que JS.

tags_controller.rb


def create

  ...

  respond_to do |format|
    if @tag.save
      format.js { flash.now[:success] = "Je l'ai sauvé." }
    else
      format.js
    end
  end
end

respond_to do |format|Est un paramètre de bloc qui divise le traitement en fonction du format de la demande. Cette fois, nous ne supposons pas qu'une demande viendra en HTML (local: true) pour ajouter / supprimer des balises, donc seulformat.js {traitement} ʻest décrit. Si vous voulez séparer le traitement lorsqu'une requête en HTML arrive, vous pouvez écrireformat.html {processing} et diviser le traitement selon la méthode de la requête. Si ʻif @ tag.save réussit, mettez le message "Saved." ʻIn the flash [: success] clé et passez-le à create.js.erb. En cas d'échec, seul le message d'erreur de validation sera affiché, donc le message flash ne sera pas utilisé, donc aucun traitement ne sera écrit après format.js`, seul l'affichage sera affiché. Comme mentionné ci-dessus, le «@ tag» où l'erreur de validation s'est produite à ce moment est passé à create.js.erb, et le traitement d'erreur (affichage du message d'erreur de validation) y est effectué.

L'action de destruction met le message "" Supprimé. "Sur la toucheflash [: warning]` et le transmet à destroy.js.erb.

Lors de l'écriture de "format.js" et lorsque vous n'écrivez pas Normalement, le contrôleur a un nom d'action avec le même nom qui correspond au fichier de vue. (index.html.erb pour «def index end»)
Sauf indication contraire dans le contenu du nom de l'action, comme «render» ou «redirect_to», «render: action name» est omis à la fin de l'action. Il fonctionne (implicitement). (C'est pourquoi la vue est rendue sans écrire le contenu, tel que `def index end`.) Cette fois, dans l'action de création, je veux passer un message flash en fonction de la condition, j'ai donc explicitement décrit `format.js` et divisé le traitement dedans, mais dans l'action de destruction, il n'est pas nécessaire de séparer le traitement jusqu'à présent , `Respond_to`,` format.js`, `render: destroy` ne sont pas décrits. Le `render: destroy` est déjà mentionné implicitement, vous n'avez donc pas à l'écrire. Comme je l'ai écrit au début, le `render: action name` est implicitement déclenché uniquement lorsqu'il n'y a pas d'autre` render: another action name` ou` redirect_to` dans l'action, donc d'un autre côté l'action create Si vous écrivez explicitement format.js et que vous l'exécutez, le "render: create" implicite ne sera pas exécuté à la fin de l'action. (C'est la raison pour laquelle l'erreur de rendu double ne se produit pas.)

[Rails Guide-Overview of Action Controller](https://railsguides.jp/action_controller_overview.html#%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%81 % A8% E3% 82% A2% E3% 82% AF% E3% 82% B7% E3% 83% A7% E3% 83% B3) Pikawaka - resoond_to [Qiita- [Rails] Méthode détaillée et explication pour afficher des messages simples à l'aide de messages flash](https://qiita.com/dice9494/items/2a0e92aba58a516e42e9#flash%E3%81%A8flashnow%E3%81%AE] % E4% BD% BF% E3% 81% 84% E5% 88% 86% E3% 81% 91% E6% 96% B9% E3% 81% AF)

Résumé

Si vous avez des questions, des différences d'interprétation ou si vous pensez que quelque chose ne va pas avec la méthode de description, nous vous serions reconnaissants de bien vouloir le signaler dans les commentaires.

Merci d'avoir lu jusqu'au bout.

Site de référence

[Rails Guide-Layout and Rendering](https://railsguides.jp/layouts_and_rendering.html#%E3%83%91%E3%83%BC%E3%82%B7%E3%83%A3%E3%83% AB% E3% 82% 92% E4% BD% BF% E7% 94% A8% E3% 81% 99% E3% 82% 8B) [Rails Guide-Overview of Action Controller](https://railsguides.jp/action_controller_overview.html#%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%81 % A8% E3% 82% A2% E3% 82% AF% E3% 82% B7% E3% 83% A7% E3% 83% B3) Pikawaka - resoond_to [Qiita- [Rails] Méthode détaillée et explication pour afficher des messages simples à l'aide de messages flash](https://qiita.com/dice9494/items/2a0e92aba58a516e42e9#flash%E3%81%A8flashnow%E3%81%AE] % E4% BD% BF% E3% 81% 84% E5% 88% 86% E3% 81% 91% E6% 96% B9% E3% 81% AF)

Recommended Posts