(1年前に途中までまとめたもの。こっちに転載。)
AngularJS: Notifying about changes from services to controllers - codelord.net を参考にStoreっぽいものを実装しようとした時のメモ
// component class FooController { constructor( private $scope: ng.IScope, private NotifyingService: NotifyingService, ) { } public $onInit() { this.NotifyingService.subscribe(this.$scope, () => { this.doSomething(); }); } private doSomething() { // do something // } } const FooComponent: ng.IComponentOptions = { template: '...' controller: FooController, bindings: { // ... } }; // service const EVENT_NAME: string = 'notifying-service-event'; class NotifyingService { $data: any; constructor(private $rootScope: ng.IRootScopeService) { } public initialize() { // this.$data = new Data(); } public subscribe(scope: ng.IScope, callback: (any) => void) { const handler = this.$rootScope.$on(EVENT_NAME, callback); scope.$on('$destroy', handler); } public update(data, noti: boolean = true) { const prev = angular.copy(this.$data); angular.extend(this.$data, data); if (!angular.equals(prev, this.$data)) { if (noti) { this.notify(); } } } private notify() { this.$rootScope.$emit(EVENT_NAME); } } angular.module('app', []) .component('foo', FooComponent) .service('NotifyingService', NotifyingService);
$destroy
がある$emit
しか利用しない- eventの挙動は隠蔽されている
簡易版
angular.module('app').controller('TheCtrl', function(NotifyingService) { NotifyingService.subscribe(function somethingChanged() {}); }); angular.module('app').factory('NotifyingService', function() { var onChanges = []; return { subscribe: function subscribe(callback) { onChanges.push(callback); }, notify: function notify() { onChanges.forEach(function(cb) { cb(); }); } }; });
- 「変更した」というアクションに限定
$scope
,$rootScope
が不要- React • TodoMVC に似ている
- 複数のCtrlから利用するときに、個別リスナー解除ができない
実際にやろうとして失敗したこと
(構成)
(例: destory時の流れ)
- ui-viewがまだコンポーネント対応していなかった
- inputs/outputsにほうがシンプルでよかったかも
- storeの並列が一番無理があった
- 両方で同じようなデータを利用するとき
- 両方に持つのも、主従にするのも…
- 初期化のタイミング、更新順
- 両方で同じようなデータを利用するとき
- containerとstoreのデータ被り
- templateで利用する必要があるため、多数の変数をcontainer上で展開
- (これは
$data: FooData;
みたいにすればよかったのかもしれない↓)
class FooController { $data: FooData; constructor( private $scope: ng.IScope, private FooStateService: ParentStateService ) { } public $onInit() { this.FooStateService.initialize(); this.FooStateService.subscribe(this.$scope, () => { this.$data = this.FooStateService.data; }) this.$data = this.FooStateService.data; } public handleUpdateBar($event: { value: number }) { this.ParentStateService.update({ bar: $event.value }); } }