Edited at

Rails 5 Auto completeを実装してみた

More than 1 year has passed since last update.

Rails Tutorialが一通り終わったので、返信機能を拡張するオートコンプリート機能を実装してみました。

返信機能に、@ + ユーザー名を入力する際、入力済みの文字列を元にユーザー名の候補のメニューを表示して入力の補完ができる機能を目指しました。


前提

Rails Tutorial 第4版を一通り実装済みで、14章最後の拡張機能で挙げられている6つの機能を実装済みです。

返信機能は、ユーザーテーブルにnicknameを追加し、一意に特定できるようにしています。

Rails 5.1.1にアップデート済みです。

form_forform_withに置き換えています。

ERBはHamlに置き換えています。

テストはminitestの代わりにRSpecを使っています。

E2EテストはTurnipを導入しています。


準備

jQuery UIで実装することにしました。

まず、アセットパイプラインに簡単にjQuery UIを入れられるようになるjquery-ui-railsをGemfileへ。


Gemfile.rb

gem 'jquery-ui-rails'


を追加し、

bundle install

を実行します。


app/assets/javascripts/application.js

//= require jquery-ui



app/assets/stylesheets/application.css

 *= require jquery-ui


を追加します。

これでjQuery UIの準備ができました。


オートコンプリートを実装する


ビューを変更する

マイクロポスト投稿のフォームに変更を加えます。


app/views/shared/_micropost_form.html.haml

= 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_contentf.text_areaに追加しました。


JavaScriptを追加する

ビューに画像の大きさを判定するJSのコードがあるので、その後に差し込みます。


app/views/shared/_micropost_form.html.haml

: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を追加します。


app/assets/stylesheets/custom.scss

/* auto-complete */

.ui-helper-hidden-accessible { display:none; }


コントローラーで受け取る

リクエストをコントローラーで受け取り、検索結果をJSONで出力します。


app/controllers/users_controller.rb

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に追加する


routes.rb

resources :users do

member do
get :following, :followers
end
collection do
get :auto_complete
end
end

新しく追加したauto_completeroutes.rbに追加します。


テストについて

以下のリンクを参考にして、メソッドを追加しました。

http://ruby-journal.com/how-to-do-jqueryui-autocomplete-with-capybara-2/


spec/support/utilities.rb


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でテストを書いているので、以下のようになりました。


app/spec/steps/home_steps.rb

step '検索フォームmicropost_contentに@arと入力するとオートコンプリートで表示され、選択する' do 

fill_autocomplete 'micropost_content', with: '@ar'
end

step 'Postボタンをクリックする' do
click_button 'Post'
end


以上で、Rails Tutorialの返信機能を拡張し、オートコンプリート機能を実装できました。


結果

スクリーンショット 2017-06-23 16.17.54.png

こちらでソースコードを公開しています。


参考リンク