Angular 根据 service 的状态更新 directive
TL;DR这篇文章讲解了三种根据 service 的状态更新 directive 的做法。分别是 问题我有一个 service 的代码如下。 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) directive 代码如下: 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 } } }) 我尝试过以下几种办法,下面一一介绍。 方法一:$watch第一个想到的方法就是在 directive 中用 // In directive link(scope) { scope.$watch(() => readerService.status,(status) => { scope.statusDisplay = STATUS_DISPLAY[status] }) } 这个做法足够简单高效,只要涉及 但如果有多个 directive 的属性都受 service status 的影响,那 // 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 里面的 computed property 。这个待会讨论。 方法二:$broadcast/$emit + $on这种思路是 service 每次状态改变都发送一个事件,然后 directive 监听事件来改变状态。因为 directive 渲染的时候也许 status 已经更新了。所以我们需要在 我最开始是用 // 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] }) } 但马上发现 修改后的代码如下: // 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',status) => { scope.statusDisplay = STATUS_DISPLAY[status] }) } 如果因为某些原因不得不用 方法三:controller + property我个人觉得前两个方法能解决问题,但代码维护性都不太好。 // 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> ` } 这样一来,大部分逻辑都可以挪到 controller 中。如果没有 DOM 操作我们甚至可以不写 参考资料$rootScope.Scope $rootScope.$emit() vs $rootScope.$broadcast() (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |