てっくらふと

IT技術系ブログ。たまにV系もあるかも : JetpackCompose / Django / FastAPI

Vue.jsで条件によって動的にコンポーネントを変更[Vue.js+TypeScript]

お久しぶりです。

皆さんはVue.jsを使用していて、条件によってVue.jsのコンポーネントを変更したいということはありませんか?
if文で分けていく方法もありますが、コンポーネントや条件が多くなっていくとどんどん読みにくくなっていってしまいます。


そういった際に、どのような方法でプログラムを組めばいいかという一例を紹介します。

実装はVue.jsとTypeScriptを使ってやっていきます。

例として使用するプログラムの構成とイメージ

f:id:wintermaples:20200514075423j:plain

状況

データ(投票数とか、売上とか)の集計結果を表示したいが、データによってグラフを変えたり表示方法を変更したいなどの理由で、データの種類毎にコンポーネントを変更したい。

ソースコード

Factories.ts

import { VueConstructor } from "vue";

export abstract class Factory {
  /**
   * このFactoryクラスが、「引数に指定されたデータを 表示するためのコンポーネント」に変換するためのFactoryである場合はtrueを返します。
   * @param data データ
   */
  abstract isFactoryFor(data: Data): boolean;

  /**
   * 渡されたデータを表示するためのコンポーネントを生成します。
   * @param data 
   */
  abstract generateComponent(data: Data): VueConstructor<Vue>;

  static addFactory(factory: Factory): void {
    this.factories.push(factory);
  }

  static findFactory(data: Data): Factory|undefined {
    return this.factories.find(f => f.isFactoryFor(data));
  }

  static factories: Factory[] = [];
}


class PieChartFactory extends Factory {
  isFactoryFor(data: Data): boolean {
    //データの種類がPieChartを表示したい時にtrue
  }
  generateComponent(data: Data): VueConstructor<Vue> {
    //コンポーネントを生成
    //例:
    //return PieChartComponent;
  }
}

class LineChartFactory extends Factory {
  isFactoryFor(data: Data): boolean {
    //データの種類がLineChartを表示したい時にtrue
  }
  generateComponent(data: Data): VueConstructor<Vue> {
    //コンポーネントを生成
    //例:
    //return LineChartComponent;
  }
}

class TextAnswerFactory extends Factory {
  isFactoryFor(data: Data): boolean {
    //データの種類がTextAnswerを表示したい時にtrue
  }
  generateComponent(): VueConstructor<Vue> {
    //コンポーネントを生成
    //例:
    //return TextAnswerComponent;
  }
}

Factory.addFactory(new PieChartFactory());
Factory.addFactory(new LineChartFactory());
Factory.addFactory(new TextAnswerFactory());

Graphs.vue

<template>
<div>
  <component v-for="data in datas"
    :is="toChartComponent(data)"
    :data="data"
  />
</div>
</template>

<script lang="ts">
import Vue from "vue";
import Component from "vue-class-component";
import { Factory } from "Factories";

@Component
export default class App extends Vue {
  datas: Data[]|null = []; //データの配列。何らかの方法でデータを入れる
  toChartComponent(data): VueConstructor<Vue> {
    return Factory.findFactory(data).generateComponent();
  }
}
</script>

<style lang="scss" scoped>
</style>

※実際に使用する時は、各自で必要なところを変更してください。

プログラムの解説

原理としては、Factoryパターンを使用して実装しています。

まず、データの種類毎に、Factoryクラスを作っていってそれを登録(Factory.addFactory)していきます。
次に、toChartComponent(Graphs.vue内)でFactory.findFactoryを呼んであげて、データの種類によって使用するFactoryを分けています。
そして、generateComponentを呼んであげてコンポーネントを生成しています。

そして、vueのtemplate内にcomponentタグを作成してあげて「:is」でtoChartComponentを呼ぶことによって生成するコンポーネントを動的に変更することができます。

デモ


See the Pen
Blog-How-To-Change-Vuejs-Component-Dynamically
by wintermaples (@wintermaples)
on CodePen.


※グラフの表示は省略しています。

参考文献

コンポーネント - vue.js


間違っている点、こうしたらいいよ!って所があればコメントよろしくおねがいしますm(_ _)m

CSS Design created by satotaka99. Thankyou.