読者です 読者をやめる 読者になる 読者になる
一人もくもく会 α verでサービス開始しました。

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

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でちゃんとしたやり方があるかもしれない。