0時に寝て7時に起きた。

vuetify で検索フォームのコンポーネント作成

ページング処理ができた。次に検索リクエストのための検索条件を扱うフォームを汎用コンポーネントで作ってみることにした。コンポーネントを生成するための検索条件のオブジェクトを外部から渡して、あとはよしなに grid に構成要素を配置する。v-date-picker のところは本当はもっと凝った作りをしないといけない。ここでは型で分岐してコンポーネントを配置する概念を表しているだけ。v-col は cols は1から12までの数字を受け取る。この数値を調整して v-spacer を入れることで行の位置調整もできるのがひと工夫しているところ。イベントハンドラーは click:search とか click:searchClearのように名前を付け替えて、外部から意図したイベントのみをフックできるように考慮している。

<template>
  <v-container>
    <v-row dense>
      <v-col v-for="item in conditions" :cols="item.cols" :key="item.label">
        <v-switch
          v-if="Boolean === item.type"
          v-model="item.value"
          :label="$t(item.label)"
          :clearable="true"
        />

        <v-text-field
          v-if="String === item.type"
          v-model="item.value"
          :label="$t(item.label)"
          :clearable="true"
        />

        <v-text-field
          v-if="Number === item.type"
          v-model="item.value"
          type="number"
          :label="$t(item.label)"
          :clearable="true"
        />

        <v-date-picker
          v-if="Date === item.type"
          v-model="item.value"
          :clearable="true"
        />

        <v-spacer v-if="null === item.type">
          <!-- use an empty block for grid layout -->
        </v-spacer>
      </v-col>
    </v-row>
    <v-row>
      <v-col cols="4">
        <v-btn
          v-text="$t('label.clearSearchCondition')"
          @click="_searchClear"
        />
      </v-col>
      <v-col cols="3">
        <v-btn color="primary" v-text="$t('label.search')" @click="_search" />
      </v-col>
    </v-row>
  </v-container>
</template>
<script lang="ts">
import { PropType, defineComponent } from '@vue/composition-api';
export interface SearchConditionItem<T = any> {
  type: PropType<T> | null;
  name: string;
  label?: string;
  col: number;
  value?: any;
  fromValue?: string | null;
  toValue?: string | null;
}

export default defineComponent({
  components: {},
  props: {
    conditions: {
      type: Array as PropType<SearchConditionItem[]>,
      default: () => [],
    },
  },
  setup(props, context) {
    return {};
  },
  methods: {
    _search(value: any) {
      this.$emit('click:search', value);
    },
    _searchClear(value: any) {
      for (const c of this.conditions) {
        c.value = null;
        c.fromValue = null;
        c.toValue = null;
      }
      this.$emit('click:searchClear', value);
    },
  },
});
</script>

呼び出し側ではこんな感じ。任意の conditions を渡し、検索ボタンをクリックしたときのイベントハンドラーを登録する。

<search-condition-form
  :conditions.sync="searchCondition"
  @click:search="search"
  v-on="$listeners"
/>

vuetify で初めてコンポーネントを作ってみた。雰囲気だけで実装している個人的な所感だけど、template, script, style を1つのファイルに同梱する考え方が私には馴染まない。1つのファイルに複数の構文が混在する認知負荷が気になるのと、1つのファイルに複数のコードを同梱しているメリットが私には感じられない。もしかしたら小さいシンプルなコンポーネントなら見通しがよいのかもしれない。しかし、業務での開発だと一定の複雑さをもつコンポーネントの方が大半だと思うので1ファイルが1画面におさまらない。どうせエディターを画面分割して複数画面でソースを読むのであれば、その画面のソースが1つのファイルでも別のファイルでも私にとってあまり大差ない。ファイル間の依存関係さえ適切に管理できればファイルは用途ごとに分割できた方が人間にとってわかりやすいのではないかとも思う。一方でフレームワーク側からみたら依存関係の解決はやや煩雑な処理になるので開発や依存管理がシンプルになってビルドが速くなるといったメリットがあったりするのかもしれない。どうなんだろう?