Ruby 2.5.7 Rails 5.2.4
jQuery 1.12.4
This time, I will explain using the Tag table.
First of all, please confirm in advance that you can add and remove tags without using ajax.
I will not touch on the Japanese localization of error messages and associations, so if you need it, please do it yourself.
turbolinks is disabled. (We have not confirmed the operation when it is enabled.)
As the title says, we will implement tag addition / deletion / error message display using ajax in the following procedure.
views / layouts / _error_messages.html.erb
, views / layouts / _flash_messages.html.erb
, views / tags / _tag.html.erb
)As an image, when you press the submit button, the controller create action is executed
The method that calls create.js.erb is executed in the create action
JavaScript (jQuery) written in create.js.erb fires
DOM manipulation overwrites error message element and tag list element (jQuery .html ()
method)
The same applies to the destroy action when deleting.
As the flow of the completed form
Enter the value in the form on the index screen. 1-1. Save a new tag if it does not exist (success message) 1-2. If the tag exists, save failure (validation error message) 1-3. If blank, save failure (validation error message)
Delete individual tags 1-1. Display success message
Since everything is ajax, there is no update of the entire page.
tag.rb
class Tag < ApplicationRecord
...
#Prohibits blanks and grants uniqueness (duplicate NG).
validates :name, presence: true, uniqueness: true
end
layouts / _error_messages.html.erb
, layouts / _flash_messages.html.erb
, _tag.html.erb
)The three partial templates created here are overwritten by DOM operations according to the action.
If you write in jQuery, it will be redundant because you will be doing DOM operations line by line, so use a partial template so that jQuery described later can be done with <% = render ...%>
1 line It has become.
Partial template for validation error messages If you already have one, you can use it.
erb:layouts/_error_messages.html.erb
<% if model.errors.any? %>
<div id="validation-errors">
<p><%= model.errors.count %>There are some errors.</p>
<ul class="error-messages">
<% model.errors.full_messages.each do |message| %>
<li class="error-message"><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
If there is a validation error when overwritten by jQuery's DOM manipulation, an error message will be displayed.
Replace the local variable model
with @ tag
(Tag.new) in index.html.erb, which will be described later.
Next, a partial template for success messages (flash messages) If you already have this, please use it.
erb:layouts/_flash_messages.html.erb
<% flash.each do |key, value| %>
<p class="alert alert-<%= key %>">
<%= value %>
</p>
<% end %>
It is supposed to get from the array with each, but this is not to get multiple flash messages, but any key (flash [key]
) can be redisplayed with this one sentence I will.
There are two types of keys used this time: [: success]
and [: warning]
. (By the way, these two types are also available in bootstrap, so I chose this time.)
If you use these two, you can rewrite the partial template as follows.
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 %>
The keys stored in flash are arrays, so they must be keys [0]
.
Change the flash message to be displayed according to the key pattern in the case statement.
The reason for using keys [0]
in the class of the p element is to change the design of css depending on the key (success or warning in this case).
However, this is redundant because it is necessary to increase the number of rows each time the number of key types (key names can be arbitrarily given) increases.
Therefore, this time, by using the each statement, the trouble is eliminated. Also, by making it a partial template, it can be reused in other views as well as validation error messages.
Partial template to display the tag list next
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>
If you do not use a partial template, you need to write 4 lines with jQuery, which will be described later, and I want to manage DOM operations with jQuery as simply as possible, so I made it a partial template.
The local variable tag
is automatically replaced as an individual local variable tag
in the partial template by specifying the instance variable @ tags
(Tag.all) in index.html.erb. (I will explain in detail in index.html.erb.)
(<% = tag.menu_tag_ids.count%>)
shows the number of menus that use that tag next to the tag name. (Menu table (omitted) 1: Multi-menu tag table (intermediate table) (omitted) Multi: 1 tag table (this time))
For <% = link_to'X', tag_path (tag), method:: delete, remote: true%>
, pressing X
will trigger the destroy action. By setting remote: true
, it is specified that it is ajax communication.
We will also create a tag list screen and a new input form in it.
erb:views/tags/index.html.erb
<div class="contents tags-index">
<div class="tags-index--messages"><!--Message display area--></div>
<h2>Tag list</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>
The newly added form is attached to the bottom of the tag list.
The new registration form uses form_with.
form_with is optional because the default has the remote: true
attribute. (When using form_for, it is necessary to specify the remote: true
attribute.)
If you don't use ajax in form_with, you need to specify the local: true
attribute.
The form helper tells the action only JS if remote: true
and HTML if local: true
.
The form helper also has a default HTTP method of POST, so method:: post
is also __optional __.
The instance variable @tags
of<% = render @tags%>
is gettingTag.all
on the controller side described later.
It is an unfamiliar way to write a partial template, but in this abbreviation partial template, _variable name.html.erb
corresponding to the variable name is called, and the local variable ( in the partial template in this case) is called in that partial template. It can be used as tag
).
If you get it as an array like @ tags
this time, it will be displayed repeatedly as many times as the number of arrays in the partial template.
This can be rewritten as follows.
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 %>
The description method this time is an abbreviation of these. [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)
Create js (.erb) to call from the controller.
By writing js here, DOM operation will be performed with jQuery that fires when create and destroy are called from the controller respectively.
The .html ()
used here will be added if there is no element there, and will be rewritten if there is already another element.
js (.erb) at the time of create action
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?%>
Checks if there is an error in @ tag
when the submit button is pressed.
If an error occurs (blanks or duplicate tag names)
-Add or rewrite validation error messages (error_messages.html.erb) to $ (". tags-index--messages ")
If no error has occurred (save was successful)
-Add or rewrite flash message (flash_message.html.erb) to $ (". tags-index--messages ")
-Rewrite the tag list ($ ("# tags-index--tag-list ")
)
-Clear the tag name entered in the form ($ ("# tag_name "). Val ('')
)
Will be executed.
js (.erb) during destroy action
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) %>")
Same as when creating. -Add or rewrite flash message when tag is deleted ・ Rewriting the tag list
Will be executed.
I will describe the necessary actions this time on the controller
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] = "I saved it." }
else
format.js
end
end
end
def destroy
@tags = Tag.all
@tag = Tag.new
Tag.find(params[:id]).destroy
flash.now[:warning] = "It has been deleted."
end
private
def tag_params
params.require(:tag).permit(:name)
end
end
This time, form_with of create and link_to of destroy are each specifying remote: true
(in the case of form_with, it is omitted because it is the default), so both are requesting only JS.
tags_controller.rb
def create
...
respond_to do |format|
if @tag.save
format.js { flash.now[:success] = "I saved it." }
else
format.js
end
end
end
respond_to do |format|
Is a block parameter that divides the process according to the request format.
This time, we do not assume that a request will come in HTML (local: true
) for adding / deleting tags, so onlyformat.js {processing}
is described.
If you want to separate the processing when a request in HTML comes, you can write format.html {processing}
and divide the processing according to the request method.
If ʻif @ tag.save is successful, put the message
"Saved." In the
flash [: success] key and pass it to create.js.erb. If it fails, only the validation error message is displayed, so the flash message is not used, so no processing is written after
format.js, only the display is performed. As mentioned above, the
@ tag` where the validation error occurred at this time is passed to create.js.erb, and the error processing (display of the validation error message) is performed there.
The destroy action puts the message " deleted. "
On the flash [: warning]
key and passes it to destroy.js.erb.
[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] Detailed method and explanation for displaying simple messages using flash messages](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)
If you have any questions, differences in interpretation, or any discomfort in the description method, we would appreciate it if you could point them out in the comments.
Thank you for reading until the end.
[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] Detailed method and explanation for displaying simple messages using flash messages](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