Angular 根据 service 的状态更新 directive


Posted in Javascript onApril 03, 2016

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 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 中用 $watch 去监视 readerService.status 。因为它不是 directive scope 的属性,所以我们需要用一个函数来包裹它。Angular 会在 dirty-checking 时计算和比较新旧值,只有状态真的发生了改变才会触发回调。

// In directive
link(scope) {
scope.$watch(() => readerService.status, (status) => {
scope.statusDisplay = STATUS_DISPLAY[status]
})
}

这个做法足够简单高效,只要涉及 readerService.status 改变的代码会触发 dirty-checking ,directive 就会自动更新。service 不需要修改任何代码。

但如果有多个 directive 的属性都受 service 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 里面的 computed property 。这个待会讨论。

方法二:$broadcast/$emit + $on

这种思路是 service 每次状态改变都发送一个事件,然后 directive 监听事件来改变状态。因为 directive 渲染的时候也许 status 已经更新了。所以我们需要在 link 中计算一个初始值。

我最开始是用 $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 是向下层所有 scope 广播,广播完成后再 dirty-checking 。一个更好的做法是使用 $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 强制触发 dirty-checking ,这也可以达到快速更新 UI 的目的。

方法三:controller + property

我个人觉得前两个方法能解决问题,但代码维护性都不太好。 $watch 在属性相互关联的情况下非常难看懂, $emit/$on 需要把一些逻辑写两次(初始化 directive 时和回调执行时)。方法一中我提到了有些时候声明式的属性比 $watch 更容易看懂。这个方法就是使用 controller 。directive 可以设置自己的 controller 作为数据来源(或者说 view model),我们可以把那些需要计算的属性作为 controller 的属性。这样 dirty-checking 时它们就会自动计算。

// 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 操作我们甚至可以不写 link 方法。也没必要加入额外的 $watch 和 $on 。只是因为 dirty-checking 的特性,绑定到 template 的属性往往会多计算几次。所以属性必须非常简单。大部分情况下这不会有什么问题。

以上内容是小编给大家介绍的Angular 根据 service 的状态更新 directive,希望对大家有所帮助!

Javascript 相关文章推荐
Firefox getBoxObjectFor getBoundingClientRect联系
Oct 26 Javascript
js过滤HTML标签以及空格的思路及代码
May 24 Javascript
javascript模拟枚举的简单实例
Mar 06 Javascript
JS实现表单中checkbox对勾选中增加边框显示效果
Aug 21 Javascript
BootStrap下拉框在firefox浏览器界面不友好的解决方案
Aug 18 Javascript
Javascript计算二维数组重复值示例代码
Dec 18 Javascript
记一次webpack3升级webpack4的踩坑经历
Jun 12 Javascript
详解关于vue-area-linkage走过的坑
Jun 27 Javascript
Vue用v-for给循环标签自身属性添加属性值的方法
Oct 18 Javascript
Vue两个版本的区别和使用方法(更深层次了解)
Feb 16 Javascript
jQuery 添加元素和删除元素的方法
Jul 15 jQuery
使用jQuery实现购物车
Oct 29 jQuery
jQuery中的Deferred和promise 的区别
Apr 03 #Javascript
再次谈论React.js实现原生js拖拽效果引起的一系列问题
Apr 03 #Javascript
jQuery qrcode生成二维码的方法
Apr 03 #Javascript
Node.js 应用跑得更快 10 个技巧
Apr 03 #Javascript
AngularJs 60分钟入门基础教程
Apr 03 #Javascript
深入浅析JSON.parse()、JSON.stringify()和eval()的作用详解
Apr 03 #Javascript
基于JavaScript实现 网页切出 网站title变化代码
Apr 03 #Javascript
You might like
php实现将wav文件转换成图像文件并在页面中显示的方法
2015/04/21 PHP
javascript数组使用调用方法汇总
2007/12/08 Javascript
JQuery实现简单验证码提示解决方案
2012/12/20 Javascript
JS验证邮箱格式是否正确的代码
2013/12/05 Javascript
javascript 用函数语句和表达式定义函数的区别详解
2014/01/06 Javascript
JavaScript的Backbone.js框架入门学习指引
2016/05/07 Javascript
基于jquery插件实现拖拽删除图片功能
2020/08/27 Javascript
jQuery EasyUI框架中的Datagrid数据表格组件结构详解
2016/06/09 Javascript
vue插件tab选项卡使用小结
2016/10/27 Javascript
详解Javascript中DOM的范围
2017/02/13 Javascript
浅析JS中的 map, filter, some, every, forEach, for in, for of 用法总结
2017/03/29 Javascript
微信小程序request出现400的问题解决办法
2017/05/23 Javascript
input输入框内容实时监测(附代码)
2017/08/15 Javascript
如何使node也支持从url加载一个module详解
2018/06/05 Javascript
LayUi使用switch开关,动态的去控制它是否被启用的方法
2019/09/21 Javascript
云服务器部署Node.js项目的方法步骤(小白系列)
2020/03/23 Javascript
python 打印对象的所有属性值的方法
2016/09/11 Python
Python实现mysql数据库更新表数据接口的功能
2017/11/19 Python
Python爬虫实现全国失信被执行人名单查询功能示例
2018/05/03 Python
python 获得任意路径下的文件及其根目录的方法
2019/02/16 Python
Python实现的银行系统模拟程序完整案例
2019/04/12 Python
Python实现的ftp服务器功能详解【附源码下载】
2019/06/26 Python
python 函数中的内置函数及用法详解
2019/07/02 Python
python opencv对图像进行旋转且不裁剪图片的实现方法
2019/07/09 Python
基于多进程中APScheduler重复运行的解决方法
2019/07/22 Python
pytorch实现用Resnet提取特征并保存为txt文件的方法
2019/08/20 Python
使用Python生成200个激活码的实现方法
2019/11/22 Python
python数据分析:关键字提取方式
2020/02/24 Python
Python实现初始化不同的变量类型为空值
2020/06/02 Python
Sunglasses Shop德国站:欧洲排名第一的太阳镜网站
2017/08/01 全球购物
JD Sports比利时官网:英国领先的运动鞋和运动服饰零售商
2018/10/10 全球购物
小学教师的个人自我鉴定
2013/10/26 职场文书
外贸英语专业求职信范文
2013/12/25 职场文书
幼儿园元旦活动感言
2014/03/02 职场文书
《神奇的克隆》教学反思
2014/04/10 职场文书
postman中form-data、x-www-form-urlencoded、raw、binary的区别介绍
2022/01/18 HTML / CSS