RailsアプリでDeviseを利用してユーザー認証をしています。パスワードの変更画面ではこれまでのパスワード、新しく設定するパスワード、新しく設定するパスワード(確認)の3つのテキストフォームがあります。
確認用のパスワードが間違っていた場合に画面遷移してエラーを出力してもよいのですが、Stimulusでチェックしてみることにしました。
Stimulusのコントローラーを作成
rails generate stimulus password_validation
新しく設定するパスワードとパスワード(確認)が一致しないときの挙動はいくつか考えられるのですが、今回は一致しないときにはボタンをクリックできないようにしました。
app/javascript/controllers/password_validation_controller.rb
// password_validation_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["password", "confirmation", "submit"]
connect() {
this.validatePasswords()
}
validatePasswords() {
const password = this.passwordTarget.value
const confirmation = this.confirmationTarget.value
if (password !== confirmation) {
this.confirmationTarget.setCustomValidity("Passwords do not match")
} else {
this.confirmationTarget.setCustomValidity("")
}
this.submitTarget.disabled = !this.confirmationTarget.checkValidity()
}
}
パスワードの入力画面
app/views/devise/registrations/edit.html.erb
<div class="card mb-3">
<div class="card-header">
<%= t("views.change_password") %>
</div>
<div class="card-body" data-controller="password-validation">
<%= form_for(resource, as: resource_name, url: registration_path(resource_name),
html: { method: :put }) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
<div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
<% end %>
<div class="row g-3 mb-3">
<div class="col-4 text-end">
<%= f.label :current_password, class: "col-form-label" %>
</div>
<div class="col-8">
<%= f.password_field :current_password, class: "form-control",
placeholder: t("activerecord.attributes.user.current_password"),
autocomplete: "current-password", required: true %>
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-4 text-end">
<%= f.label :password, class: "col-form-label" %>
</div>
<div class="col-8">
<%= f.password_field :password, class: "form-control",
placeholder: t("activerecord.attributes.user.password"),
autocomplete: "new-password", minlength: 10, required: true,
"data-password-validation-target": "password",
"data-action": "input->password-validation#validatePasswords" %>
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-4 text-end">
<%= f.label :password_confirmation, class: "col-form-label" %>
</div>
<div class="col-8">
<%= f.password_field :password_confirmation, class: "form-control",
placeholder: t("activerecord.attributes.user.password_confirmation"),
autocomplete: "new-password", minlength: 10, required: true,
"data-password-validation-target": "confirmation",
"data-action": "input->password-validation#validatePasswords" %>
</div>
</div>
<%= f.submit class: "btn btn-primary col-4 offset-4 mt-1",
"data-password-validation-target": "submit" %>
<% end %>
</div>
</div>
動作確認
新しく設定するパスワードとパスワード(確認)が一致しないとボタンをクリックできない状態になっています。
まだまだ迷いながらですが、Hotwire時代に適応できるよう頑張っていこうと思います。