详解Angularjs 自定义指令中的数据绑定


Posted in Javascript onJuly 19, 2018

有关自定义指令的scope参数,网上很多文章都在讲这3种绑定方式实现的效果是什么,但几乎没有人讲到底怎么使用,本篇希望聊聊 到底怎么用 这个话题。

一. 自定义指令

自定义指令,是 Angularjs 用来实现组件化的方式,相比于 React Vue 的组件化方式,它真的很复杂,自定义指令太重了,它暴露了太多可供定制的参数,以至于普通的开发者完全不知道要用它来做什么而将其束之高阁,毕竟一般的业务逻辑通过controller和service就已经可以完成了。

自定义指令在 Angularjs 项目中主要有两大用途:

1.封装指定组件的DOM操作

Angularjs 期望的开发方式是将DOM的操作尽可能封装在自定义指令中,这样对于局部变量的操作会更容易加入到Angular自己的生命周期中。

2.组件化

Angularjs 靠自定义指令实现组件化。诸如你在 React 和 Vue 中看到的类似于 , 这样的自定义标签,或是父级子级传值所使用的 prop ,又或者是标记组件自身状态的 state ,在 Angularjs 中全部都是通过自定义指令来实现的。

二. 数据绑定的形式

自定义指令在定义后,需要在html文件中编写,最常用的方式是将其书写为 标签属性。 当使用自定义指令时,常常需要将一个变量的值从controller传递至directive中,此时需要在 scope 属性中进行变量绑定设置, Angularjs 提供了3种不同的绑定方式(实际上也可以直接传递True),如下所示:

scope: {
  infiniteScroll: '=', // 将infiniteScroll同父级controller中的指定对象双向绑定
  onSend: '&', // 从父级获取一个变量的引用,常用作方法调用
  fromName: '@' // 从父级获取值后便只在本地作用域生效
}

关于三种绑定方式使用的方法,网上可以搜到非常多的文章,本篇不再赘述,今天我们只来详细看一下这几种方式的使用场景和区别。

2.1 @绑定

@绑定 可以转移常量赋值的位置,常用于为自定义封装组件暴露一个可设定常量参数的接口。 这种绑定方式的意义,在于从自定义指令外部(一般是从html页面上绑定一个常量或控制器中的变量)获取一个局部变量的值。

实际场景:

例如我们封装了一个分页组件,其中指令局部作用域中的 displayPaginationNums 属性用于决定分页组件的页码栏显示多少个按钮,然后把剩余的按钮收起来并添加 ... 按钮,这是一个很常见的需求。

不使用@绑定

不使用@绑定,完全可以做到,只需要在link函数里,初始化为其赋值即可。

link:function(scope, elements, attrs){
    scope.displayPaginationNums = 5;//用于决定分页导航栏最多可显示几个数字
 },

使用这样的方式,就可以,但我们默认了一个前提,那就是 所有调用这个组件的人,都会浏览这个组件的源代码 。这其实是很不方便的,换位思考一下,你使用 Angularjs 的时候,会先去源码里找一下对应的方法开头都定义了哪些变量,哪些可以修改吗?当然不会。

这个属性在不同的项目中都会需要赋值,但需要动态去修改的场景其实并不多,所以我们需要将接口暴露至更高的开发层级,供调用者直接赋值。

使用@绑定

当使用@绑定后,我们实际上是面向调用者暴露了去设定重要参数的接口,使用起来更加方便。下面的写法让开发者使用这个组件时,可以在代码编写时方便地传入自己想要设定的值:

//指令定义时
 scope:{
   displayPaginationNums:'@'
 },
<!--指令调用时-->
 <div table-pagination
    display-pagination-nums="5">

面向对象程序设计原则中有一个重要的原则,叫做 开放封闭原则 ,它的意思是说,你在程序设计中所书写的代码, 应该对扩展开放,对修改封闭 。简单地说就是你所编写的代码成型以后,在后续的使用和功能扩展的时候,尽可能不需要再去改动代码,而只需要通过 编写与扩展相关的代码 即可。

此处就是 从封闭转为开放 的一个示例,虽然看起来很细小,但可以很明确地表达这个原则。

2.2 &绑定

&绑定 用于传递父级函数的引用,用来调用父级控制器中定义的方法。 如果只是以业务逻辑为模块进行封装,这种绑定方式可以帮我们避免一部分代码重复,如果是为通用框架编写纯组件,则可以为调用者提供自定义函数的接口。

实际场景:

比如我们在制作一个表格和分页组件时,表格每一页只显示10条数据,分页是后台来完成的,那么每一次点击分页组件上的页码按钮时,我们都需要向后台发送ajax请求来获取新一页的数据。那么这个发送ajax请求的方法你会写在哪里呢?

不使用&绑定

将方法写在controller中

优势:这样做的好处是如果以后我们需要增加一个输入框来实现精确跳转到哪一页时,可以直接在模板中使用 ng-change="sendAjax( )" 来绑定这个方法,方便复用,扩展,甚至修改功能。

劣势:但这样做的话,如果想在自定义指令中就无法直接调用这个方法,常见的处理策略是在自定义指令中使用 scope.$emit( ) 将一个自定义事件发送至父级controller,在父级controller中使用 $scope.$on( ) 来监听这个自定义事件,并在回调中执行 $scope.sendAjax( ) 这个方法。

将方法写在指令的link函数中

优势:可以将一些不需要用户感知的函数封装起来,例如数据发送前的校验,或是响应数据的结构重组等,提高业务逻辑相关的代码在controller中的比重,减小controller的体积。

劣势:当其他组件想要使用这个方法时会很困难,Angularjs并没有提供一种跨directive调用方法的机制。

实际上在开发过程中,不熟悉 &绑定 的开发者在使用自定义指令时,几乎都会选择将方法写在controller中并通过消息机制来触发这个函数(也就是上文中第一个方法),他们希望指令所封装的组件是纯粹的,换句话说,它是可复用且与业务逻辑剥离的。

使用&绑定

对于业务逻辑开发而言

简洁且容易使用,组件可直接调用controller中的业务逻辑代码,避免了当自定义事件过多时造成的controller中充满了事件监听的回调方法的问题,使用方法如下:

//主模板中
  <div change-page="sendAjax"></div>
//指令定义中
  ...
  template:'<div ng-click="changePage()"></div>'
  scope:{
    changePage: '&'
  },
  ...

对于模块封装而言

从上面的示例就可以看出,自定义指令中实际执行的 changePage( ) 方法,是用户在使用这个组件时编写在controller之中的 sendAjax( ) 这个方法,当我们需要封装一个供其他开发者调用的组件时(往往是在编写一个组件库),这种结构是在angular中最自然的实现方式。

当你希望给一个自定义指令暴露越来越多个性化定制接口时,它很可能变得臃肿,甚至一无是处。

&绑定 意义,在于将业务逻辑从组件中剥离出来,但过多的 可定制性 又会给开发者带来额外的问题,你会发现,仅仅是简单地使用一个下拉框或是勾选框之类的简单组件时,就需要传入一大堆自定属性,而这本该是在 交互设计标准 中确定好并编写在项目中的指定位置的。自定义指令的可定制性越高,html模板的体积就会越大,controller中的代码量也会随之增大,带来的直接问题就是: 开发很方便,维护很痛苦。

2.3 =绑定

=绑定 是3中绑定形式中最常用的一种,常用于将用于渲染的数组或对象传入自定义指令中 。这样做可以将业务逻辑分块,使得代码结构更具有层次性,降低维护难度。

实际场景:

一个表格组件,需要通过ajax请求从后台获取100条用于展示的数据,这些数据 可能需要排序,过滤,分页 等操作,首先应该明确的是,即时这些代码全部写在controller中,程序也是可以运行的,只是当你在其他场合需要复用时,就需要 复制粘贴 很多代码。那么该如何来设计这样一个功能并提取公用组件呢? 排序 , 过滤 , 分页 都是表格组件的通用动作,也就是说与数据对象本身的结构并没有太大关系,对于一个通用型表格控件来说,我们唯一必须要传入的只有一项—— 数据源 ,且它是有可能会随着用户操作而 发生变化 的。

推荐的技术方案为:

  • service : 封装$http操作,信息提示,及容错处理
  • controller : 调用service暴露的方法从后台获取数据,并赋值给指定变量
  • directive : 双向数据绑定controller中的变量以获取驱动表格渲染的数据,将排序,过滤,分页的具体实现封装在指令内部。

这样的结构,使 宏观业务逻辑 , 前后台信息交互 , 组件通用功能 分别在不同的模块中实现,可以极大提高定位问题的速度。

=绑定 的双向数据绑定在使用中是存在一些方法问题的,详情请参考 《Angularjs1.X进阶笔记(1)—两种不同的双向数据绑定》

三. 自定义指令的实用意义

=绑定 —— 常用于传递从后台获取的用于驱动纯组件的源数据。

@绑定 —— 为自定义指令中传递可配置的常量参数提供设置接口。

&绑定 —— 为自定义指令中传递自定义方法提供接口。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript Sort 的一个错误用法示例
Mar 20 Javascript
JS实现向表格行添加新单元格的方法
Mar 30 Javascript
javascript 继承学习心得总结
Mar 17 Javascript
JS实现屏蔽网页右键复制及ctrl+c复制的方法【2种方法】
Sep 04 Javascript
js数组操作方法总结(必看篇)
Nov 22 Javascript
JS前端加密算法示例
Dec 22 Javascript
jQuery插件HighCharts绘制2D带Label的折线图效果示例【附demo源码下载】
Mar 08 Javascript
jQuery ajax读取本地json文件的实例
Oct 31 jQuery
Vue项目总结之webpack常规打包优化方案
Jun 06 Javascript
vue 实现 rem 布局或vw 布局的方法
Nov 13 Javascript
如何在微信小程序中存setStorage
Dec 13 Javascript
详解如何修改 node_modules 里的文件
May 22 Javascript
微信小程序实现天气预报功能
Jul 18 #Javascript
vue代理和跨域问题的解决
Jul 18 #Javascript
小程序自定义组件实现城市选择功能
Jul 18 #Javascript
微信小程序实践之动态控制组件的显示/隐藏功能
Jul 18 #Javascript
微信小程序项目实践之主页tab选项实现
Jul 18 #Javascript
详解性能更优越的小程序图片懒加载方式
Jul 18 #Javascript
微信小程序项目实践之验证码倒计时功能
Jul 18 #Javascript
You might like
ecshop实现smtp发送邮件
2015/02/03 PHP
Yii2分页的使用及其扩展方法详解
2016/05/23 PHP
Laravel 5.5基于内置的Auth模块实现前后台登陆详解
2017/12/21 PHP
php中关于换行的实例写法
2019/09/26 PHP
JavaScript中“基本类型”之争小结
2013/01/03 Javascript
使用原生js封装webapp滑动效果(惯性滑动、滑动回弹)
2014/05/06 Javascript
jQuery 和 CSS 的文本特效插件集锦
2014/12/12 Javascript
由ReactJS的Hello world说开来
2015/07/02 Javascript
详解Nodejs基于mongoose模块的增删改查的操作
2016/12/21 NodeJs
ajax与json 获取数据并在前台使用简单实例
2017/01/19 Javascript
如何在Angular8.0下使用ngx-translate进行国际化配置
2019/07/24 Javascript
Vue 自定义指令功能完整实例
2019/09/17 Javascript
JavaScript switch语句使用方法简介
2019/12/30 Javascript
JS实现简易日历效果
2021/01/25 Javascript
Python对两个有序列表进行合并和排序的例子
2014/06/13 Python
pandas DataFrame 根据多列的值做判断,生成新的列值实例
2018/05/18 Python
python3.x中安装web.py步骤方法
2020/06/23 Python
python3 os进行嵌套操作的实例讲解
2020/11/19 Python
一款纯css3实现简单的checkbox复选框和radio单选框
2014/11/05 HTML / CSS
CSS3 按钮边框动画的实现
2020/11/12 HTML / CSS
html5中去掉input type date默认样式的方法
2018/09/06 HTML / CSS
html5中地理位置定位api接口开发应用小结
2013/01/04 HTML / CSS
H5页面适配iPhoneX(就是那么简单)
2019/12/02 HTML / CSS
台湾网友喜爱的综合型网路购物商城:Yahoo! 奇摩购物中心
2018/03/10 全球购物
MATCHESFASHION.COM美国官网:英国奢侈品零售商
2018/10/29 全球购物
简述数组与指针的区别
2014/01/02 面试题
TCP/IP的分层模型
2013/10/27 面试题
linux面试题参考答案(8)
2015/08/11 面试题
药学专业毕业生求职信
2013/10/20 职场文书
医科大学生毕业的自我评价分享
2013/11/12 职场文书
单位在职证明范本
2014/01/09 职场文书
应届生求职信范文
2014/05/26 职场文书
超市店长竞聘书
2015/09/15 职场文书
python 多态 协议 鸭子类型详解
2021/11/27 Python
MySQL中一条SQL查询语句是如何执行的
2022/04/08 MySQL
Redis全局ID生成器的实现
2022/06/05 Redis