Stimulusの習作: インタラクティブな削除
チュートリアル
Stimulus.js Tutorial: Interactive Deletes with Rails UJS
こちらのチュートリアルを参考にコードを書いてみました。
このチュートリアルでは、リモート削除リンクを利用して、その場で確認メッセージを追加し、削除要求がサーバーに送信されたときに要素を非表示にしています。リクエストが成功して戻ってくると、要素はページから削除され、エラーが発生すると要素は元の場所に戻されます。
環境
macOS 10.15.4
Ruby 2.6.5
Rails 6.0.2.2
Yarn 1.22.4
Node 13.12.0
stimulus@1.1.1
リポジトリ
アプリの新規作成
stimulusを指定してRailsアプリを新規作成します。
rails new stimulus_tutorial_interactive_deletes_with_rails_ujs --webpack=stimulus
cd stimulus_tutorial_interactive_deletes_with_rails_ujs
stimulusが導入されたことを確認します。
※typoしていてもエラーが出ないので念のため。
cat app/javascript/controllers/index.js
ここまでの成果を保存。
git add .
git commit -m 'rails new stimulus_tutorial_interactive_deletes_with_rails_ujs --webpack=stimulus'
足場の生成
チュートリアルを参考に足場を作成します。
投稿(Post)モデルはタイトルと著者を持ちます。
bin/rails g scaffold post title author
bin/rails db:migrate
ここまでの成果を保存。
git add .
git commit -m 'bin/rails g scaffold post title author'
ダミーデータ生成
書籍のダミーデータを生成するためにGemfileにFakerを追加します。
gem 'faker'
Gemをインストールします。
bundle install
db/seeds.rbにダミーデータの生成処理を記述します。
書籍ではありませんがFaker::Bookを流用しました。
100.times do |i|
Post.create!(title: Faker::Book.title,
author: Faker::Book.author)
print '.' if (i % 10).zero?
end
ダミーデータを生成します。
bin/rails db:seed
ここまでの成果を保存。
git add .
git commit -m "gem 'faker'"
ビューの修正
チュートリアルを参考にindex.html.erbを変更します。
要素の属性にdata-controller="delete"を追加します。
<h1 >All Posts</h1>
<%= link_to "New Post", new_post_path %>
<% @posts.each do |post| %>
<div data-controller="delete">
<p>
<h2><%= link_to post.title, post %></h2>
<strong><%= post.author %></strong>
<br />
<em><%= post.created_at.strftime("%l:%M %P") %></em>
<br />
<%= link_to "Delete", post_path(post), method: :delete, remote: true, data: { target: "delete.link", action: "ajax:beforeSend->delete#click" } %>
</p>
</div>
<% end %>
実際の表示内容が意図したものであることを確認しておきます。
bin/rails s
open http://localhost:3000/posts/
ここまでの成果を保存。
git add .
git commit -m 'data-controller="delete"'
コントローラーの作成
チュートリアルを参考にdelete_controller.jsを追加します。
const RESET_TIMEOUT_MILLIS = 3000;
const CONFIRMATION_MESSAGE = '<strong>Are you sure?</strong>';
import { Controller } from "stimulus"
export default class extends Controller {
static targets = ['link']
initialize() {
this.handleSuccess = (event) => {
clearTimeout(this.timeout)
this.element.parentNode.removeChild(this.element)
}
this.handleError = (event) => {
clearTimeout(this.timeout)
this.resetState()
this.element.style = ''
}
}
connect() {
this.delete = false
}
click(event) {
if (this.delete) {
this.element.style = 'display: none;'
this.linkTarget.addEventListener('ajax:success', this.handleSuccess)
this.linkTarget.addEventListener('ajax:error', this.handleError)
} else {
this.oldMessage = this.linkTarget.innerHTML
this.linkTarget.innerHTML = CONFIRMATION_MESSAGE
this.delete = true
this.timeout = setTimeout(() => {
this.resetState()
}, RESET_TIMEOUT_MILLIS)
event.preventDefault()
return false
}
}
resetState() {
if (this.delete) {
this.linkTarget.removeEventListener('ajax:success', this.handleSuccess)
this.linkTarget.removeEventListener('ajax:error', this.handleError)
this.linkTarget.innerHTML = this.oldMessage
this.delete = false
}
}
}
意図通りに動作していることを確認できたら、ここまでの成果を保存。
git add .
git commit -m 'delete_controller.js'
お題に挑戦
Try to add something to alert the deleter on the page when the deletion fails. It can assume that failure isn’t a likely case, so the error message should be very disruptive to the page flow.
ということで、削除に失敗したときにアラートを表示してみます。
app/controllers/posts_controller.rb
必ず削除に失敗するようにして、
def destroy
respond_to do |format|
if false # @post.destroy
format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
else
format.html {
@posts = Post.all
render :index
}
format.json { head :unprocessable_entity }
end
end
end
app/views/posts/index.html.erb
JSON側をたたくように変えてみました。
<%= link_to "Delete", post_path(post, format: :json), method: :delete, remote: true, data: { target: "delete.link", action: "ajax:beforeSend->delete#click" } %>
app/javascript/controllers/delete_controller.js
アラート表示を追加しました。
this.handleError = (event) => {
clearTimeout(this.timeout)
this.resetState()
this.element.style = ''
alert("Failed to delete.")
}
意図通りに動作していることを確認できたら、ここまでの成果を保存。
git add .
git commit -m 'Add alert for failed deletion'
この記事が気に入ったらサポートをしてみませんか?