Rails Tutorialが一通り終わったので、返信機能を拡張するオートコンプリート機能を実装してみました。
返信機能に、@ + ユーザー名を入力する際、入力済みの文字列を元にユーザー名の候補のメニューを表示して入力の補完ができる機能を目指しました。
#前提
Rails Tutorial 第4版を一通り実装済みで、14章最後の拡張機能で挙げられている6つの機能を実装済みです。
返信機能は、ユーザーテーブルにnickname
を追加し、一意に特定できるようにしています。
Rails 5.1.1にアップデート済みです。
form_for
をform_with
に置き換えています。
ERBはHamlに置き換えています。
テストはminitestの代わりにRSpecを使っています。
E2EテストはTurnipを導入しています。
#準備
jQuery UIで実装することにしました。
まず、アセットパイプラインに簡単にjQuery UIを入れられるようになるjquery-ui-railsをGemfileへ。
gem 'jquery-ui-rails'
を追加し、
bundle install
を実行します。
//= require jquery-ui
*= require jquery-ui
を追加します。
これでjQuery UIの準備ができました。
#オートコンプリートを実装する
##ビューを変更する
マイクロポスト投稿のフォームに変更を加えます。
= form_with model: @micropost do |f|
= render 'shared/error_messages', object: f.object
.field
= f.text_area :content, placeholder: "Compose new micropost...", id: :micropost_content
= f.submit "Post", class: "btn btn-primary"
%span.picture
= f.file_field :picture, accept: 'image/jpeg,image/gif,image/png'
id: :micropost_content
をf.text_area
に追加しました。
##JavaScriptを追加する
ビューに画像の大きさを判定するJSのコードがあるので、その後に差し込みます。
:javascript
$(function() {
$('#micropost_content').autocomplete({
source: "/users/auto_complete.json",
delay: 500,
minLength: 2,
focus: function(event, ui) {
$("#micropost_content").val(ui.item.nickname);
return false;
},
select: function(event, ui) {
$('#micropost_content').val(ui.item.nickname);
return false;
}
}).data("ui-autocomplete")._renderItem = function(ul, item) {
return $("<li>").attr("data-value", item.nickname).data("ui-autocomplete-item", item).append("<a>" + item.nickname + "</a>").appendTo(ul);
};
});
source:
にリクエスト先を指定します。
autocompleteのオプションについて詳しくは以下を参照してください。
https://api.jqueryui.com/autocomplete/
オートコンプリートした際に何件検索で見つかったか、デフォルトで表示する設定のため、非表示にするCSSを追加します。
/* auto-complete */
.ui-helper-hidden-accessible { display:none; }
##コントローラーで受け取る
リクエストをコントローラーで受け取り、検索結果をJSONで出力します。
class UsersController < ApplicationController
#...
def auto_complete
@users = if params[:term] =~ /(@[^\s]+)\s.*/
elsif user_nickname = params[:term].match(/(@[^\s]+)/)
users = User.select('nickname').where('nickname LIKE ? AND activated = ?', "%#{user_nickname[1].to_s[1..-1]}%", true)
users.map {|user| {nickname: "#@{user.nickname}"} }
end
render json: @users.to_json
end
#...
end
データベースに@を保存しないようにしているので、検索ではそれを考慮したコードになっています。
params[:term]
でフォームに入力された値を取得することができます。
###routes.rbに追加する
resources :users do
member do
get :following, :followers
end
collection do
get :auto_complete
end
end
新しく追加したauto_complete
をroutes.rb
に追加します。
##テストについて
以下のリンクを参考にして、メソッドを追加しました。
http://ruby-journal.com/how-to-do-jqueryui-autocomplete-with-capybara-2/
def fill_autocomplete(field, options = {})
fill_in field, with:options[:with]
page.execute_script %{ $('##{field}').trigger('focus') }
page.execute_script %{ $('##{field}').trigger('keydown') }
selector = %{ul.ui-autocomplete li.ui-menu-item a:contains("#{options[:select]}")}
expect(page).to have_selector('ul.ui-autocomplete li.ui-menu-item a')
page.execute_script %{ $('#{selector}').trigger('mouseenter').click() }
end
Turnipでテストを書いているので、以下のようになりました。
step '検索フォームmicropost_contentに@arと入力するとオートコンプリートで表示され、選択する' do
fill_autocomplete 'micropost_content', with: '@ar'
end
step 'Postボタンをクリックする' do
click_button 'Post'
end
以上で、Rails Tutorialの返信機能を拡張し、オートコンプリート機能を実装できました。
こちらでソースコードを公開しています。
#参考リンク