タグ検索の実装
昨日の続きとなります。
タグの初期データを用意したり、個別の掲示板にタグを設定して保存したり、掲示板詳細画面に関連づけられたタグを表示する機能については【52日目】を参照してください。
今日の内容は下記のとおりです。
○ 掲示板一覧でタグによる絞り込み機能の実装
・ セレクトボックスの作成方法
○ ヘッダーメニュー(グローバルメニューの作成)
・ ヘルパーメソッドの自作方法
・ HTMLタグの自作方法
・ ルーティングの自作方法
掲示板一覧画面にタグのセレクトボックスを用意し、タグを入力して、該当のタグのある掲示板だけ残すようにしていきます。
##掲示板一覧にタグのセレクトボックスを追加する(view)
indexのviewを編集して、掲示板一覧からタグを選び、選んだタグに関連づけられた掲示板のみを表示するようにします。
タグの選択は、プルダウン式のセレクトボックス(select_tagヘルパー使用)によって実装します。
セレクトボックスを選んだ瞬間に掲示板が絞り込まれるのは、「onchange」という属性によってJSを実行します。
<%= form_tag boards_path, method: :get, class: 'boards__searchForm' do %>
# form_tag, アクションへのパス, HTTPメソッド, HTMLオプション
# classはセレクトボックスの位置を調整するためのHTMLオプションで、あとでCSSを編集する。
<%= select_tag :tag_id, # セレクトボックスのname属性がtag_idになる。
options_from_collection_for_select(Tag.all, :id, :name, params[:tag_id]),
#ヘルパーによってセレクトボックスの選択肢となるオプション要素を複数作成している
#選択肢としたいオブジェクトのリスト, オプションのvalue, オプションの表示名, 何を選択状態とするの指定
# 第四引数の指定によってGET通信でURLに含まれるクエリパラメータのtag_idを選択状態とすることができ、検索後も選択した状態に出来る。
{ #ハッシュの中身全部が第三引数
prompt: 'タグで絞り込み', #空白の時に表示する文字
class: 'form-control boards__select', # クラス属性に設定する値
onchange: 'submit(this.form);'
#選択されるたびに実行するJSのコードを記載している
#onchange属性はセレクトボックスに限らず、値が変わった際に実行されるJSのコードを設定できるイベント属性。
#ここでは、何か選択肢から選んだ時点でフォーム(this.form)を作成するようになっている
}
%>
<% end %>
CSSの編集
.boards__searchForm {
display: inline-block;
}
.boards__select {
display: inline-block;
width: auto;
}
これによって、掲示板一覧画面の上部にタグのセレクトボックスが表示されます。
ここでタグを選ぶことによって、選んだタグのtag_idをindexアクションに送ることができる。
ただし、現状のコントローラーにはviewから渡ってきたtag_idを使って検索する機能がないので、コントローラーを編集します。
tag_idを用いた検索機能の実装(controller)
tag_idに該当するタグの取得 => タグオブジェクトから関連する掲示板を取得する。
def index
@boards = params[:tag_id].present? ? Tag.find(params[:tag_id]).boards : Board.all
# tag_idがあるかを確認(present? ?)
# tag_idがある場合には、tag_idのパラメータを持つタグを検索(.find)し、
# 該当するタグにhas_manyでアソシエーションされている掲示板のリストを取得している(親モデル.<has_manyアソシエーション名>)
# tag_idがない場合は(present? ? 存在する場合の処理 : 存在しない場合の処理)全ての掲示板(Board.all)を表示する
# @boardsを参照するまではBoard.allを取得することはない。
@boards = @boards.page(params[:page])
# 上記で定義された@boardsに対して、page内に表示する件数の制限をかけてから、取得する。
# 例えば10件とした場合、10件分だけのSQLが発行される。ためBoard.allとしてもパフォーマンスに悪影響しない
end
ヘッダーメニューの追加
ブートストラップのデザインを使用し(もちろんCSSでもいい)viewを作成し、ページを遷移しても常に表示されるメニューを作成する。
touch app/views/application/_header.html.erb
を編集していく。
ここではブートストラップのナビゲーションバーのデザインを使用し、部分的に修正している。
修正箇所にコメントを付記する。
独自のヘルパーを用いる部分があるので、この後ヘルパーを定義する。
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<a class="navbar-brand" href="#">BoardApp</a> #タイトル名を変更
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria- controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
# リンク部分の修正。header_link_iteというへルパーを作って使用する。
# link_toとしないのは、現在表示中のページのみをアクティブ(リンクが白くなる)にするために設定を加えたいから。
# header_link_item('表示名', リンク先のパス) とすることでリンクを張れる。
<%= header_link_item('Home', root_path) %> #root_pathはrootingを追加しないと存在しないので後で追加します。ホーム画面です。
<%= header_link_item('Boards', boards_path) %>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Dropdown
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">Something else here</a>
</div>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#">Disabled</a>
</li>
</ul>
<form class="form-inline my-2 my-lg-0">
<input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
</form>
</div>
</nav>
##ヘルパーメソッドの作成
今回はグローバルメニューに使う=全ページで使うヘルパーになるので
app/helpers/application_helper.rb
の中に設定する。
特定のコントローラーでしか使用しない場合は、
boards_helper.rb
のように区別した方が用途がわかりやすくて間違いにくい。
module ApplicationHelper
def header_link_item(name, path)
class_name = 'nav-item' # ブートストラップのデザインに合わせるためのクラス
class_name << ' active' if current_page?(path) # 表示中のパスと引数のパスが同一であるかを判定する
# 表示するパスと引数のパスが同じであれば、クラスの属性に「active」を追加する、というもの。
content_tag :li, class: class_name do
# content_tag(HTMLタグ名, HTMLクラス)で任意のHTMLタグを作成できる。
# ここでは上で作成した「class_name」を指定している。
link_to name, path, class: 'nav-link'
# content_tagのブロック内に書いたHTMLが作成したタグの中で展開される。
# ここでは仮引数で受け取ったとおりにリンクを貼るアンカータグを埋め込むことになる。
end
end
end
root_pathの作成
次に、ルーティングを追加する。
ここではまだページは作成していない。
root 'home#index'
# パス名 'コントローラー名#アクション名'
ヘッダーのview(パーシャル)の作成
ヘッダーのパーシャルをrenderで呼び出すだけ。
<%= render 'header' %>
この後認証機能の学習に進みますが、かなりジャンルが変わるのでここで一旦投稿します。