您好,欢迎访问一九零五行业门户网

Angular 根据 service 的状态更新 directive_AngularJS

angular js (angular.js) 是一组用来开发web页面的框架、模板以及数据绑定和丰富ui组件。它支持整个开发进程,提供web应用的架构,无需进行手工dom操作。
angularjs是为了克服html在构建应用上的不足而设计的。html是一门很好的为静态文本展示设计的声明式语言,但要构建web应用的话它就显得乏力了。这里angularjs就应运而生,弥补了html的天然缺陷,用于构件web应用等。
tl;dr
这篇文章讲解了三种根据 service 的状态更新 directive 的做法。分别是 $watch 表达式,事件传递,和 controller 的计算属性。
问题
我有一个 readerservice ,其中包含一些状态信息(比如连接状态和电量)。现在我需要做一个 directive 去展示这些状态。因为它只需要从 readerservice 中获取数据,不需要任何外部传值,所以我直接把 service 注入进去。但如何更新就成了一个问题。
service 的代码如下。
const status = {detach: 'detach',attach: 'attach',ready: 'ready'}class readerservice {constructor() {this.status = status// the status will be changed by some callbacksthis.status = status.detach}}angular.module('app').service('readerservice', readerservice)
directive 代码如下:
angular.module('app').directive('readerindicator', (readerservice) => {const status = readerservice.statusconst status_display = {[status.detach]: 'disconnected',[status.attach]: 'connecting...',[status.ready]: 'connected',}return {restrict: 'e',scope: {},template: `{{statusdisplay}}
`,link(scope) {// set and change scope.statusdisplay here}}})
我尝试过以下几种办法,下面一一介绍。
方法一:$watch
第一个想到的方法就是在 directive 中用 $watch 去监视 readerservice.status 。因为它不是 directive scope 的属性,所以我们需要用一个函数来包裹它。angular 会在 dirty-checking 时计算和比较新旧值,只有状态真的发生了改变才会触发回调。
// in directivelink(scope) {scope.$watch(() => readerservice.status, (status) => {scope.statusdisplay = status_display[status]})}
这个做法足够简单高效,只要涉及 readerservice.status 改变的代码会触发 dirty-checking ,directive 就会自动更新。service 不需要修改任何代码。
但如果有多个 directive 的属性都受 service status 的影响,那 $watch 代码就看得比较晦涩了。尤其是 $watch 修改的值会影响其他的值的时候。比如:
// in directivelink(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 已经更新了。所以我们需要在 link 中计算一个初始值。
我最开始是用 $broadcast 去做的。代码如下:
// in servicesetstatus(value) {this.status = value// need to inject $rootscopethis.$rootscope.$broadcast('reader.statuschanged', this.status)}// in directivelink(scope) {scope.statusdisplay = status_display[nfcreaderservice.status]scope.$on('reader.statuschanged', (event, status) => {scope.statusdisplay = status_display[status]})}
但马上发现 $broadcast 之后 ui 更新总要等 1 秒多(不过 $on 回调倒是很快)。google 一番后知道原因是 $broadcast 是向下层所有 scope 广播,广播完成后再 dirty-checking 。一个更好的做法是使用 $emit ,它只会向上传递事件,不过不管发送事件还是监听事件都得用 $rootscope 。
修改后的代码如下:
// in servicesetstatus(value) {this.status = value// use $emit instead of $broadcastthis.$rootscope.$emit('reader.statuschanged', this.status)}// in directivelink(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 强制触发 dirty-checking ,这也可以达到快速更新 ui 的目的。
方法三:controller + property
我个人觉得前两个方法能解决问题,但代码维护性都不太好。 $watch 在属性相互关联的情况下非常难看懂, $emit/$on 需要把一些逻辑写两次(初始化 directive 时和回调执行时)。方法一中我提到了有些时候声明式的属性比 $watch 更容易看懂。这个方法就是使用 controller 。directive 可以设置自己的 controller 作为数据来源(或者说 view model),我们可以把那些需要计算的属性作为 controller 的属性。这样 dirty-checking 时它们就会自动计算。
// in directiveclass readercontroller {constructor($scope, readerservice) {this.readerservice = readerservice}get statusdisplay() {return status_display[this.readerservice.status]}}return {// ...controller: readercontroller,controlleras: 'vm',template: `{{vm.statusdisplay}}
}
这样一来,大部分逻辑都可以挪到 controller 中。如果没有 dom 操作我们甚至可以不写 link 方法。也没必要加入额外的 $watch 和 $on 。只是因为 dirty-checking 的特性,绑定到 template 的属性往往会多计算几次。所以属性必须非常简单。大部分情况下这不会有什么问题。
以上内容是小编给大家介绍的angular 根据 service 的状态更新 directive,希望对大家有所帮助!
其它类似信息

推荐信息