Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
8
Help us understand the problem. What is going on with this article?
@i__kobe

Railsで動的なセレクトボックスを作る最も簡単な方法。

More than 1 year has passed since last update.

主に使用する言語はRuby(on Rails), JSです。

動的なセレクトボックスとは?

伊藤さんの書いているこちらの記事を見てみてください。

ここで記述されているセレクトボックスのように親要素が選択された時に子要素のセレクトボックスの中身が選択された親要素に紐づいている子要素になるというものをここでは動的なセレクトボックスと呼んでいます。

上記の記事ではルーティングを設定したり、ajaxの記述をしたりと色々していますが、ある要素を使えばこのセレクトボックスは簡単に実装できます。

結論:template要素を使う

ここでもCategory, SubCategoryのモデルでアソシエーションを組んで実装していきましょう。

class Category < ApplicationRecord
  has_many :sub_categories
end
class SubCategory < ApplicationRecord
  belongs_to :category
end

このアソシエーションによってCategoryモデルとSubCategoryモデルで1対多の関係性を築けました。

次はビューファイルの編集です。

= f.collection_select :category, Category.all, :id, :name, include_blank: "カテゴリーを選択してください"

- Category.all.each do |category|
 %template{id: "sub-category-of-category#{category.id}"}
  = f.collection_select :sub_category, categroy.sub_categories, :id, :name, include_blank: "サブカテゴリーを選択してください"

今の状況はこんな感じです。

<select name="category" id="category">
 <option value="1">カテゴリー1</option>
 .
 .
 .
</select>
<template id="sub-category-of-category1">#document-fragment</template>
<template id="sub-category-of-category2">#document-fragment</template>
<template id="sub-category-of-category3">#document-fragment</template>
<template id="sub-category-of-category4">#document-fragment</template>
<template id="sub-category-of-category5">#document-fragment</template>
.
.
.

template要素とは何か??

template要素は、ページの読み込み時に描画されず、後で JavaScript を使用してインスタンスを生成できるクライアント側のコンテンツを保持するメカニズムです。
(引用元:https://developer.mozilla.org/ja/docs/Web/HTML/Element/template

簡単にいうと、JavaScriptで操作して本文へと挿入しない限りはデベロッパーツールのHTML上には表示されるがビューの見た目には何も影響しないということです。

なのでjsファイル内での記述も書いていきましょう。
今回はjQueryで書いていきます。

jsファイル内の記述

$(document).on('turbolinks:load', function() {
 $(document).on('change', '#category', function() {
  let categoryVal = $('#category').val();
  if (categoryVal !== "") {
   let selectedTemplate = $(`#sub-category-of-category${categoryVal}`);
   $('#category').after(selectedTemplate.html());
  };
 });
});

ただ、このままだと初めは親要素のセレクトボックスしかなく、親要素を選択した際に子要素のセレクトボックスが現れるといった記述になることに加えて、一度選択して二度目の選択をする際に新しいセレクトボックスが生成されるという状況になってしまう。

少しビューファイルとjsファイルをいじってそれを直していきます。

= f.collection_select :category, Category.all, :id, :name, include_blank: "カテゴリーを選択してください"

= f.select :sub_category, [], include_blank: "サブカテゴリーを選択してください", class: "default-sub-category-select"

- Category.all.each do |category|
 %template{id: "sub-category-of-category#{category.id}"}
  = f.collection_select :sub_category, categroy.sub_categories, :id, :name, include_blank: "サブカテゴリーを選択してください"

これでページの読み込み時に親要素と子要素(option要素は何もない)が出来上がっている状況である。

ちなみにこの時のデフォルトで表示されている子要素のセレクトボックスのidが'sub_category'になっているということだけ覚えておいていただきたい。(jsファイルでの記述に使うから。)

そこからjsファイルの編集にうつる。

$(document).on('turbolinks:load', function() {
 //HTMLが読み込まれた時の処理
 let categoryVal = $('#category').val();
 //一度目に検索した内容がセレクトボックスに残っている時用のif文
 if (categoryVal !== "") {
  let selectedTemplate = $(`#sub-category-of-category${categoryVal}`);
  $('#sub_category').remove();
  $('#category').after(selectedTemplate.html());
 };

 //先ほどビューファイルに追加したもともとある子要素用のセレクトボックスのHTML
 let defaultSubCategorySelect = `<select name="sub_category" id="sub_category">
<option value>サブカテゴリーを選択してください</option>
</select>`;

 $(document).on('change', '#category', function() {
  let categoryVal = $('#category').val();
  //親要素のセレクトボックスが変更されてvalueに値が入った場合の処理
  if (categoryVal !== "") {
   let selectedTemplate = $(`#sub-category-of-category${categoryVal}`);
   //デフォルトで入っていた子要素のセレクトボックスを削除
   $('#sub_category').remove();
   $('#category').after(selectedTemplate.html());
  }else {
   //親要素のセレクトボックスが変更されてvalueに値が入っていない場合(include_blankの部分を選択している場合)
   $('#sub_category').remove();
   $('#category').after(defaultSubCategorySelect);
  };
 });
});

これで最初のビューに親要素・子要素のセレクトボックスが存在し、親要素が選択されていない場合はoption要素が何もないデフォルトのセレクトボックスが表示され、親要素が選択された際にその要素と紐づいた子要素が入ったセレクトボックスが現れるようになったと思う。ただIEがtemplate要素に対応してないみたいなの見たきがしないでもないからその辺りは注意してください。

リファクタリングはまた今度します。

8
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
i__kobe
HTML5 / CSS3 / JavaScript / Ruby

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
8
Help us understand the problem. What is going on with this article?