railsとjqueryと使った複数登録画面の実装
こんにちわ!この記事ではrailsとjqueryを使った複数画面の登録機能の実装について書いて行きたいと思います。
プレビュー
こんな感じの登録画面ですね!皆さんも一度は目にしたことがあるのではないでしょうか?
画面遷移自体はjqueryを使って隠れている分を出していってるだけです!
今回はここにそれぞれの画面でバリデーションが行えるようにしたいと思います!
具体的にはこんな感じですね!
###ステップ
ユーザが入力する
↓
次へボタンを押すとajaxでコントローラーにリクエストを送ってでvalidationを行う
↓
問題がなければ次の画面に遷移する、validationで引っかかれば画面遷移はせず、alertを出してあげる
流れになります!
###具体的な実装
各ページの実装のvalidationのための実装はほぼ同じなので今回は最初のページだけのコードを見せつつ説明をして行きたいと思います。
まずはview
.registration-container
.registration-header
%h3
registration
.registration-view-part
= form_for @user, html: {class: 'registration-form clearfix'} do |f|
.first-step-registration
.field
.field-label
= f.label '名前(名)'
.field-input#first_name_input
= f.text_field :first_name, id: 'first_name'
.field
.field-label
= f.label '名前(性)'
.field-input#family_name_input
= f.text_field :family_name, id: 'family_name'
.field
.field-label
= f.label 'ニックネーム'
.field-input#nickname_input
= f.text_field :nickname, id: 'nickname'
.field
.field-label
= f.label '電話番号'
.field-input#phone_number_input
= f.text_field :phone_number, id: 'phone_number'
.btn#first-step-btn
next
.second-step-registration
.field
.field-label
= f.label '性別'
.field-input
= f.select :sex, { '男の子': :male, '女の子': :female }
.field
.field-label
= f.label 'パスワード'
.field-input#password_input
= f.password_field :password, id: 'password'
.field
.field-label
= f.label 'パスワード(確認)'
.field-input#password_confirmation_input
= f.password_field :password_confirmation, id: 'password_confirmation'
.btn#second-step-btn
next
.third-step-registration
.field
.field-label
= f.label '学年'
.field-input#grade
= f.select :grade, {'大学1年': :grade_1, '大学2年': :grade_2,'大学3年': :grade_3,'大学4年': :grade_4 }
.field
.field-label
= f.label '大学'
.field-input#university_input
= f.text_field :university, id: 'university'
.field
.field-label
= f.label 'Eメールアドレス'
.field-input#email_input
= f.email_field :email, id: 'email'
= f.submit 'create account', class: 'registration-submit-btn'
これが登録画面のview部分の実装です。この全体のviewをcssで3つに分割しているように見せています。そして画面遷移はjqueryを使って隠れている部分を出して行きます。
.registration-container {
width: 800px;
height: 650px;
padding: 30px 0;
margin: 30px auto;
background: #fff;
.registration-header {
width: 200px;
margin: 0 auto;
height: 30px;
line-height: 30px;
text-align: center;
h3 {
font-size: 30px;
}
}
.registration-view-part {
width: 480px;
height: 550px;
margin: 30px auto;
overflow: hidden;
border: 1px solid black;
.registration-form {
width: 2000px;
height: 440px;
.clearfix::after {
content: "";
display: block;
clear: both;
}
.first-step-registration {
border-box: box-sizing;
width: 400px;
height: 500px;
padding: 40px;
float: left;
.field {
width: 400px;
height: 70px;
margin: 0 30px 30px 30px;
.field-label {
margin-bottom: 10px;
}
.field-input {
width: 80%;
input {
width: 90%;
height: 30px;
padding: 5px 10px;
border-radius: 5px;
border: 1px solid black;
}
select {
width: 100px;
height: 40px;
}
}
p {
color: red;
}
}
.btn {
width: 100px;
height: 40px;
margin: 50px auto 30px auto;
display: block;
border-radius: 3px;
font-size: 20px;
border: 1px solid black;
text-align: center;
line-height: 40px;
cursor: pointer;
}
}
.second-step-registration,
.third-step-registration {
border-box: box-sizing;
width: 400px;
height: 400px;
padding: 40px;
float: left;
.field {
width: 400px;
height: 100px;
margin: 0 30px 30px 30px;
.field-label {
margin-bottom: 10px;
}
.field-input {
width: 80%;
input {
width: 90%;
height: 30px;
padding: 5px 10px;
border-radius: 5px;
border: 1px solid black;
}
select {
width: 100px;
height: 40px;
}
}
p {
color: red;
}
}
.btn {
width: 100px;
height: 40px;
margin: 0 auto;
display: block;
border-radius: 3px;
font-size: 20px;
border: 1px solid black;
text-align: center;
line-height: 40px;
cursor: pointer;
}
.registration-submit-btn {
margin: 0 auto;
display: block;
border-radius: 3px;
font-size: 20px;
border: 1px solid black;
text-align: center;
line-height: 40px;
padding: 5px;
}
}
}
}
}
参考までにcssのファイルも載せておきます。
そして次のページに遷移するタイミングでvalidationを行いたいのですがここで重要になってくるのがreformというgemです。このgemはバリデーションを行うためのgemでファイルに属性を記述することでバリデーションしてくれます。
まずは次にボタンを押した段階で非同期通信でコントローラーにリクエストを送ります。
$('#first-step-btn').on('click', function() {
var first_name = $('#first_name').val();
var family_name = $('#family_name').val();
var nickname = $('#nickname').val();
var phone_number = $('#phone_number').val();
var btn = $(this)
$.ajax({
type: 'POST',
url: '/first_step',
data: { registrations: { first_name: first_name, family_name: family_name, nickname: nickname, phone_number: phone_number} },
dataType: 'json'
})
.done(function(message) {
if(message.length != 0) {
$('#nickname_input > p').remove();
$('#phone_number_input > p').remove();
alert_first_step_error(message)
console.log('bad')
} else {
$('.registration-form').css({
'transform': 'translate(-480px)',
'transition-duration': '1s'
})
}
})
}
})
ここで最初のページで入力された名前等の情報をコントローラーの送ります。
class RegistrationsController < ApplicationController
def first_step
@form = Registrations::FirstStepUsecase.new(first_step_params).execute
if @form.errors.empty?
render json: []
else
render 'first_step_errors', formats: 'json', handlders: 'jbuilder'
end
end
private
def first_step_params
params.require(:registrations).permit(:first_name, :family_name, :nickname, :phone_number)
end
end
受け取ったパラメーターをRegistrations::FirstStepUsecaseクラスに渡しています。
module Registrations
class FirstStepUsecase
attr_reader :param, :form
def initialize(param)
@param = param
@form = Registrations::FirstStepForm.new(User.new)
end
def execute
return form unless form.validate(param)
form
end
end
end
そしてここでreformの出番です。
require "reform/form/validation/unique_validator"
module Registrations
class FirstStepForm < Reform::Form
property :first_name
property :family_name
property :nickname
property :phone_number
validates :first_name, presence: true
validates :family_name, presence: true
validates :nickname, presence: true, unique: true
validates :phone_number, presence: true, unique: true
end
end
コードはこんな感じです。使い方によって書き方は違ってくるのですが、propertyのところはモデルの属性ですね。
validatesのところでどんなバリデーションをしたいのかを記述します。ここはモデルのファイルに記述するバリデーションと似ていますね。
詳しい使い方に関しては以下の記事やドキュメントを参考にしてみてください。
http://trailblazer.to/gems/reform/
https://qiita.com/subaru-shoji/items/e548f10a871938bedd06
英語のドキュメントは読むのが大変だと思うのですが、一番詳しく書いてあるので頑張って読んでみてください笑
module Registrations
class FirstStepUsecase
attr_reader :param, :form
def initialize(param)
@param = param
@form = Registrations::FirstStepForm.new(User.new)
end
def execute
return form unless form.validate(param)
form
end
end
end
executeの部分でバリデーションを行なっています。エラーがあればエラー付きのformを返します。無ければ普通にformを返します。
そして再びコントローラに戻ります。
def first_step
@form = Registrations::FirstStepUsecase.new(first_step_params).execute
if @form.errors.empty?
render json: []
else
render 'first_step_errors', formats: 'json', handlders: 'jbuilder'
end
end
ここではエラーが無ければ何も返さずにエラーがあればエラーの内容をjson形式で返すためにjbuilderに渡して
クライアントサイドに返します。
if @form.errors.messages[:nickname].present?
json.nickname "このニックネームは使用されています。"
end
if @form.errors.messages[:phone_number].present?
json.phone_number "この電話番号は使用されてます"
end
クライアント側に戻ってし処理を続けます。
.done(function(message) {
if(message.length != 0) {
$('#nickname_input > p').remove();
$('#phone_number_input > p').remove();
alert_first_step_error(message)
console.log('bad')
} else {
$('.registration-form').css({
'transform': 'translate(-480px)',
'transition-duration': '1s'
})
}
})
先ほどのjsファイルの部分でエラーがあればエラー文を組み立てる、無ければ次のページに遷移するという実装の部分です。 alert_first_step_error(message)の部分がエラー文を組み立てる関数になっているのですがここでは省きます。
次のステップもやることは変わりません。
だいぶ大雑把に書きましたが、参考になれば嬉しいです。
参考
https://qiita.com/subaru-shoji/items/e548f10a871938bedd06
http://trailblazer.to/gems/reform/
https://qiita.com/akichim21/items/afe961ea752f894b389b