実装要件
- GUI構成:3つのテキストフィールドと1つのラベル
- ラベルには3つの数値入力の合計をリアルタイム表示
- 不正な数値入力時は0として処理
- 初期値は各フィールドに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>