状態管理はSPA(シングルページアプリケーション)の実装に用いられる。
状態管理では、単純にHTMLからjavascriptを呼び出してリクエストを送るだけでなく、ストアという概念が登場する。
ストアにデータをため込んで使用することで、ページ遷移を伴わずに、リアクティブにページ内で処理を行うことができる。
ファイル階層
compName
┗compName.component.html
┗compName.component.css
┗compName.component.ts // Controller的な役割
┗state
┗compName.model.ts
┗compName.query.ts // リアルタイム監視したいパラメータについて記述する
┗compName.service.ts // ビジネスロジックを記述する
┗compName.service.spec.ts // serviceのテストを記述する
┗compName.store.ts // ブラウザのストアで管理したいパラメータについて記述する
データの流れ
- componentからserviceを呼び出す
- serviceからstoreに値を登録する
- storeに登録されている値の中から、リアルタイム監視したいものはqueryで管理する
- componentはqueryを参照して、リアルタイムで値が反映される
実装方法
compNamt.component.tsの書き方
@Component({
selector: 'app-compname', //コンポーネントの名称を定義
templateUrl: './compName.component.html', // 使用するHTMLファイルを指定
styleUrls: ['./compName.component.scss'] // 使用するCSSファイルを指定
})
export class CompNameComponent implements OnInit {
/** プロパティを記載 */
readonly PROP = "sample"; // 固定値は readonlyを使用する
sampleParam: string;
sampleParam$ = this.compNameQuery.sampleParam$; //クエリの値をコンポーネントにバインド
constructor(
private service: CompNameService,
private query: CompNameQuery,
private router: Router //ページ遷移するときとかによく使うパッケージ
) {
// 初期值設定
this.sampleParam = '';
}
// 画面起動時の処理 (Angularのデフォルトのメソッド)
async ngOnInit(): Promise {
this.service.init();
}
// 関数。htmlからこの関数名で呼び出す。
// ビジネスロジックはservice.tsに記載し、ここでそれを呼び出すのみとする。
async funkName(): Promise {
await lastValueFrom(this.service.serviceFunkName())
.catch((error) => {
// エラー時の処理
})
.finally (() => {
// エラーが起きても実行したい処理
});
}
compName.service.tsの書き方
import { CompNameQuery } from './compname.query';
@Injectable({ providedIn: 'root' }) // DI
export class CompNameService {
constructor(
private compNameQuery CompNameQuery) {}
init(): Observable {
// 例としてREST APIにリクエストを送る処理
// ヘッダー送信する場合
const httpOptions = {
headers: new HttpHeaders({
headerKey: headerValue
})
};
// クエリパラメータ
const queryParam = 'queryParamVal';
// リクエストBody
const initRequest: InitRequest = { // modelに定義が必要
param: 'paramVal'
this.compNameQuery.getValue().sampleParam // クエリでobserve(監視)しているパラメータを取得する場合
}
// リクエスト送信
return this.http
.get(`/api/sampleapi?queryParam=${queryParamVal}`, initRequest, httpOptions)
// 第一引数はAPIのURI。リテラルに変数を埋め込む場合は``でくくり${}で変数指定
// 第2引数にリクエストBody
// 第3引数にオプション(ヘッダーなど)
// レスポンスがvoidの場合は、get<void>とする
.pipe(
tap((response) => {
this.compNameStore.update({
storeParam: response.storeParam; // レスポンスをストアに設定
});
}));
}
}
compName.store.tsの書き方
おまじない部分が多い。
export interface CompNameState {
param: string
}
// Stateとは 『状態』のこと
// Stateの初期値の設定
export function createInitialState(): CompNameState {
return {
param: ''
};
}
@Injectable({ providedIn: 'root' }) // DI
@StoreConfig({ name: 'compName', resettable: true })
export class CompNameStore extends Store {
constructor() {
super(createInitialState()); // おまじない
}
}
compName.model.tsの書き方
export class CompNameInitResponse {
param: string
initReqObj: InitReqObj
}
export interface InitReqObj {
param2: string
}
compName.query.tsの書き方
@Injectable({ providedIn: 'root' })
export class CompNameQuery extends Query {
//ウォッチしたいパラメータを設定
param$ = this.select(
(state) => state.sampleParam // selectメソッドによってObservalになる
);
constructor(protected override store: Cws1Store) { // おまじない
super(store);
}
}
コメント