1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Rails】ancestryを用いた多階層カテゴリー機能の実装『Bootstrap3でウィンドウ作ってみた編』

Last updated at Posted at 2020-06-21

目標

ezgif.com-video-to-gif.gif

開発環境

・Ruby: 2.5.7
・Rails: 5.2.4
・Vagrant: 2.2.7
・VirtualBox: 6.1
・OS: macOS Catalina

前提

下記実装済み。

Slim導入
Bootstrap3導入
Font Awesome導入
ログイン機能実装
投稿機能実装
多対多のカテゴリー機能実装
多階層カテゴリー機能実装(準備編)
多階層カテゴリー機能実装(seed編)
・[多階層カテゴリー機能実装(作成フォーム編)]
(https://qiita.com/matsubishi5/items/4afb4a4f307023126c66)
・[多階層カテゴリー機能実装(編集フォーム編)]
(https://qiita.com/matsubishi5/items/10e61f314f6c56b8690d)

1.コントローラーを編集

homes_controller.rb
# 追記
def category_window
  @children = Category.find(params[:parent_id]).children
end

【解説】

① Ajax通信で送られてきたパラメーターに対応するカテゴリーの、子カテゴリーを抽出し、インスタンス変数に代入する。

@children = Category.find(params[:parent_id]).children

2.json.jbuilderファイルを作成・編集

ターミナル
$ touch app/views/homes/category_window.json.jbuilder
category_window.json.jbuilder
json.array! @children do |children|
  json.id children.id
  json.name children.name
end

【解説】

get_category_childrenアクションで抽出したレコードを繰り返し処理し、配列を作成する。

json.array! @children do |children|

② 各IDと名前を で作成した配列に格納する。

json.id children.id
json.name children.name

◎ 親カテゴリー(ビジネス)にマウスが乗っている場合の返り値

[
  {
    "id": 2, 
    "name": "金融"
  },
  {
    "id": 6, 
    "name": "経済"
  },
  {
    "id": 9, 
    "name": "経営"
  },
  {
    "id": 13, 
    "name": "マーケティング"
  },
]

◎ 子カテゴリー(金融)にマウスが乗っている場合の返り値

[
  {
    "id": 3, 
    "name": "株"
  },
  {
    "id": 4, 
    "name": "為替"
  },
  {
    "id": 5, 
    "name": "税金"
  },
]

3.ルーティングを追加

routes.rb
# 追記
get 'get_category/new', to: 'homes#category_window', defaults: { format: 'json' }

4.ビューを編集

application.html.slim
body
  header
    nav.navbar.navbar-default.navbar-fixed-top
      .container-fluid
        ul.nav.navbar-nav.navbar-right
          li.dropdown role='presentation'
            a.dropdown-toggle data-toggle='dropdown' href='#' role='button' aria-expanded='false'
              i.fas.fa-list-ul
              span
                |  カテゴリーから探す
              span.caret
            ul.dropdown-menu role='menu'
              li role='presentation'
                - Category.where(ancestry: nil).each do |parent|
                  = link_to parent.name, root_path, id: "#{parent.id}", class: 'parent-category'
              br
              li role='presentation' class='children-list'
              br
              li role='presentation' class='grandchildren-list'

【解説】

※Bootstrapの書き方については省略します。

① ancestryの値がnil、つまり親カテゴリーを全て抽出し、プルダウンメニューに表示する。

- Category.where(ancestry: nil).each do |parent|
  = link_to parent.name, root_path, id: "#{parent.id}", class: 'parent-category'

② 子カテゴリーを表示する場所を用意する。

li role='presentation' class='children-list'

③ 孫カテゴリーを表示する場所を用意する。

li role='presentation' class='grandchildren-list'

5.JavaScriptファイルを作成・編集

ターミナル
$ touch app/assets/javascripts/category_window.js
category_window.js
$(function() {
  function buildChildHTML(children) {
    let html = `
      <a class="children-category" id="${children.id}" href="/">
        ${children.name}
      </a>
    `;
    return html;
  }

  $('.parent-category').on('mouseover', function() {
    let id = this.id;
    $('.children-category').remove();
    $('.grandchildren-category').remove();
    $.ajax({
      type: 'GET',
      url: '/get_category/new',
      data: {
        parent_id: id,
      },
      dataType: 'json',
    }).done(function(children) {
      children.forEach(function(child) {
        let html = buildChildHTML(child);
        $('.children-list').append(html);
      });
    });
  });

  function buildGrandChildHTML(children) {
    let html = `
      <a class="grandchildren-category" id="${children.id}" href="/">
        ${children.name}
      </a>
    `;
    return html;
  }

  $(document).on('mouseover', '.children-category', function() {
    let id = this.id;
    $.ajax({
      type: 'GET',
      url: '/get_category/new',
      data: {
        parent_id: id,
      },
      dataType: 'json',
    }).done(function(children) {
      children.forEach(function(child) {
        let html = buildGrandChildHTML(child);
        $('.grandchildren-list').append(html);
      });
      $(document).on('mouseover', '.children-category', function() {
        $('.grandchildren-category').remove();
      });
    });
  });
});

【解説】

① 子カテゴリーのHTMLを作成する。

function buildChildHTML(children) {
  let html = `
    <a class="children-category" id="${children.id}" href="/">
      ${children.name}
    </a>
  `;
  return html;
}

② どの親カテゴリーにマウスが乗っているかによって、子カテゴリーの表示内容を変更する。

  $('.parent-category').on('mouseover', function() {
    let id = this.id;
    $('.children-category').remove();
    $('.grandchildren-category').remove();
    $.ajax({
      type: 'GET',
      url: '/get_category/new',
      data: {
        parent_id: id,
      },
      dataType: 'json',
    }).done(function(children) {
      children.forEach(function(child) {
        let html = buildChildHTML(child);
        $('.children-list').append(html);
      });
    });
  });

◎ 親カテゴリーにマウスが乗った時に発火するイベントを作成する。

$('.parent-category').on('mouseover', function() {});

category_window.json.jbuilderから送られてきたIDを、変数へ代入する。

let id = this.id;

◎ とりあえず子カテゴリー以下を削除しておく。

$('.children-category').remove();
$('.grandchildren-category').remove();

◎ パラメーター(parent_id)に先ほど作成した変数を設定して、category_windowアクションを非同期で実行する。

  $.ajax({
    type: 'GET',
    url: '/get_category/new',
    data: {
      parent_id: id,
    },
    dataType: 'json',
  })

◎ Ajax通信が成功した場合は対応する子カテゴリーのHTMLを作成し、表示する。

.done(function(children) {
  children.forEach(function(child) {
    var html = buildChildHTML(child);
    $('.children-list').append(html);
  });
});

③孫カテゴリーのHTMLを作成する。

function buildGrandChildHTML(children) {
  var html = `
    <a class="grandchildren-category" id="${children.id}" href="/">
      ${children.name}
    </a>
  `;
  return html;
}

④ どの子カテゴリーにマウスが乗っているかによって、孫カテゴリーの表示内容を変更する。( とほぼ同じなので説明は省略)

$(document).on('mouseover', '.children-category', function() {
  var id = this.id;
  $.ajax({
    type: 'GET',
    url: '/get_category/new',
    data: {
      parent_id: id,
    },
    dataType: 'json',
  }).done(function(children) {
    children.forEach(function(child) {
      var html = buildGrandChildHTML(child);
      $('.grandchildren-list').append(html);
    });
    $(document).on('mouseover', '.children-category', function() {
      $('.grandchildren-category').remove();
    });
  });
});

注意

turbolinksを無効化しないとプルダウンメニューが非同期で動作しないので、必ず無効化しておきましょう。

turbolinksを無効化する方法

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?