とあるエンジニアの備忘録

【Vue】propsのobject,Arrayに型判定などのバリデーションを追加する

Javascript

Vueで開発を進めて約1年ほど開発をしていますが、だんだんコンポーネントが増えていろんな場所でコンポーネントが使われると Objectで値を渡されているのをみると、不安でしかない... 最初は小規模のプロジェクトだったのでオブジェクトで渡すことも少なかったのですが、だんだん大きなプロジェクトになると当たり前のようにオブジェクトが渡されるようになる...

例えば、APIからのレスポンスをそのままpropsにオブジェクトに渡している時などは下記のようなオブジェクトが渡ってくることがありました。

const post = {
  id: 1,
  title: "hoge",
  status: "publish",
  body: "テキストテキスト",
  category: { id: 1, name: "カテゴリー名" }
  tags: [
    { id: 1, name: "タグ1" },
    { id: 2, name: "タグ2" },
  ]
}

プロジェクトにもよりますが、これをそのままpropsに渡すケースがあったりします...

<template>
  <p>{{ post.body }}</p>
  <p>{{ post.category.name }}</p>
  <div v-for="tag in post.tags" :key="tag.id">
    <p>{{ tag.name }}</p>
  </div>
</template>

<script>
export default {
  props: {
    post: {
      type: Object,
      default: [] => ()
    }
  }
}
</script>

これtagsがなかったり、categoryがないケースは確実にエラー出ますよね... プロジェクトが肥大化してたり、あまり厳密にAPI、component設計をしていない案件は確実にこの辺で詰みます

API改修したり、コンポーネントの改修するのも良いのですが、(上記の例は簡易的ですが) どこまで影響があるのかわからないので修正するのも辛い状況になります。そしてそれをまた修正する... コンポーネントがおかしなことになります。

そうなったときに明示的にvalidationをつけたり、vueでクラス運用しているプロジェクトであればvalidationを入れることをお勧めします。

例えば上記の例で言えば、categoryやtagsがない場合エラーを出すようにしています。もちろん厳密にチェックしても良いです。

export default {
  props: {
    post: {
      type: Object,
      default: [] => (),
      validator(post) {
        // タグ、categoryがない場合エラー
        if (post.tags.length < 1 || !post.category) return false
        // NOTE: 必要に応じて厳密に全部validationを加える
        return true
      },
    }
  }
}

そもそもこんなオブジェクト渡すなって話なのですが...(笑)しょうがないケースもたくさんあります。

ただこのバリデーションをすべて記載するのも辛かったりします。 そこでclassの定義を下記のようして必須の情報を全て存在判定します。

class Post {
  constructor(data) {
    if (this.id || this.title || this.body) {
      throw new Error('postデータがありません');
    }
    if (this.category.id || this.category.name) {
      throw new Error('categoryデータがありません');
    }
    this.tags.forEach(tag => {
      if (this.tag.id || this.tag.name) {
        throw new Error('tagデータがありません');
      }
    })

    this.id = data.id
    this.title = data.title
    this.body = data.body
    this.category = data.category
    this.tags = data.tags
  }
}
import Post from '@/model/post'

export default {
  props: {
    post: {
      type: Post,
      default: [] => (),
    }
  }
}

とするとのコンポーネント他のコンポーネントでも問題なくできたり、プロパティの確認も容易にできたりしますね。 配列の場合はArrayList的なクラスを用意するのも良いですが、これをそのまま使ってバリデーションでチェックもできます。プロジェクトの方針によって決めて良きかなと思います。

import Post from '@/model/post'

export default {
  props: {
    posts: {
      type: Array,
      default: [],
      validator(posts) {
        return posts.every(post => {
          return post instanceof Post
        })
      },
    }
  }
}

今のところこの方針で開発を進めています。 冗長的な部分もあったりしますが、コードはかきやすくなりました。

すぐclassの導入が大変という場合でもvalidatorをcomponentで利用する値だけかけておけば 保守性が高まり他の人が使った時にも何の値が必要か不要かわかったり、リファクタリングする際にも役立つので書いておくのをオススメします。

はやくtypescript対応したい

参考

https://jp.vuejs.org/v2/guide/components-props.html#%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3%E3%81%AE%E3%83%90%E3%83%AA%E3%83%87%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3

https://jp.vuejs.org/v2/guide/components-props.html#%E5%9E%8B%E3%81%AE%E6%A4%9C%E6%9F%BB


© 2020 物置エンジニアの備忘録