AngularJS 入门3

AngularJS   入门3


七. 使用指令封装DOM操作

1. 创建指令

指令也是一种服务,只是这种服务的定义有几个特殊要求:

  1. 必须使用模块的directive()方法注册服务
  2. 必须以对象工厂/factory()方法定义服务实现
  3. 对象工厂必须返回一个指令定义对象
  
  
  1. //定义指令的类工厂
  2. var directiveFactory = functioninjectables){
  3. //指令定义对象
  4. var directiveDefinationObject = {
  5. ...
  6. };
  7. return directiveDefinationObject;
  8. };
  9. //在模块上注册指令
  10. angular.module"someModule",[])
  11. .directive"directiveName"directiveFactory);

INSIDE:指令在注入器中的登记名称是:指令名+Directive。 例如,ng-app指令的服务名称是:"ngAppDirective"。


javascript 实例:

var ezHoverableFactory = function(){
return {
restrict : "A",
link : function(scope,element,attrs){
element.on("mouseover",function(){
element.css({outline:"#ff0000 dotted thick"});
})
.on("mouseout",function(){
element.css({outline:"none"});
})
}
};
};
angular.module("ezstuff",[])
.directive("ezHoverable",ezHoverableFactory);


2. 指令对象

每个指令定义的工厂函数,需要返回一个指令定义对象。指令定义对象就是 一个具有约定属性的javascript对象,编译器/$compile在编译时就根据这 个定义对象对指令进行展开。

指令定义对象的常用属性如下:

  • template : string

使用template指定的HTML标记替换指令内容(或指令自身)

  • restrict : string

用来限定指令在HTML模板中出现的位置。

  • replace : true|false

使用这个属性指明template的替换方式。

  • scope : true|false|{...}

scope属性为指令创建私有的作用域,这在创建可复用的Widget时非常有用。

  • link : function(..){...}

link属性是一个函数,用来在指令中操作DOM树、实现数据绑定。

  • transclude : true|false|'element'

1). template:定义替换模板

template指明一个HTML片段,可以用来:

  • 替换指令的内容。这是默认的行为,可以使用replace属性更改。
  • 如果replace = true,那么用HTML片段替换指令本身。
  • 包裹指令的内容,如果transclue属性为true。

javascript 实例代码:

angular.module("ezstuff",[])
.controller("ezCtrl", ["$scope", function($scope) {
  $scope.customer = {
    name: "Naomi",
    address: "1600 Amphitheatre"
  };
}])
.directive("ezCustomer", function() {
  return {
    template: "Name: {{customer.name}} Address: {{customer.address}}"
  };
});


2). restrict:限制指令的出现位置

restict属性可以是EACM这四个字母的任意组合,用来限定指令的应用场景。 如果不指定这个属性,默认情况下,指令将仅允许被用作元素名属性名

  • E - 指令可以作为HTML元素使用
  • A - 指令可以作为HTML属性使用
  • C - 指令可以作为CSS类使用
  • M - 指令可以在HTML注释中使用

我们对之前的示例,增加一个restrict属性,限制这个只能作为元素名使用。 代码已经预置到右边,你可以看到,现在唯一合法的方式是使用如下方式应用指令:

  
  
  1. <ez-customer></ez-customer>

javascript 实例代码:

angular.module("ezstuff",[])
.controller("ezCtrl", ["$scope", function($scope) {
  $scope.customer = {
    name: "Naomi",
    address: "1600 Amphitheatre"
  };
}])
.directive("ezCustomer", function() {
  return {
 restrict:"E",
 template: "Name: {{customer.name}} Address: {{customer.address}}"
  };
});


3). replace:模板的使用方式

我们希望使用template完整地替换原始的DOM对象,而不是填充其内容,replace 属性负责这件事。

replace属性指明使用template时,如何替换指令元素:

  • true - 编译时,将使用template替换指令元素
  • false - 编译时,将使用template替换指令元素的内容
你可能注意到模板的内容稍微修改了一下,这是因为replace为true时,要求模板有 一个根节点


4). 作用域问题

默认情况下,指令没有自己的scope对象,换句话说,它使用所在DOM对象对应的scope对象。

那么问题来了,如果一个指令在同一个scope内出现多次,会怎样?

  
  
  1. <div ng-controller="ezCtrl">
  2. <ez-customer></ez-customer>
  3. <ez-customer></ez-customer>
  4. </div>

没错,由于两个ez-customer指令都处在ezCtrl开辟的作用域内,所以两个指令绑定到了同样的 数据模型上,得到的是重复的结果。

显然,我们可以将每个ez-customer指令置于不同的作用域下,这意味着我们给每个ez-customer 一个不同的控制器:

  
  
  1. <div ng-controller="ezCtrl1">
  2. <ez-customer></ez-customer>
  3. </div>
  4. <div ng-controller="ezCtrl2">
  5. <ez-customer></ez-customer>
  6. </div>

5). scope:使用隔离的作用域

通过设置scope属性,指令的每个实例都将获得一个隔离的本地作用域

  
  
  1. var ezCustomerDirectiveFactory = function(){
  2. return {
  3. restrict:"E"
  4. replace:true
  5. scope:{
  6. name : "@name"
  7. address : "=address"
  8. },
  9. template:"<div>name:{{name}} address:{{address}}</div>"
  10. }
  11. }

在上面的例子中,我们在本地scope上定义了两个属性:name和address,这样在 模板中就可以使用name和address了。

你应该已经注意到,name属性的值之前有一个@符号,这是一个约定好的标记,它 告诉编译器,本地scope上的name值需要从应用这个指令的DOM元素的name属性值 读取,如果DOM元素的name属性值变了,那么本地scope上的name值也会变化。

同样,address属性之前的=符号也是一个约定好的标记,它告诉编译器,本地scope 上的address属性值和DOM元素的address属性值指定的外部scope对象上的模型需要建立双向连接:外部scope上模型的变化会改变本地scope上的address属性,本地 scope上address属性的变化也会改变外部scope上模型的变化。

有点绕,上个图:



从图中可以看出:

  1. 指令的template绑定的是本地scope上的name和address。
  2. 本地scope的name属性的值始终是ez-customer对象上name属性的值
  3. 本地scope的address属性值始终和ez-customer对应的scope对象上的Emmy.address 保持同步。


javascript 实例代码:

angular.module("ezstuff",[])
.controller("ezCtrl", ["$scope", function($scope) {
  $scope.Emmy = {
    name: "Emmy",
    address: "1600 Amphitheatre"
  };
  $scope.Edison = {
    name: "Edison",
    address: "2500 Amphitheatre"
  };
}])
.directive("ezCustomer", function() {
  return {
 restrict:"E",
 replace:true,
 scope:{
customer:"=sb",
 },
 template: "<div>Name: {{customer.name}} Address: {{customer.address}}</div>"
  };
});


HTML 代码:

<html ng-app="ezstuff">
<head>
<script src="angular.min.js"></script>
</head>
<body>
<div ng-controller="ezCtrl">
<ez-customer sb="Emmy"></ez-customer>
<ez-customer sb="Edison"></ez-customer>
</div>
</body>
</html>



6). link:在指令中操作DOM

如果需要在指令中操作DOM,我们需要在对象中定义link属性,link函数的定义如下:

  
  
  1. function linkscope iElement iAttrs controller transcludeFn { ... }

注意link函数的参数,AngularJS在编译时负责传入正确的值:

  • scope

指令对应的scope对象。如果指令没有定义自己的本地作用域,那么传入的就是外部的 作用域对象。

  • iElement

指令所在DOM对象的jqLite封装。如果使用了template属性,那么iElement对应 变换后的DOM对象的jqLite封装。

  • iAttrs

指令所在DOM对象的属性集。这是一个Hash对象,每个键是驼峰规范化后 的属性名。

后两个参数我们先略过。

示例

在下方的示例中,我们实现了一个可以指定显示格式的小时钟指令:ezCurrentTime。和原来一样, 我们在link函数中启动定时器,并在定时器中更新DOM。有几点解释下:

  1. 我们在scope上使用$watch()方法对format的值进行监听,并使用这个值调整显示格式
  2. 我们监听element的$destroy事件,这个事件是在DOM对象销毁时触发。我们在这个事件触发时 销毁定时器以释放资源
  3. 我们使用了AngularJS内置的$interval服务,而不是setInterval()函数创建定时器。
  4. 我们使用了AngularJS内置的dateFilter过滤器服务,对时间的显示进行格式化。 和$interval一样,dateFilter服务也是通过注入器注入的。

javascript 实例代码

angular.module("ezstuff", [])
.controller("ezCtrl", ["$scope", function($scope) {
$scope.format = "M/d/yy h:mm:ss a";
}])
.directive("ezCurrentTime", ["$interval", "dateFilter", function($interval, dateFilter) {
//定义link函数
function link(scope, element, attrs) {
var format,
timeoutId;

//更新DOM内容
function updateTime() {
element.text(dateFilter(new Date(), format));
}

//监听时钟格式
scope.$watch(attrs.ezCurrentTime, function(value) {
format = value;
updateTime();
});

//在DOM对象销毁时注销定时器
element.on("$destroy", function() {
$interval.cancel(timeoutId);
});

//启动定时器
timeoutId = $interval(function() {
updateTime(); //update DOM
}, 1000);
};

//返回指令定义对象
return {
link: link
};
}]);


HTML 代码:

<html ng-app="ezstuff">
<head>
<script src="angular.min.js"></script>
</head>
<body>
<div ng-controller="ezCtrl">
  Date format: <input ng-model="format"> <hr/>
  Current time is: <span ez-current-time="format"></span>
</div>
</body>
</html>


7). transclude: 包含其他元素

有些指令需要能够包含其他未知的元素。比如我们定义一个指令ez-dialog,用来 封装对话框的样式和行为,它应当允许在使用期(也就是在界面模板文件里)才指 定其内容:

  
  
  1. <ez-dialog>
  2. <p>对话框的内容在我们开发ez-dialog指令的时候是无法预计的。这部分内容需要
  3. 被转移到展开的DOM树中适当的位置。</p>
  4. </ez-dialog>

transclude属性可以告诉编译器,利用所在DOM元素的内容,替换template中包含 ng-transclude指令的元素的内容:



从上图中可以看到,使用transclude有两个要点:

  1. 需要首先声明transclude属性值为true,这将告诉编译器,使用我们这个指令的 DOM元素,其内容需要被复制插入到编译后的DOM树的某个点。
  2. 需要在template属性值中使用ng-transclude指明插入点

右边嵌入了ez-dialog的实现实例。


javascript代码实例:

angular.module("ezstuff", [])
.controller("ezCtrl", ["$scope", function($scope) {
$scope.name = "Mr. Whoami";
}])
.directive("ezDialog", function() {
return {
restrict: "E",
replace : true,
transclude: true,
template: "<div class='ez-dialog'><div class='header'>alert</div><div class='content' ng-transclude></div></div>"
};
});


HTML 代码实例:

<html ng-app="ezstuff">
<head>
<script src="angular.min.js"></script>
</head>
<body>
<div ng-controller="ezCtrl">
<ez-dialog>
<p>Check out the contents, {{name}}!</p>
<p><button>Ok,I will</button></p> </ez-dialog>
</div>
</body>
</html>


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值