問題
CSV出力機能を実装した際に、そのページに検索機能(ransack gem)があることに気づいた。
検索結果をCSVファイルに反映する方法をいろいろ調べたのですが、実装するのにけっこう紆余曲折してしまったので記録として残しておきます。
前提
バージョン
Ruby 2.5
Rails 5.1
Ransack 2.1.1
実装前のソースコード
class ItemsController < ApplicationController
def index
@q = Item.ransack(params[:q])
@items = @q.result
respond_to do |format|
format.html
format.csv do
send_data @items.export, filename: "items_export.csv"
end
end
end
end
コントローラーにメソッドを書くとごちゃごちゃしてしまうので、実装はモデルに書きます。
def self.export
CSV.generate(encoding: Encoding::SJIS, row_sep: "\r\n", force_quotes: true) do |csv|
csv << %w(
ID 名前 所有者名
)
all.each do |item|
id = item.id
name = item.name
user = user.name
csv << [
id, name, user
]
end
end
end
CSV出力の設定に関してはこの記事では触れないので、他の方の記事なども参考にしてください。
<div>
<h4>アイテム一覧</h4>
<%= search_form_for(@q, url: items_path) do |f| %>
<div>
<%= link_to 'データ出力', purchase_orders_path(format: :csv),
class: 'uk-button uk-button-secondary uk-button-small create-button' %>
</div>
<div>
<div class="uk-grid-small uk-grid">
<%= f.label :id_cont, 'ID', class: 'uk-form-label search-label' %>
<div class="uk-form-controls">
<%= f.search_field :id_cont, class: 'uk-input uk-form-small' %>
</div>
<%= f.label :name_cont, '名前', class: 'uk-form-label search-label' %>
<div class="uk-form-controls">
<%= f.search_field :name_cont, class: 'uk-input uk-form-small uk-form-width-small' %>
</div>
<%= f.label :user_cont, '所有者名', class: 'uk-form-label search-label' %>
<div class="uk-form-controls">
<%= f.search_field :usere_cont, class: 'uk-input uk-form-small uk-form-width-medium' %>
</div>
<%= f.submit '検索', class: 'uk-button uk-button-secondary uk-button-small mr15' %>
<%= link_to 'クリア', url_for, class: 'uk-button uk-button-default uk-button-small' %>
</div>
</div>
<% end %>
<div>
<%= will_paginate(@items, previous_label: ' < 前へ', next_label: '次へ >') %>
</div>
<table class="uk-table uk-table-striped table-th uk-table-small">
<thead>
<tr>
<th><%= sort_link(@q, :id, 'ID') %></th>
<th><%= sort_link(@q, :name, '名前') %></th>
<th><%= sort_link(@q, :user, '所有者名') %></th>
</tr>
</thead>
<tbody>
<% @items.each do |item| %>
<tr>
<td><%= item.id %></td>
<td><%= item.name %></td>
<td><%= item.user%></td>
</tr>
<% end %>
</tbody>
</table>
</div>
上記のviewで書いたような出力ボタンの場合、検索前①も検索後②も下記のようにcontrollerのindexで取得していた値が全てCSVに出力されます。つまり、検索結果は反映されません。
出力結果
ID | 名前 | 所有者名 |
---|---|---|
1 | item1 | user1 |
2 | item1 | user2 |
3 | item1 | user2 |
4 | item1 | user2 |
5 | item1 | user2 |
解決策
出力ボタンの書き方を変えます。
<div>
<h4>アイテム一覧</h4>
<%= search_form_for(@q, url: items_path) do |f| %>
<div>
<button type='submit' name='format' value='csv' class='uk-button uk-button-secondary uk-button-small create-button'>
データ出力
</button>
</div>
~~~~~~~~
</div>
これで検索後②の結果を反映したCSVが出力できるようになります。
本当はRailsっぽく f.submit
を使って実装したかったのですが、これを使う場合format
属性はsearch_for_form
に入れる必要があるんですよね。そうすると、検索機能自体が上手くいかなくなってしまうので、止む無くこの書き方にしました。
もっときれいな書き方を知ってる方がいたらぜひ教えてください。
出力結果
ID | 名前 | 所有者名 |
---|---|---|
1 | item1 | user1 |
ちなみに、実は検索後のページに遷移してなくても、検索項目に値が入っているだけでそちらを反映したデータが出力されるので注意してください。
出力結果
ID | 名前 | 所有者名 |
---|---|---|
2 | item1 | user2 |
検索ボタンをdisableにしない方法
出力ボタンを押した後、検索ボタンが押せないようになっています。
これはRailsの機能で、submit
ボタンが2回クリックされてしまうとHTTPリクエストが重複してしまい、バックエンド側で検出できなくなる可能性があるため、それを防止するためにある機能です。
参考:railsガイドの「3.4 入力を自動で無効にする」
https://railsguides.jp/working_with_javascript_in_rails.html#入力を自動で無効にする
データ出力した後に検索機能を使う可能性は低いと思いますが、念のため直しておきましょう。
検索ボタンにdata-disable-with
を追記します。
<div>
~~~~~~~~
<%= f.submit '検索', class: 'uk-button uk-button-secondary uk-button-small mr15', 'data-disable-with' => false %>
<%= link_to 'クリア', url_for, class: 'uk-button uk-button-default uk-button-small' %>
</div>
</div>
<% end %>
~~~~~~~~
</div>
これでCSV出力後も検索ボタンが非活性にならないようになりました。