5
2

More than 3 years have passed since last update.

Choices.jsでリッチな複数選択リストを実装する

Last updated at Posted at 2020-09-04

スマートな操作性と、モダンなUIを提供してくれる「Choices」をRailsアプリに組み込む方法です。

image.png

今回のサンプルでは「タスクに複数のメンバーをアサインする」部分を例に解説してみたいと思います。

動作イメージ

choices.gif

環境とgem

  • ruby 2.7.1
  • rails 6.0.3
  • webpackerは有効
  • turbolinksは無効
  • hamlit-rails
  • simple_form

手順

  1. モデルの作成
  2. Choices.jsのセットアップ
  3. select要素とChoicesの紐付け
  4. おまけ:RSpecでシステムテスト
  5. サンプルレポジトリ
  6. 追記:2020/09時点のメンテナンス状況

1. モデルの作成

> rails g scaffold task title content
> rails g scaffold member name
> rails g model assignment task:references member:references
> rails db:migrate
task.rb
class Task < ApplicationRecord
  has_many :assignments, dependent: :destroy
  has_many :members, through: :assignments
end
member.rb
class Member < ApplicationRecord
  has_many :assignments, dependent: :destroy
  has_many :tasks, through: :assignments
end
assignment.rb
class Assignment < ApplicationRecord
  belongs_to :member
  belongs_to :task
end

2. Choices.jsのセットアップ

> yarn add choices.js

3. select要素とChoicesの紐付け

js側の設定

app/javascript/packs/choices/init.js
import * as Choices from 'choices.js'

document.addEventListener('DOMContentLoaded', function () {

  new Choices('#choices-multiple-remove-button', {
    delimiter: ',',
    maxItemCount: 5,
    removeItemButton: true,
    noChoicesText: '選択されていません',
    itemSelectText: '選択してください',
    maxItemText: (maxItemCount) => {
      return `タスクにアサインできるのは ${maxItemCount} 人までです`;
    },
  });
});

js側の設定(スタイルシート)

app/javascript/src/choices.scss
@import '~choices.js/src/styles/choices'

ファイルのimport

ふたつのファイルをimportしておきます。

app/javascript/packs/application.js
import './choices/init.js';
import '../src/choices.scss'

view側の設定

tasks/_form.html.haml
= simple_form_for @task do |f|
  = f.error_notification
  = f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present?

  .field
    = f.input :title, id: 'task_title'
  .field
    = f.input :content, as: :text

  .field
    = f.association :members,
      required: true,
      as: :select,
      collection: Member.pluck(:name, :id),
      input_html: { id:'choices-multiple-remove-button', multiple: true }

  .actions
    = f.button :submit

Choices.jsがマッピングするために、idをあわせておきます。
init.js内のnew Choices('#choices-multiple-remove-buttonで指定したidを設定します。複数選択なのでmultiple: trueもわすれずに。

抜粋
= f.association :members,
  required: true,
  as: :select,
  collection: Member.pluck(:name, :id),
  input_html: { id:'choices-multiple-remove-button', multiple: true }

選択肢の部分は、collection: Member.pluck(:name, :id)とすれば、あとはChoicesが面倒みてくれます。「編集時はchoices.setValueして初期値を設定」なんてこともしなくてよいです。
selectedな項目が選ばれた状態になってくれます。

image.png

レンダリングされたHTML
<select multiple="multiple" class="select required" required="required" aria-required="true" name="task[member_ids][]"
  id="task_member_ids">
  <option value="1">Alice</option>
  <option selected="selected" value="2">Bob</option>
  <option selected="selected" value="3">Charlie</option>
</select>

TasksControllerのStrongParameterを修正

選択した項目の値を配列で受け取れるように、以下のように修正します。

tasks_controller.rb
def task_params
  params.require(:task).permit(:title, :content, member_ids: [])
end

変更はこれだけで完了です👍

メッセージの日本語化や、表示をカスタマイズできるオプションもありますので、色々できそうですね。
jshjohnson/Choices: A vanilla JS customisable select box/text input plugin ⚡️

4. おまけ:RSpecでシステムテスト

tasks_spec.rb
require 'rails_helper'

RSpec.describe 'Tasks', type: :system do
  let!(:board) { create(:board) }
  let!(:member) { create(:member, name: 'チョイ・スー') }

  it 'タスクを追加できる', js: true do
    visit new_board_task_path(board)

    fill_in 'Title', with: 'Qiita記事を書く'
    fill_in 'Content', with: 'Choices.jsの使い方を紹介します'

    # リストを展開
    first('.choices').click
    # リストアイテムを選択
    first('.choices__item').click
    # リストを閉じるためEsc送信
    find('#task_title').send_keys :escape

    expect do
      click_on 'Create Task'
    end.to change { Task.count }.by(1)
  end
end

※Rspecや、FactoryBotをつかっていますが詳細は割愛します。動かなかった場合はコメントいただければお答えできるかも。

5. サンプルレポジトリ

:octocat: hirocueki/kanban-for-choices-js

6. 追記:2020/09時点のメンテナンス状況

公式レポジトリに、以下の文面がありました。
要約すると、「このライブラリをサポートする時間がなくてメンテナーを探しているので連絡ください」ということです。利用の際には、この点も含めて考慮してください :pray:

image.png

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2