はじめに
Angularでアプリを開発し始めたとき、RxJSの概念には本当に戸惑いました。「オブザーバブル」「サブジェクト」「オペレータ」という言葉の意味がわからず、コードを書くのに苦労しました。でも実際に使ってみると、非同期処理やイベント処理がとても簡潔に書けるようになり、その威力を実感しました。
RxJS
RxJSは非同期処理を扱うためのライブラリです。その大きな特徴は、すべてをデータの流れとして考えることです。ユーザーのクリック、HTTPリクエスト、時間の経過など、あらゆる出来事をデータの流れとして統一的に扱います。
これにより、複雑な非同期処理を簡潔に書けるようになり、特にAngularアプリケーションでの開発効率が大幅に向上します。
実装
コンポーネントの作成
ng generate component reduce-demo
TypeScript
import { Component, OnInit } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { map, Subject } from 'rxjs';
@Component({
selector: 'app-reduce-demo',
standalone: true,
imports: [FormsModule, CommonModule],
templateUrl: './reduce-demo.component.html',
styleUrl: './reduce-demo.component.css'
})
export class ReduceDemoComponent implements OnInit {
// 入力フィールドの値
numbersInput = '';
// 変換後の数値配列
numbers: number[] = [];
// 計算結果
sum = 0;
// RxJSのSubject - イベントを発行するためのオブジェクト
private inputSubject = new Subject<string>();
ngOnInit() {
// RxJSのパイプラインを設定
this.inputSubject.pipe(
// 文字列を数値配列に変換
map((input: string) => {
// 例: input = "1, 2, 3"
return input
.split(',') // ["1", " 2", " 3"]
.map((n) => Number(n.trim())) // [1, 2, 3]
.filter((n) => !isNaN(n)); // 無効な値を除外
}),
// 合計を計算
map(numbers => {
// 例: numbers = [1, 2, 3]
this.numbers = numbers; // 配列を保存
// 合計計算
return numbers.length > 0
? numbers.reduce((acc, curr) => acc + curr, 0)
: 0;
})
).subscribe(sum => {
// 例: sum = 6
this.sum = sum; // 結果を保存
});
}
// 入力イベントハンドラ
processInput() {
// 入力値をSubjectに送信
this.inputSubject.next(this.numbersInput);
}
}
HTML
<div class="container">
<h2>RxJSを使ったリアクティブな計算</h2>
<!-- 入力フォーム -->
<div class="input-section">
<label for="numbers">数値をカンマ区切りで入力してください:</label>
<input
id="numbers"
type="text"
[(ngModel)]="numbersInput"
(input)="processInput()"
placeholder="例: 1,2,3,4,5"
/>
</div>
<!-- 結果表示 -->
<div class="results" *ngIf="numbers.length > 0">
<h3>結果:</h3>
<p>入力された数値: {{ numbers.join(', ') }}</p>
<p>合計: {{ sum }}</p>
</div>
</div>
RxJSの主要概念と実装解説
初心者にとって難しいRxJSの概念を、計算アプリの実装を通して説明します。
オブザーバブル
オブザーバブルは水が流れる川のようなものです。データが時間とともに流れていきます。この実装では、ユーザー入力の変化が川の流れになっています。入力が変わるたびに、新しいデータが流れていきます。
// Subjectはオブザーバブルの一種です
private inputSubject = new Subject<string>();
// パイプラインの設定(この中をデータが流れていきます)
this.inputSubject.pipe(
map(...),
map(...)
)
この例では、inputSubject
がオブザーバブルとして機能し、パイプラインを通じてデータが流れていきます。ユーザーが入力フィールドに何かを入力するたびに、そのデータがオブザーバブルに流れ込み、パイプラインで処理されていきます。
サブジェクト
サブジェクトは水源と川の両方の性質を持つものです。水を供給することも、水を流すこともできます。この実装では、inputSubject
がサブジェクトとして機能しています。
private inputSubject = new Subject<string>();
ユーザーが入力フォームに何かを入力すると、processInput()
メソッドが呼び出され、inputSubject.next()
でデータを発行します。これは川に水を流す(データを送り出す)ようなものです。
processInput() {
// 入力値をSubjectに送信
this.inputSubject.next(this.numbersInput);
}
また、サブジェクトにはパイプラインが接続されており、流れてきた水(データ)を受け取って処理もします。
オペレータ
オペレータは川に設置されたフィルターのようなものです。流れてくる水(データ)を受け取って、形を変えて次に流します。
この実装では、map
オペレータを使用しています。最初のmap
は入力された文字列をカンマで分割し、数値の配列に変換するフィルターです。2番目のmap
はその数値配列を合計値に変換するフィルターです。
例えば、ユーザーが 1,2,3
と入力すると、
最初のmap
でデータは "1,2,3"
から [1,2,3]
に変換されます
map((input: string) => {
return input
.split(',')
.map((n) => Number(n.trim()))
.filter((n) => !isNaN(n));
})
2番目のmap
でデータは [1,2,3]
から 6
に変換されます
// 合計を計算
map(numbers => {
// 例: numbers = [1, 2, 3]
this.numbers = numbers; // 配列を保存
// 合計計算
return numbers.length > 0
? numbers.reduce((acc, curr) => acc + curr, 0)
: 0;
})
サブスクライブ
.subscribe(sum => {
this.sum = sum;
});
サブスクライブは川の下流に立って、流れてくる水(データ)を受け取ります。
この実装では、subscribe
メソッドがその役割を果たしています。パイプラインの最後で変換されたデータ(この場合は合計値)を受け取り、それを使って画面を更新します。ユーザーが 1,2,3
と入力した場合、最終的に 6
という値がサブスクライブの部分に届き、その値がthis.sum
に保存されてUIに表示されます。
まとめ
この記事では、RxJSの基本である「オブザーバブル」「サブジェクト」「オペレータ」「サブスクライブ」を簡単な計算アプリで学びました。
RxJSは最初は難しく感じますが、データの流れとして考えれば理解しやすくなります。まずは小さなアプリから始めて、少しずつ慣れていきましょう。RxJSを使いこなせば、より簡潔で保守しやすいコードが書けるようになります。
説明が不十分な点や疑問に思う部分があれば、お気軽にご指摘ください。RxJSについて一緒に理解を深められたら幸いです。