はじめに
前回の記事に引き続き、状態管理ライブラリのAkitaについて学習していきます。今回は、AkitaのEntityStore
を使用して、TODOアプリを作成します。
環境
- Angular: 8.2.14
- @datorama/akita: 5.2.4
$ ng version
の実行結果
Angular CLI: 8.3.29 Node: 12.13.1 OS: darwin x64 Angular: 8.2.14 ... animations, common, compiler, compiler-cli, core, forms ... language-service, platform-browser, platform-browser-dynamic ... router Package Version ----------------------------------------------------------- @angular-devkit/architect 0.803.29 @angular-devkit/build-angular 0.803.29 @angular-devkit/build-optimizer 0.803.29 @angular-devkit/build-webpack 0.803.29 @angular-devkit/core 8.3.29 @angular-devkit/schematics 8.3.29 @angular/cli 8.3.29 @ngtools/webpack 8.3.29 @schematics/angular 8.3.29 @schematics/update 0.803.29 rxjs 6.4.0 typescript 3.5.3 webpack 4.39.2
TODOアプリ作成
データベースのテーブルのようなデータ構造を持っているEntityStoreを使用して、TODOアプリを作成します。
EntityStoreには、通常のStoreと違ってselectAll
やadd
、remove
などのメソッドが用意されています。
Model
まずは、Modelから作成していきます。Modelはデータベースでいうところのテーブルの構造にあたります。
todo.model.ts
import { ID } from '@datorama/akita'; export interface Todo { id: ID; title: string; } export function createTodo(params: Partial<Todo>) { return {} as Todo; }
EntityStore
TODOの状態を保持するStoreです。今回はEntityStoreを採用しています。
todo.store.ts
import { Injectable } from '@angular/core'; import { EntityState, EntityStore, StoreConfig } from '@datorama/akita'; import { Todo } from './todo.model'; export interface TodoState extends EntityState<Todo> {} @Injectable({ providedIn: 'root' }) @StoreConfig({ name: 'todo' }) export class TodoStore extends EntityStore<TodoState> { constructor() { super(); } }
Query
TODOの取得機能を提供しているQueryです。
todo.query.ts
import { Injectable } from '@angular/core'; import { QueryEntity } from '@datorama/akita'; import { TodoState, TodoStore } from './todo.store'; @Injectable({ providedIn: 'root' }) export class TodoQuery extends QueryEntity<TodoState> { constructor(protected store: TodoStore) { super(store); } }
Service
addTodo
メソッドとremoveTodo
メソッドでTODOの追加と削除の機能を提供しています。
todo.service.ts
import { Injectable } from '@angular/core'; import { guid, ID } from '@datorama/akita'; import { TodoStore } from './todo.store'; @Injectable({ providedIn: 'root' }) export class TodoService { constructor(private store: TodoStore) {} /** * TODOを追加する * * @param {string} title * @memberof TodoService */ addTodo(title: string) { this.store.add({ id: guid(), title, }); } /** * TODOを削除する * * @param {ID} id * @memberof TodoService */ removeTodo(id: ID) { this.store.remove(id); } }
Component
TODO画面のコンポーネントクラスです。QueryのselectAll
メソッドでTODOの全量を取得し、Serviceを経由してTODOの追加と削除を行っています。
todo.component.ts
import { TodoState } from './state/todo.store'; import { Component, OnInit } from '@angular/core'; import { getEntityType, ID } from '@datorama/akita'; import { Observable } from 'rxjs'; import { TodoService } from './state/todo.service'; import { TodoQuery } from './state/todo.query'; import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; @Component({ selector: 'app-todo', templateUrl: './todo.component.html', styleUrls: ['./todo.component.scss'], }) export class TodoComponent implements OnInit { readonly allTodo$: Observable<getEntityType<TodoState>[]>; todoFormGroup: FormGroup; titleControl: FormControl; constructor( private service: TodoService, private query: TodoQuery, private fb: FormBuilder ) { this.allTodo$ = this.query.selectAll(); this.todoFormGroup = this.fb.group({ title: ['', []], }); this.titleControl = this.todoFormGroup.get('title') as FormControl; } ngOnInit() {} /** * TODOを追加する * * @memberof TodoComponent */ addTodo() { this.service.addTodo(this.titleControl.value); } /** * TODOを削除する * * @param {ID} id * @memberof TodoComponent */ removeTodo(id: ID) { this.service.removeTodo(id); } }
todo.component.html
<div class="todo-area"> <form [formGroup]="todoFormGroup"> <mat-form-field> <input matInput placeholder="TODOタイトル" formControlName="title" /> </mat-form-field> </form> <button (click)="addTodo()" mat-raised-button color="primary"> Add TODO </button> </div> <ul> <li *ngFor="let todo of allTodo$ | async"> {{ todo.title }} <button (click)="removeTodo(todo.id)" mat-button color="warn">Remove</button> </li> </ul>
実際にTODOアプリを動作させる
上記のコードを動作させると、下記のようなアプリになります。Add TODO
ボタンを押下するとTODOが追加され、Remove
ボタンを押下すると、TODOが削除されます。
参考サイト
TypeScript特有の組み込み型関数 - log.pocka.io
Akita | Reactive State Management
Netanel Basal – Datorama Engineering
Angular向け状態管理ライブラリAkitaの紹介 - Qiita