所以干脆自己写了一个。因为后面又说可以为空,所以就把不能为空这个注释掉了。
备注:
(1)调试时,在html中加上下面这个可以提示是否捕获到了定义的error。{{formValidator.transferCount.$error}}{{item.transferCount}}
(2)Html中引用的时候,记得中间加-。
原因:因为HTML对大小写不敏感,而JavaScript对大小写敏感。为了保证HTML和JavaScript都能按原有模式正常工作,AngularJS提出了解决方法:Directive的命名使用驼峰式语法,也就是从第二个单词开始,每个单词的首字母大写,并且不使用连接符号。在HTML代码中,使用连接符的形式。
<input type="number" ng-maxnum=100 ng-minnum=0 ng-model="subItem.waitVerifyRate" numvalue-check>
(3)因为组内指令都是在formvalidator指令基础上。所以form这里面的类名都要,而且必须是在form中。。
<form class="formform-horizontal form-group"name="formValidator"form-Validator=""novalidate>
novalidate属性是不走h5验证。
(4)其中错误提示,依赖了组内封装的validator服务(formvalidator.js)中定义的validator.showError、validator.removeError、validator.getErrorMessage方法。这3个方法,是统一定制了错误提示的提示形式,并加入了自定义错误提示语句。
(5)而$setValidity(validationErrorKey,isValid)用于改变验证状态,以及在控制变化的验证标准时通知。 validationErrorKey是不通过原因,后面true表示通过,false表示不通过。这个方法应该由一个验证器来调用。例如,一个解析器或者格式化函数。
(6)对输入框的校验:
除了bind blur事件,然后destroy,用下面的scope.watch也可以。还有$parsers主动识别也可以。
1.可以用elem.bind,给元素绑定blur事件,并设置blur事件的作用域,ctrl.$viewValue就是当前输入的值。最后用scope.$on('$destroy',解除blur事件的绑定。2.也可以用scope.$watch,通过newValue, oldvalue的对比,和newValue的判断,来进行验证。
3.formSubmit和formRepeat的实现用了$parse。作用:将一个AngularJS表达式转换成一个函数.
angular.module('adminApp')
.directive('numvalueCheck', ['common', 'validator', function (Common, validator) {
return {
restrict: "A",
replace: true,
require: "?ngModel",
link: function (scope, elem, attrs, ctrl) {
var max = scope.$eval(attrs.ngMaxnum);
var min = scope.$eval(attrs.ngMinnum);
elem.bind("blur.numvalueCheck", function () {
// if (Common.isEmpty(ctrl.$viewValue)) {
// ctrl.$setValidity('required', false);
// validator.showError(elem, validator.getErrorMessages(elem, ctrl.$error));
// return;
// } else {
// ctrl.$setValidity('required', true);
// validator.removeError(elem);
// };
if (ctrl.$viewValue > max || ctrl.$viewValue < min) {
ctrl.$setValidity("numvalueCheck", false);
validator.showError(elem, validator.getErrorMessages(elem, ctrl.$error));
return;
} else {
ctrl.$setValidity("numvalueCheck", true);
validator.removeError(elem);
}
});
scope.$on('$destroy', function () {
elem.unbind("blur.numvalueCheck");
});
// scope.$watch(attrs.ngModel, function (newValue, oldvalue) {
// console.log("=====", newValue, oldvalue);
// if (!Common.isEmpty(newValue) && (newValue > max || newValue < min)) {
// ctrl.$setValidity("numvalueCheck", false);
// validator.showError(elem, validator.getErrorMessages(elem, ctrl.$error));
// return;
// }
// else {
// ctrl.$setValidity("numvalueCheck", true);
// validator.removeError(elem);
// }
// }, true);
}
}
}]);
指令常用的几个属性总结:
1. restrict [string]-规定指令使用形式
主要是用来规定指令在HTML代码中可以使用什么表现形式。实际情况中我们一般使用AE这两种方式。
'E': 表示元素,例如<hello></hello>
'A':表示属性,例如<div hello=""></div>
'C'表示类名,样式,例如<divclass=hello:""></div>
'M'表示注释,<!-- directive: hello-->
例子:
1. <html ng-app="testModule">
2. <head>
3. <meta charset="utf-8">
4. <title>My HTML File</title>
5. <link rel="stylesheet" href="css/app.css">
6. <link rel="stylesheet" href="css/bootstrap.css">
7. <script src="lib/angular/angular.js"></script>
8. </head>
9. <body ng-controller="testController">
10. <input type="text" ng-model="person">
11. <hello person="person">
12. <div>
13. {{person}}
14. </div>
15. </hello>
16. <p>{{a}}</p>
17. <script>
18. var testModule=angular.module('testModule',[]);
19. testModule.controller('testController',function($scope){
20. $scope.person="Tony";
21. });
22. testModule.directive('hello',function(){
23. return{
24. restrict: 'E',
25. transclude: true,
26. scope: {
27. 'personName': '=person'
28. },
29. template:'<div ng-transclude></div><input type="button" value="click" ng-click="toggle()"><div ng-show="isShow">{{personName}} say hello to us</div>',
30. link:function(scope,element,attrs){
31. scope.toggle=function toggle(){
32. scope.isShow=!scope.isShow;
33. }
34. }
35. };
36. });
37. </script>
38. </body>
39. </html>
2. template与templateUrl-引入新的html代码或文件。
template
d放置新的html文本,也可以为文件路径。template[string or function]这个属性,规定了指令被Angular编译和链接(link)后生成的HTML标记,这个属性可以简单到只有一个HTML文本在里面,也可以特别复杂,当该属性的值为function的时候,那么该方法返回的就是代表模板的字符串,同时也可以在里面使用{{}}这个表达式。
templateUrl获取模板
(1)templateUrl:
"part.html"
有些时候,Directive中的模板template
会变得很大,如果仍然放置在定义中,那么可能会造成阅读和修改不方便的情况。
针对这种情况,我们可以将template
替换为templateUrl
,通过引入外部文件的形式来调用布局。
varApp = angular.module(
"App", []);
App.directive(
"formDirective",
function (){
return
{
restrict:
"A",
scope: {
},
templateUrl:
"part.html"
}
});
App.controller(
"FirstCtrl",
function ($scope){
});
<!DOCTYPE html>
<htmllang="zh"ng-app="App">
<head>
<metacharset="UTF-8">
<title>
{{"
学习
AngularJS 1.x"}}
</title>
<linkhref="css/style.css"rel="stylesheet">
</head>
<body>
<divng-controller="FirstCtrl">
<divform-directive></div>
</div>
<scripttype="text/javascript"src="components/angular/angular.js"></script>
<scripttype="text/javascript"src="js/app.js"></script>
</body>
</html>
同时,我们还要加入一个新的html文档,命名为part.html:
<h1>part.html
</h1>
<p>这里是
part.html
中的内容
</p>
(
2
)
ng-template
除了直接将HTML部件存储为独立的文件,我们也可以直接使用AngularJS提供的ng-template功能。这点在第四章中也有提到,这里是一个新的示例,帮助您对比物理文件和ng-template
文件的优先级。
<scripttype="text/ng-template"id="part.html">
<h1>通过ng-template封装的part.html</h1>
<p>这里是part.html中的内容</p>
</script>
要使用这个功能,我们需要在<script>
标签中,首先声明type="text/ng-template"
,并给这段代码进行命名,示例代码中的id="part.html"
即是这部分HTML代码片段的命名。使用时,直接调用part.html
即可,不需要额外存储一个part.html文件。
这样进行封装,与函数封装调用的概念类似。当默认HTML进行展示时,是不会显示这段代码的。而通过id
调用后,又可以直接展示出来。
注意:通过示例我们可以看到,文档中的part.html
的优先级高于独立的HTML文件。
(3)使用函数获取templateUrl
templateUrl的特性与我们在第四章学习的ng-include类似,也支持通过函数来获取最终的url地址。
App.directive(
"formDirective",
function (){
return
{
restrict:
"A",
scope: {},
templateUrl:
function (){
return"part.html"
;
}
}
});
其中,配置路由的时候,用template会走缓存。用template,通过字符串形式引入文件,不走缓存。
.state('order', {
url:"/order?rnd",
templateUrl:"views/order/order.html?rnd=",
controller:'orderCtrl'
})
.state('pages-exception-detail', {
url:'/pages-exception-detail?id',
template:require('./detail/detail.html'),
controller:'app.pages.exceptionDetailCtrl',
permission:'pages-exception-detail'
})
所以上次那个bug: 一个有权限的人登录成功退出后,无权限的人登录后,本该跳到无权限页,却跳到订单页。后面对403做了判断,重定向到无权限页后,再进行同样的操作,却先跳到订单页,再跳到无权限页。
本地有index的缓存,就不向后端请求index页面了,他就没办法直接拦截请求重定向了。而前端也会走订单页发ajax请求这个流程了,这样就是ajax请求进行的403后的重定向,所以会闪屏。
后面解决办法是给login的回调参数的index.html链接,加了时间戳。
index.html文件是入口文件,不让走缓存的,可以通过meta设置,或者后端设置http头信息。
index.html页是html写的,order.html页是js写的,给index头部加不缓存,order页却有缓存,因为order页是用templateUrl引入的,会去查缓存。order页加rnd,是在路由点击的时候,页面上信息会刷新。并不是解决缓存问题。所以要用template就好了。
3. replace[boolean]属性-是否替换定义此指令的HTML元素
这个属性用来规定template生成的HTML内容,是否会替换掉定义此指令的HTML元素。当我们把该属性的值设为true的时候,打开控制台发现这个指令生成的元素, 如果template中的div有name属性,包含这个标签的div也有name属性,name会有变化。
可以让AngularJS用替换的模式应用Directive。具体的效果,是会去除掉<people> </people>
这对标签。
App.directive(
"people",
function(){
return
{
restrict :
"E",
replace :
true,
template :
"<p>姓名:{{data.name}}</p><p>性别:{{data.sex}}</p>"
}
});
4. require[string or string[]]参数-声明对指令controller实例的引用
声明对controller实例的引用。让父子指令或者兄弟指令的controller之间搭建一个桥梁。也就是说父指令里的controller中的数据能分享给子指令的controller。其中link第四个参数的值是父指令的controller对象的作用域上下文。
require有两个修饰符号:”?”、”^”
? : 如果require没有找到相应的指令避免报错,还能确保程序的正常执行
^ : 表示往父级查找
Require: 字符串前可加?、^、 或不加
例子:
require: 'ngModel',表示必须有ngModel
require: '?ngModel',表示可以没有ngModel,依赖变为可选项
require:'^he',表示使用he指令的controller
require - 请求另外的controller,传入当前directive的link function中。require需要传入一个directive controller的名称。如果找不到这个名称对应的controller,那么将会抛出一个error。名称可以加入以下前缀:
? - 不要抛出异常。这使这个依赖变为一个可选项。
^ - 允许查找父元素的controller
5. link[function]属性-操作dom的函数
link函数主要是用来添加对DOM元素的事件监听、监视模型属性变化、以及更新DOM的。link函数的执行时机为angular编译此模板之后。它里面4个参数:link:function($scope,elm,attr,controller){}。
(1)scope参数,当前directive的作用域,是否独立由scope参数决定。在我们没有为指令定义scope属性的时候,那么他代表的就是父controller的scope。
(2)element参数,指令所在的元素。就是指令的jQLite(jQuery的子集)包装DOM元素。如果你在引入AngularJS之前引入了jQuery,那么这个元素就是jQuery元素,而不是jQLite元素。由于这个元素已经被jQuery/jQLite包装了,所以我们就在进行DOM操作的时候就不需要再使用 $()来进行包装。
element与jQuery的关系:
1)在Directive中,我们不免需要对页面元素进行操作。为了提供这项功能,AngularJS几乎原版搬运了jQuery操作元素的功能,他们称之为"jQuery Lite"(jqLite)。AngularJS通过jqLite的调用方法angular.element
,实现了jQuery中的大部分常用功能。也就是,我们可以在获取了element
参数后,调用jQuery常用的语法,如bind()
,addClass()
,removeClass()
等来直接对元素进行操作,实现我们期望的功能。
2)同时,如果你希望在AngularJS中直接使用完整的jQuery也是非常容易的。只需要安装jQuery,并在index.html
中,保证在angular.js
引入之前引入jQuery.js
。AngularJS会自动将angular.element
绑定到jQuery上。也就是,在Directive中,会自动使用jQuery来解析界面元素,我们获取的element
会自动变为jQuery对象。
实际体验element
的功能:
我们在引入jQuery后,可以通过如下代码查看效果:
这里需要注意的是,使用jqLite的方法和以下代码中使用的方法是不一样的,因为jqLite不支持通过标签方式获取子元素。
App.directive(
"formDirective",
function (){
return
{
restrict:
"A",
template:
"<h1>标题</h1><p>这里是段落文字</p>",
link:
function(scope, element, attrs){
element.children(
"h1").addClass(
"strike");
}
}
});
<divng-controller="FirstCtrl">
<divform-directive></div>
</div>
在element
上绑定鼠标移入移出时的变化效果
App.directive(
"formDirective",
function (){
return
{
restrict:
"A",
template:
"<h1>标题</h1><p>这里是段落文字</p>",
link:
function(scope, element, attrs){
element.children(
"h1").bind(
"mouseenter",
function(){
element.children(
"h1").addClass(
"strike");
element.children(
"h1").text(
"鼠标移过来了");
});
element.children(
"h1").bind(
"mouseleave",
function(){
element.children(
"h1").removeClass(
"strike");
element.children(
"h1").text(
"鼠标移开了");
})
}
}
});
运行时,当鼠标移动到标题上,则标题文字会变化成"鼠标移过来了",并加上删除线效果;当鼠标移开,则文字会变为"鼠标移开了"。
(3)attrs参数,directive对应元素的属性,它包含了该指令所在元素的属性的标准化参数对象。
举例的话
<demo attrribute='some attrribute ></demo>
中attrs. attrribute就是'some attrribute是写死的,如果想绑定的话必须独立作用域。
(4)controller:require里面require的那个参数。
提供require进来的directive所提供的方法,如果require了多个,controller将会是一个数组。或者是require参数中的ngModel。
6. scope[boolean or object]属性-传入数据
该属性是用来定义指令的scope的范围,默认情况下是false,也就是说该指令继承了父controller的scope,可以随意的使用父controller的scope里的属性,但是这样的话就会污染到父scope里的属性,这样是不可取的。所以我们可以让scope取以下两个值:true和{}。
当为true的时候,表示让Angular给指令创建一个继承于父scope的scope。
scope:{
name1:'=name1',
name2:'@name2',
name3:'&name3'
}
'=name1':,name1为取标签中的属性名为name1的值对于$scope;若name1为空,即‘=’,则取同名属性的值的$scope,且实现了双向绑定。
'@name2':取标签中name2属性值,若<helloname2="nname22"></hello>,则取的值为nname22。
'&name3':name3为controller中的$scope同名方法。
(1)用法
在控制器ng-controller的使用过程中,我们使用了$scope功能。$scope用于提供对接HTML和JavaScript对应模块的功能。
而Directive在默认情况下,是没有自动绑定一个$scope的。也就是说,在默认情况下,Directive无法在JavaScript中接收传入的数据(因为缺少一个存储信息的载体),形成我们期望的效果。但是,Directive提供了非常简单的定义一个scope的功能:
App.directive(
"people",
function(){
return
{
restrict:
"A",
scope:{
info:
"="
},
template :
"<p>姓名:{{info.name}}</p><p>性别:{{info.sex}}</p>"
}
});
App.controller(
"FirstCtrl",
function ($scope){
$scope.harry = {
name:
"Harry",
sex :
"男"
};
});
注意,这里我将restrict从"E"(element元素)改变成为了"A"(attribute属性),这样它的使用方法有了一些变化:
<divng-controller="FirstCtrl">
<divpeopleinfo="harry"></div>
</div>
在HTML代码里,我们为div元素配置了一个people的属性和一个info属性;并将FirstCtrl的$scope.harry传入给了info。
(2) scope中的配置:
可以看到,在上方的JavaSciprt文件中,我们对scope的定义使用了如下结构:
scope:{
info:
"="
}
首先,scope:{}
是告诉这个Directive它需要自己存储信息(类似于建立一个基于这个Directive的$scope
)。
info: "="
这段配置,告诉Directive从HTML标签中,获取名为info
的属性,并将它的值存储在scope.info
中。这样,我们就达到了存储数据的效果。
(3)在一个ng-ontroller中放入多个相同的Directive:
下面,我们在FirstCtrl
中增加几个人的数据,并将它们通过Directive显示出来:
//在FirstCtrl中加入如下代码
$scope.anotherPerson = {
name :
"张三",
sex :
"男"
};
App.directive(
"people",
function(){
return
{
restrict:
"A",
scope:{
info:
"="
},
template :
"<p>姓名:{{info.name}}</p><p>性别:{{info.sex}}</p>"
}
});
<divng-controller="FirstCtrl">
<divpeopleinfo="harry"></div>
<divpeopleinfo="anotherPerson"></div>
</div>
(4)通过ng-repeat和directive一起显示数据
知道了如何传入数据,那么我们就可以将Directive的使用和ng-repeat
结合起来,实现列表显示数据的效果。
我们先将FirstCtrl
的数据变化为一个array
:
App.controller(
"FirstCtrl",
function ($scope){
$scope.
peopleList= [
{
name:
"Harry",
sex:
"男"
},
{
name:
"张三",
sex:
"男"
}
];
});
App.directive(
"people",
function(){
return
{
restrict:
"A",
scope:{
info:
"="
},
template :
"<p>姓名:{{info.name}}</p><p>性别:{{info.sex}}</p>"
}
});
<divng-controller="FirstCtrl">
<spanng-repeat="person in peopleList">
<divpeopleinfo="person"></div>
</span>
</div>
(5)在Directive中修改控制器中的数据
方法:将template
中原先显示的数据的部分,替换为input
即可
App.directive(
"people",
function (){
return
{
restrict:
"A",
scope: {
info:
"="
},
template:
"<input type='text' ng-model='info.name'><p>性别:{{info.sex}}</p>"
}
});
<divng-controller="FirstCtrl">
{{ peopleList | json}} <!--
显示一下其中的数据变化
-->
<spanng-repeat="person in peopleList">
<divpeopleinfo="person"></div>
</span>
</div>
(6) 以只读的方式传入数据
除了以等号=
直接传入对象之外,Directive也支持直接传入字符串文本,使用@
符号。
App.directive(
"people",
function (){
return
{
restrict:
"A",
scope: {
name:
"@",
sex :
"@"
},
template:
"<input type='text' ng-model='name'><p>性别:{{sex}}</p>"
}
});
<divng-controller="FirstCtrl">
{{ peopleList | json}}
<spanng-repeat="person in peopleList">
<!-- 注意此处的数据传入方法 -->
<divpeoplename="{{person.name}}"sex="{{person.sex}}"></div>
</span>
</div>
可以看到,我们在Directive中传入的数据进行的数据修改,并未反馈到FirstCtrl中。所以是只读的方式。
(7)在Directive中进行函数回调
上面我们介绍了等号=
和@
符号的使用方法,它们分别对应传入对象和文本。但是,如果我们期望传入一个回调函数呢?这样我们就可以实现如封装一个按钮为一个Directive,然后让它在点击后实现我们期望的功能的效果。
这就需要使用到&
符号,下面我们来看看实际的例子:
varApp = angular.module(
"App", []);
App.directive(
"formDirective",
function (){
return
{
restrict:
"A",
scope: {
//这里使用&符号来接受传入的函数
btnClick:
"&"
//注意:这里没有加入下方的value模型
},
template:
//一个用于输入文字的输入框,绑定到value上
"<input type='text' ng-model='value'><br>"
+
//提交的按钮,绑定上方scope的btnClick方法
//注意传入参数的方式和HTML中具体使用的方式
"<input type='button' value='提交' ng-click='btnClick({message:value})'>"
}
});
App.controller(
"FirstCtrl",
function ($scope){
$scope.clickBtnCallback =
function (msg){
alert(
"点击了按钮!信息是:"+ msg);
}
});
对应的HTML代码:
<!DOCTYPE html>
<htmllang="zh"ng-app="App">
<head>
<metacharset="UTF-8">
<title>
{{"
学习
AngularJS 1.x"}}
</title>
<linktype="text/css"rel="stylesheet"href="css/style.css">
</head>
<body>
<divng-controller="FirstCtrl">
<!-- 注意这里绑定btn-click/btnClick中传入的参数的命名 -->
<divform-directivebtn-click="clickBtnCallback(message)"></div>
</div>
<scripttype="text/javascript"src="components/angular/angular.js"></script>
<scripttype="text/javascript"src="js/app.js"></script>
</body>
</html>
7. controller[string or function]-指令之间交互
当我们想要允许其他的指令和你的指令发生交互时,我们就需要使用 controller 函数。当另一个指令想要交互时,它需要声明它对你的指令 controller 实例的引用(require)。
例如:
.directive('hello', function() {
return{
scope:{},
require:'^he',
compile: function (element,attrs){
return function (scope,elements,attrs,cntIns){
cntIns.fn()
};
}
}
})
.directive('he', function () {
return{
restrict:'AE',
scope:{},
controller: function ($scope,$compile, $http) {
this.fn= function () {
alert('hello');
};
}
}
})
<he>
<hello color="color"sayhello="sayhello()"></hello>
</he>
8. transclude[boolean]属性-把指令变为一个容器
这个属性用来让我们规定指令是否可以包含任意内容.
在前面我们使用到的Directive,都会将包含有Directive的元素整体替换为template
中的内容。这样,就让Directive的用途缩减为只能封装最低级别的元素。
但是我们使用的ng-app
,ng-controller
等,也同样都是Directive,而我们可以在这些元素中,直接填入HTML代码。这是如何实现的呢?这就要应用到Directive的transclude
属性。
App.directive(
"formDirective",
function (){
return
{
restrict:
"A",
//通过transclude标签将Directive变为一个容器
transclude:
true,
//注意template中的ng-transclude,这里是放置原有代码的地方。
template:
"<h1>标题</h1><p>这里是段落文字</p><div ng-transclude></div>"
}
});
<divng-controller="FirstCtrl">
<divform-directive>
<p>
这段文字是放置在
Directive
中间的。
</p>
</div>
</div>
9. priority[number]属性-规定自定义的指令的优先级
这个属性是来规定自定义的指令的优先级的,当一个DOM元素上面有一个以上的指令的时候,就需要去比较指令的优先级了,优先级高的指令先执行。这个优先级就是用来在执行指令的compile函数前,先排序的。
10. terminal[boolean]属性-否停止当前元素上比本指令优先级低的指令
该参数用来定义是否停止当前元素上比本指令优先级低的指令,如果值为true,就是正常情况,按照优先级高低的顺序来执行,如果设置为false,就不会执行当前元素上比本指令优先级低的指令。
11. compile[function]参数-改变原始的dom,在ng创建原始dom实例以及创建scope实例之前使用
该方法有两个参数element,attrs,第一个参数element指指令所在的元素,第二个attrs指元素上赋予的参数的标准化列表。这里我们也有个地方需要注意:compile 函数不能访问 scope,并且必须返回一个 link 函数。但是如果没有设置 compile 函数,你可以正常地配置 link 函数,(有了compile,就不能用link,link函数由compile返回)。
ng-repeat就是一个最好的例子,它就在是compile函数阶段改变原始的dom生成多个原始dom节点,然后每个又生成element实例.