RxJSとVue.jsを活用したリアクティブGUIプログラミング

実装要件

  1. GUI構成:3つのテキストフィールドと1つのラベル
  2. ラベルには3つの数値入力の合計をリアルタイム表示
  3. 不正な数値入力時は0として処理
  4. 初期値は各フィールドに1, 2, 3を設定

プロジェクト構築手順


# Vue CLIのインストール
$ npm install --global @vue/cli

# プロジェクト作成
$ vue create rxjs-vue-demo
# 手動設定でBabelとTypeScriptを選択

# 開発サーバー起動
$ cd rxjs-vue-demo
$ npm run serve

RxJS非使用時の実装例

<template>
  <p>
    <input v-model="num1" @input="calculateSum" class="input-field"> +
    <input v-model="num2" @input="calculateSum" class="input-field"> +
    <input v-model="num3" @input="calculateSum" class="input-field"> =
    <span>{{ total }}</span>
  </p>
</template>

<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
  data() {
    return {
      num1: '1',
      num2: '2',
      num3: '3',
      total: '6'
    };
  },
  methods: {
    parseInput(value: string): number {
      return Number(value) || 0;
    },
    calculateSum() {
      const sum = 
        this.parseInput(this.num1) + 
        this.parseInput(this.num2) + 
        this.parseInput(this.num3);
      this.total = sum.toString();
    }
  },
  mounted() {
    this.calculateSum();
  }
});
</script>

<style scoped>
.input-field {
  width: 60px;
  text-align: right;
}
</style>

RxJS使用時の実装例

<template>
  <p>
    <input id="field1" class="input-field" value="1"> +
    <input id="field2" class="input-field" value="2"> +
    <input id="field3" class="input-field" value="3"> =
    <span>{{ reactiveTotal }}</span>
  </p>
</template>

<script lang="ts">
import { defineComponent, onMounted } from 'vue';
import { fromEvent, combineLatest } from 'rxjs';
import { map, pluck, startWith } from 'rxjs/operators';

export default defineComponent({
  data() {
    return {
      reactiveTotal: '6'
    };
  },
  setup() {
    const createInputObservable = (id: string) => {
      const element = document.getElementById(id) as HTMLInputElement;
      return fromEvent(element, 'input').pipe(
        map(event => (event.target as HTMLInputElement).value),
        startWith(element.value)
      );
    };

    onMounted(() => {
      const parsers = (value: string): number => Number(value) || 0;
      
      combineLatest([
        createInputObservable('field1'),
        createInputObservable('field2'),
        createInputObservable('field3')
      ]).pipe(
        map(([a, b, c]) => 
          parsers(a) + parsers(b) + parsers(c)
        ),
        map(total => total.toString())
      ).subscribe(total => {
        // Vueのリアクティブデータを更新
        this.reactiveTotal = total;
      });
    });
  }
});
</script>

<style scoped>
.input-field {
  width: 60px;
  text-align: right;
}
</style>

タグ: rxjs vue.js reactive-programming TypeScript

7月21日 17:07 投稿