Angular状態管理基礎

状態管理は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  // ブラウザのストアで管理したいパラメータについて記述する

データの流れ

  1. componentからserviceを呼び出す
  2. serviceからstoreに値を登録する
  3. storeに登録されている値の中から、リアルタイム監視したいものはqueryで管理する
  4. 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);
  }
}

コメント

タイトルとURLをコピーしました