LoginSignup
3
0

More than 3 years have passed since last update.

[nuxt] [rails] filterメソッドで多階層のセレクトボックスを非同期で実現する

Last updated at Posted at 2020-09-06

filterメソッドで多階層のセレクトボックスを非同期で実装することがあったので復習がてら記事にします。

前提条件

フロント: nuxt
バック: rails
api通信はできている

データ構成は下記記事を参考にする
Rails * Vue.js * Ajaxで多階層カテゴリを作った話

完成品
f6f3716e51204748799c283176cc6b6b.gif

rails側の実装

  • seed_fuでデータ作成
  • ルーティング生成
  • コントローラ作成
  • jbuilderでデータをjsonで返す
001_category.rb
Category.seed(:id,
  { id: 100, name: '親1'},
  { id: 110, name: '親1の子1'},
  { id: 111, name: '親1の子1の孫1'}, { id: 112, name: '親1の子1の孫2'}, { id: 113, name: '親1の子1の孫3'},
  { id: 120, name: '親1の子2'},
  { id: 121, name: '親1の子2の孫1'}, { id: 122, name: '親1の子2の孫2'}, { id: 123, name: '親1の子2の孫3'},
  { id: 130, name: '親1の子3'},
  { id: 131, name: '親1の子3の孫1'}, { id: 132, name: '親1の子3の孫2'}, { id: 133, name: '親1の子3の孫3'},

  { id: 200, name: '親2'},
  { id: 210, name: '親2の子1'},
  { id: 211, name: '親2の子1の孫1'}, { id: 212, name: '親2の子1の孫2'}, { id: 213, name: '親2の子1の孫3'},
  { id: 220, name: '親2の子2'},
  { id: 221, name: '親2の子2の孫1'}, { id: 222, name: '親2の子2の孫2'}, { id: 223, name: '親2の子2の孫3'},
  { id: 230, name: '親2の子3'},
  { id: 231, name: '親2の子3の孫1'}, { id: 232, name: '親2の子3の孫2'}, { id: 233, name: '親2の子3の孫3'}
)
routes.rb
Rails.application.routes.draw do
  resources :categories, only: %i[index]
end
categories_controller.rb
class CategoriesController < BaseController
  def index
    @categories = Category.all
  end
end
index.jbuilder
json.categories @categories do |category|
  json.extract! category, :id, :name
end

nuxt側の実装

nuxtの方はvuestifyというマテリアルデザインコンポーネントフレームワークを使用

category.vue
<template>
  <div>
    <v-select
      label="親カテゴリー"
      :items="parentCategory"
      item-text="name"
      item-value="id"
      v-model="parentSelected"
    />

    <v-select
      label="子カテゴリー"
      :items="childCategory"
      item-text="name"
      item-value="id"
      v-model="childrenSelected"
    />

    <v-select
      label="孫カテゴリー"
      :items="grandChildCategory"
      item-text="name"
      item-value="id"
      v-model="grandChildrenSelected"
    />
  </div>
</template>

<script>
export default {
  mounted() {
    this.fetchCategory()
  },

  data: () => {
    return {
      categories: [],
      parentSelected: '',
      childrenSelected: '',
      grandChildrenSelected: '',
    }
  },

  computed: {
    parentCategory() {
      return this.categories.filter((category) => category.id % 100 === 0)
    },

    childCategory() {
      if (this.parentSelected === '') {
        return []
      }

      return this.categories.filter((category) => {
                // 100の位が同じcategory
        return (Math.floor(category.id  / 100) === Math.floor(this.parentSelected / 100) &&
                // 1の位が0のcategory
                category.id % 10 === 0 &&
                // 親カテゴリーと一致しないcategory
                category.id !== this.parentSelected)
        })
    },

    grandChildCategory() {
      if (this.childrenSelected === '') {
        return []
      }

      return this.categories.filter((category) => {
                // 10の位が一致するcategory
        return (Math.floor(category.id  / 10) === Math.floor(this.childrenSelected / 10) &&
                // 1の位が0ではないのcategory
                category.id % 10 !== 0)
      })
    }
  },

  methods: {
    fetchCategory() {
      const url = 'api/categories'
      this.$axios
        .$get(url)
        .then((response) => {
          this.categories = response.categories
        })
        .catch((errors) => {
          console.log(errors)
        })
    }
  }
}
</script>
  • mountedでfetchCategoryを呼び出し
  • セレクトボックスのオプションをcomputedでそれぞれ定義
  • それぞれのセレクトボックスにv-modelを設定
  • 親のv-modelが変更 -> 子カテゴリーのオプションが変更
  • 子のv-modelが変更 -> 孫カテゴリーのオプションが変更

filterはこのように使用します

[検証したいデータ].filter((1データ) => 条件式)

親カテゴリーの例でみてみます

// 親カテゴリー
<v-select
  label="親カテゴリー"
  :items="parentCategory"
  item-text="name"
  item-value="id"
  v-model="parentSelected"
/>

// オプション定義
parentCategory() {
  return this.categories.filter((category) => category.id % 100 === 0)
  // 100 % 100 = 0, 110 % 100 = 10, 111 % 100 = 11, 200 % 100 = 0
},

:items="オプションのデータ"なのでcomputed内にあるparentCategoryを参照します
this.categoriesにはrailsで定義したCategory.allのデータが入っています。
categoryには{ id: 100, name: '親1' }のように一つのデータが入っています。
そのデータのidを100で割った時のあまりが0と一致するデータを返すという処理になっているので
この場合idが100, 200, 300...のレコードが返ります。

以上です。

filterの条件式をもう少しうまく記述できそうな気がしますが。。。
他にいい方法があればコメントで教えてください!

3
0
1

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
3
0