Angular JS (Angular.JS) は、Web ページの開発に使用されるフレームワーク、テンプレート、データ バインディング、およびリッチ UI コンポーネントのセットです。開発プロセス全体をサポートし、手動による DOM 操作を必要としない Web アプリケーションのアーキテクチャを提供します。
AngularJS は、アプリケーションの構築における HTML の欠点を克服するように設計されています。 HTML は、静的テキスト表示用に設計された優れた宣言型言語ですが、WEB アプリケーションの構築には弱いです。ここで AngularJS が登場し、HTML の本来の欠点を補い、Web アプリケーションの構築に使用されます。
TL;DR
この記事では、サービスのステータスに基づいてディレクティブを更新する 3 つの方法について説明します。これらは、$watch 式、イベント配信、コントローラーの計算プロパティです。
質問
私は、いくつかのステータス情報 (接続ステータスやバッテリーなど) を含む ReaderService を持っています。次に、これらの状態を表示するディレクティブを作成する必要があります。 ReaderService からデータを取得するだけでよく、外部値は必要ないため、サービスを直接挿入します。しかし、どうやって更新するかが問題になります。
サービスコードは以下の通りです。
const STATUS = { DETACH: 'DETACH', ATTACH: 'ATTACH', READY: 'READY' } class ReaderService { constructor() { this.STATUS = STATUS // The status will be changed by some callbacks this.status = STATUS.DETACH } } angular.module('app').service('readerService', readerService)
ディレクティブコードは次のとおりです:
angular.module('app').directive('readerIndicator', (readerService) => { const STATUS = readerService.STATUS const STATUS_DISPLAY = { [STATUS.DETACH]: 'Disconnected', [STATUS.ATTACH]: 'Connecting...', [STATUS.READY]: 'Connected', } return { restrict: 'E', scope: {}, template: ` <div class="status"> {{statusDisplay}} </div> `, link(scope) { // Set and change scope.statusDisplay here } } })
私は以下の方法を試しました。以下に 1 つずつ紹介します。
方法 1: $watch
最初に思い浮かぶ方法は、ディレクティブで $watch を使用して、readerService.status を監視することです。これはディレクティブ スコープの属性ではないため、関数でラップする必要があります。 Angular はダーティ チェック中に古い値と新しい値を計算して比較し、実際に状態が変化した場合にのみコールバックがトリガーされます。
// In directive link(scope) { scope.$watch(() => readerService.status, (status) => { scope.statusDisplay = STATUS_DISPLAY[status] }) }
このメソッドはシンプルで十分に効率的です。readerService.status の変更を伴うコードがダーティ チェックをトリガーする限り、ディレクティブは自動的に更新されます。このサービスではコードを変更する必要はありません。
しかし、サービスのステータスに影響を受けるディレクティブ属性が複数ある場合、$watch コードはさらにわかりにくくなります。特に $watch によって変更された値が他の値に影響を与える場合。例:
// In directive link(scope) { scope.$watch(() => readerService.status, (status) => { scope.statusDisplay = STATUS_DISPLAY[status] scope.showBattery = status !== STATUS.DETACH }) scope.$watch('showBattery', () => { // some other things depend on showBattery }) }
現時点では、Ember や Vue の計算プロパティなど、宣言型プログラミング スタイルの方が理解しやすいでしょう。これについては後で説明します。
方法 2: $broadcast/$emit + $on
その考え方は、状態が変化するたびにサービスがイベントを送信し、ディレクティブがそのイベントをリッスンして状態を変更するというものです。ディレクティブが表示されるときにステータスが更新されている可能性があるためです。したがって、リンクの初期値を計算する必要があります。
最初は $broadcast でやりました。コードは次のとおりです:
// In service setStatus(value) { this.status = value // Need to inject $rootScope this.$rootScope.$broadcast('reader.statusChanged', this.status) } // In directive link(scope) { scope.statusDisplay = STATUS_DISPLAY[nfcReaderService.status] scope.$on('reader.statusChanged', (event, status) => { scope.statusDisplay = STATUS_DISPLAY[status] }) }
しかし、$broadcast 後の UI 更新には常に 1 秒以上かかることがすぐにわかりました (ただし、$on コールバックは非常に高速です)。 Google が調査した結果、その理由は $broadcast がすべての下位レベルのスコープにブロードキャストし、ブロードキャストの完了後にダーティ チェックが実行されるためであることがわかりました。より良いアプローチは $emit を使用することです。これはイベントを上方向に渡すだけですが、イベントを送信したりイベントをリッスンするには $rootScope を使用する必要があります。
変更されたコードは次のとおりです:
// In service setStatus(value) { this.status = value // Use $emit instead of $broadcast this.$rootScope.$emit('reader.statusChanged', this.status) } // In directive link(scope) { scope.statusDisplay = STATUS_DISPLAY[nfcReaderService.status] // Use $rootScope instead of scope $rootScope.$on('reader.statusChanged', (event, status) => { scope.statusDisplay = STATUS_DISPLAY[status] }) }
何らかの理由で $broadcast を使用する必要がある場合は、$on コールバックの最後で $digest または $apply を使用してダーティ チェックを強制的にトリガーできます。これにより、UI を迅速に更新するという目的も達成できます。
方法 3: コントローラー + プロパティ
個人的には最初の 2 つの方法で問題は解決できると思いますが、コードの保守性はあまり良くありません。 $watch は、属性が相互に関連している場合に理解するのが非常に困難です。 $emit/$on では、ロジックを 2 回記述する必要があります (ディレクティブの初期化時とコールバックの実行時)。方法 1 で、$watch よりも宣言型属性の方が理解しやすい場合があると述べました。この方法はコントローラーを使用する方法です。ディレクティブは独自のコントローラーをデータ ソース (またはビュー モデル) として設定でき、計算する必要があるプロパティをコントローラーのプロパティとして使用できます。こうすることで、ダーティ チェック中に自動的に計算されます。
// In directive class ReaderController { constructor($scope, readerService) { this.readerService = readerService } get statusDisplay() { return STATUS_DISPLAY[this.readerService.status] } } return { // ... controller: ReaderController, controllerAs: 'vm', template: ` <div class="status"> {{vm.statusDisplay}} </div> }
この方法で、ほとんどのロジックをコントローラーに移動できます。 DOM 操作がない場合は、link メソッドを記述する必要さえありません。 $watch と $on を追加する必要はありません。ダーティ チェックの特性により、テンプレートにバインドされたプロパティは数倍多く計算されることがよくあります。したがって、プロパティは非常に単純でなければなりません。ほとんどの場合、これは問題になりません。
上記の内容は、編集者が紹介したサービスの状況を踏まえたAngularアップデート指示です。皆様のお役に立てれば幸いです。