2018-11-27に更新

Angular2で共通データの管理(Shared Data)

※古い記事のため主にRxJSの説明になっています。共通データを管理したい場合はstoreを使いましょう。


Angularにて、アプリケーション全体で共通のデータを利用したいことなどがある。
例えばログインしているユーザーの情報など。

ダメな例

そこで共通データを管理するSharedDataService等を作る場合、
何も考えないと下記のようになると思う。

export class SharedDataService {
  data: any;

  constructor() {
    data = {};
  }

  set(key: string, value: any) {
    this.data[key] = value;
  }

  get(key: string) {
    return this.data[key];
  }
}

各コンポーネントからは下記のようにデータを取得。

const user = this.sharedDataService.get('user');

しかしこれはうまくいかない。

なぜかというと、だいたいログイン情報のようなものは非同期で取得して来ると思う。
その際にsetで値を保存すると思うが、その時はすでに各コンポーネントのonNgInit等も完了してしまっているし、
コンポーネントのプロパティに値を渡しているわけでもないので全体的に再描画はされない。

成功例

RxJSを使ってSharedDataService内のデータが変わったことを通知する仕組みにする必要がある。

export class SharedDataService {
  data: any;
  updateSources: any;
  syncs: any;

  constructor() {
    data = {};
    updateSources = {};
    syncs = {};
  }

  set(key: string, value: any) {
    this.data[key] = value;
    if (this.updateSources[key]) {
      this.updateSources[key].next(value);
    }
  }

  sync(key: string, callback: (data: any)=>void) {
    if (!this.syncs[key]) {
      const updateSource = new Subject<any>();
      this.updateSources[key] = updateSource;
      this.syncs[key] = updateSource.asObservable();
    }
    this.syncs[key].subscribe(result => callback(result));
    if (this.data[key] !== undefined) {
      callback(this.data[key]);
    }
  }
}

データを取得する各コンポーネント側では下記のようにする。

this.sharedDataService.sync('user', user => this.user = user);

これでどこかで値がsetされる度に各コンポーネントの値が更新されて再描画されていく。

応用例

この仕組みを利用するとtitleタグなどの書き換えも簡単にできる。
setTitleはどうもAppComponent上でしか動作しないようなので、まずAppComponentにて

this.sharedDataService.sync('title', this.titleService.setTitle(title));

ルーティングの各コンポーネントで

this.sharedDataService.set('title', article.title);

そしてtitleをsetしていないページではtitleが元に戻るように、AppComponentにて

this.router.events.subscribe((event) => {
  if (event instanceof NavigationStart) {
    this.data.set('title', null);
  }
});

これで指定したページでだけtitleが書き換えられる負担の少ない処理が可能となる。

補足

ざっと作ったのでもしかするともうちょっと一般的なやり方があるかもしれないし、
構文も間違っている箇所があるかもしれない。

syncはコールバックの利用でなくObservableをそのままreturnした方が良いのかもしれない。
ただ、元々データが存在した場合はすぐ値がほしいので、
それを考慮するとコールバック形式である必要があるかもしれないしObservableでちゃんとしたやり方があるかもしれない。

ツイッターでシェア
みんなに共有、忘れないようにメモ

だら@Crieit開発者

Crieitの開発者です。 Webエンジニアです(在宅)。大体10年ちょい。 記事でわかりにくいところがあればDMで質問していただくか、案件発注してください。 業務依頼、同業種の方からのコンタクトなどお気軽にご連絡ください。 業務経験有:PHP, MySQL, Laravel, React, Flutter, Vue.js, Node, RoR 趣味:Elixir, Phoenix, Nuxt, Express, GCP, AWS等色々 PHPフレームワークちいたんの作者

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント