AngularJS1.x入门·常用指令以及自定义指令

时间:2021-08-04 10:29:43

==AngularJS入门系列博客仅作入门快速开发参考,深层次的内容有待补充==

常用指令

  1. ng-change:元素值改变的时候出发监听事件
<!-- 动态改变数量,计算总价 -->
<body ng-app="main">
    <div ng-controller="indexController">
        单价10,数量<input ng-model="num" ng-change="changeHello()" />,总价<input ng-model="total" />
    </div>
    <script type="text/javascript" src="../js/angular-1.6.4/angular.min.js"></script>
    <script type="text/javascript"> var app = angular.module("main", []); app.controller("indexController", ['$scope', function($scope) { $scope.total=$scope.num*10; $scope.num=1; $scope.changeHello = function() { $scope.total=10*$scope.num }; }]); </script>
</body>
  1. ng-class:根据变量布尔值动态显示class
<!-- CSS -->
.red{color:#DD1144;}
.blue{color:#0000FF}

ng-class里面的表达式:当模型中 showRed为true时显示 .red样式,当showBlue为true时,显示 .blue样式。点击按钮,触发changeColor函数,true变false,false变true,实现动态切换css样式。

<body ng-app="main">
    <div ng-controller="indexController">
        <p ng-class="{'red':showRed,'blue':showBlue}">可以变色的字体</p>
        <button class="btn btn-primary" ng-click="changeColor()">改变颜色</button>
    </div>
    <script type="text/javascript" src="../js/angular-1.6.4/angular.min.js"></script>
    <script type="text/javascript"> var app = angular.module("main", []); app.controller("indexController", ['$scope', function($scope) { $scope.showRed=true; $scope.showBlue=false; $scope.changeColor=function(){ $scope.showRed=!$scope.showRed; $scope.showBlue=!$scope.showBlue; }; }]); </script>
</body>
  1. ng-controller:指定controller的作用范围
  2. ng-dblclick:双击事件,用法同ng-click。
  3. ng-hide/ng-show:动态显示或隐藏
    以上例子可改为动态显示不同颜色的字体
<!-- 其他的代码同上面的例子 -->
<div ng-controller="indexController">
    <p class="red" ng-show="showRed">可以变色的字体</p>
    <p class="blue" ng-hide="showRed">可以变色的字体</p>
    <button class="btn btn-primary" ng-click="changeColor()">改变颜色</button>
</div>
  1. ng-keypress:监听按键事件。例如监听input搜索框中按下回车键开始关键字搜索
<!-- 将关键词属性绑定在模型中,判断如果是回车键被按下时,则根据关键字搜索。 -->
<body ng-app="main">
    <div ng-controller="indexController">
        <input type="text" ng-model="keyword" ng-keypress="search($event)" />
    </div>
    <script type="text/javascript" src="../js/angular-1.6.4/angular.min.js"></script>
    <script type="text/javascript"> var app = angular.module("main", []); app.controller("indexController", ['$scope', function($scope) { $scope.keyword=""; $scope.search=function($event){ if(13==$event.keyCode){ alert("正在根据 "+$scope.keyword+" 开始搜索 ... "); } }; }]); </script>
</body>
  1. ng-repeat:遍历。
<!-- 遍历书的集合显示为ul列表 -->
<body ng-app="main">
    <div ng-controller="indexController">
        <ul>
            <li ng-repeat="item in books">
                {{item}}                    
            </li>
        </ul>
    </div>
    <script type="text/javascript" src="../js/angular-1.6.4/angular.min.js"></script>
    <script type="text/javascript"> var app = angular.module("main", []); app.controller("indexController", ['$scope', function($scope) { $scope.books=['Java','Javascript','python','Mysql']; }]); </script>
</body>

自定义指令

首先看几个个最简单的指令例子:

示例1:固定的模板内容显示
<body ng-app="main">
    <div ng-controller="indexController">
        <span hello>该文本被替换</span>  <br />
        <hello>该文本被替换</hello>       <br />
        <span class="hello">该文本被替换</span><br />
        <!-- directive:hello -->
    </div>
    <script type="text/javascript" src="../js/angular-1.6.4/angular.min.js"></script>
    <script type="text/javascript"> var app = angular.module("main", []); app.directive('hello',function(){ return { restrict:'ACEM', template:'<span>Hello Angular ... </span>', replace:true, transclude:false }; }); </script>
</body>

解析:
1. 使用 directive 函数创建一个新的指令,名为hello。注意如果创建的指令名称是驼峰的。如创建指令 app.directive(‘helloWorld’,function(){ … });则在使用的时候需要用 ‘-’ 来连接,

<span hello-world></span>
  1. directive函数返回一个对象。restrict 定义了指令的识别规则。
    • A 识别attribute,作为属性名使用。
    • E 识别Element,作为标签使用。
    • C 识别class,作为class使用。
    • M 识别注释,作为注释来使用,两边要有空格。
      ==推荐使用 A E==
  2. template 指令显示的内容模板。
  3. replace 标记了该指令的HTML元素中的内容是否覆盖为模板中的内容。
  4. transclude 如果为true,则意味着你想要指令内部的内容嵌套到模板中而不是直接覆盖掉,如果transclude为true,则需要在模板中提供一个带有ng-transclude指令的容器(可以div或span)来存放指令内部内容。例如:
app.directive('hello',function(){
    return {
        restrict:'ACEM',
        template:'<span>Hello Angular ... <span ng-transclude></span></span>',
        replace:false,
        transclude:true
    };
});

当你这样使用时

<span hello>内部的文本不会被直接覆盖而被包裹到模板中带有ng-transclude指令的标签中</span>

页面会显示

Hello Angular ... 内部的文本不会被直接覆盖而被包裹到模板中带有ng-transclude指令的标签中
示例2:DOM绑定事件
<body ng-app="main">
    <div ng-controller="indexController">
        <p ng-bind="count"></p>
        <button class="btn btn-primary" hello>count</button>
    </div>
    <script type="text/javascript" src="../js/angular-1.6.4/angular.min.js"></script>
    <script type="text/javascript"> var app = angular.module("main", []); app.controller("indexController", ['$scope', function($scope) { $scope.count=1; }]); app.directive('hello',function(){ return { restrict:'A', link:function(scope,element,attrs){ element.on('click',function(){ scope.$apply(function(){ scope.count++; }); }); } }; }); </script>
</body>

scope上初始化count属性并赋值1,p标签绑定count,hello指令:带有hello指令的元素绑定click事件,点击一次count加1。
解析:
link:通常在link函数中封装对dom的操作。参数:
1. scope:未直接指明scope作用域时,此scope共享controller的scope。
2. element:指标记了该指令的且由angular封装后的元素,封装了基本的方法,跟jQuery封装的用法差不多。
3. attrs:标记了该指令的元素的所有属性封装的对象。比如本例子中,标记了hello指令的button带有class属性,可以使用attrs.class 取出class属性的值。!!!注意一个坑,这个地方的属性名称会全部转成小写字母,如果你的属性名中有大写,一定要注意。
4. click函数中调用了

scope.$apply(function(){ … })

如果我们去掉这个外层的包裹,直接在click函数中写 scope.count++;会发现页面上的count值没有变化,但是在浏览器debug js的时候发现scope中的值其实已经改变了,只是页面没有做出响应。我们可以想一下,如果我们自己来实现angular的双向数据绑定,至少要有两步:1.监听数据模型与视图的变化。2.当视图数据有变时通知model更新model数据,当model数据有变时通知视图去更新视图内容。$apply() 就是其实干这个通知的事的,具体的实现原理后面我们再探讨,他的入参是一个函数。
什么情况下需要调用这个$apply()呢?大多数情况下无需手动调用,在使用angular自带的指令比如ng-click,angular内置的服务如$http() ``$timeout() 的时候,angular已经为我们调用了这个函数,我们使用的时候无需调用,如果手动调用反而会报错。而上面的例子我们没有使用angular的指令或服务,而是普通的事件监听,所以$apply()需要手动调用。

示例3:指令独立scope
<body ng-app="main">
    <div ng-controller="indexController">
        <p>{{username}}</p>
        <p hello>
            {{username}}        
        </p>
    </div>
    <script type="text/javascript" src="../js/angular-1.6.4/angular.min.js"></script>
    <script type="text/javascript"> var app = angular.module("main", []); app.controller("indexController", ['$scope', function($scope) { }]); app.directive('hello',function(){ return { scope:true, /*或者 scope:{} 都表示指令声明了自己独立的scope*/ restrict:'A', controller:function($scope){ $scope.username='Mike'; } }; }); </script>
</body>

还是hello指令,不同的是在自定义指令中返回的对象中设置了==scope:true==,表示声明指令的scope是私有的,并为指令创建了独立的控制器,为私有scope赋值username属性为Mike,在页面上在hello指令下取 username 是可以取到的,但是在hello指令外取的username是没有值的。

示例4:指令独立scope中的数据绑定外层controller的scope策略
‘@’ +{{}}策略: :字符串传递,数据单向绑定,当修改指令外层scope中的模型数据时指令内部绑定的模型值会改变,但是修改指令内部的模型值时外层不会改变;
<body ng-app="main">
    <div ng-controller="indexController">
        <p>{{username}}</p>
        indexController下的scope值 <input type="text" ng-model="username" />
        <p hello name="{{username}}">
            指令私有scope: {{name}}     
        </p>
    </div>
    <script type="text/javascript" src="../js/jquery1.12.4.min.js"></script>
    <script type="text/javascript" src="../js/angular-1.6.4/angular.min.js"></script>
    <script type="text/javascript"> var app = angular.module("main", []); app.controller("indexController", ['$scope', function($scope) { $scope.username="Lily"; }]); app.directive('hello',function(){ return { scope:{ name:'@' }, restrict:'A', relplace:true, template:'<p><p>指令私有scope中的值:{{name}}<p><button class="btn btn-primary">change private</button></p>', link:function(scope,ele,attrs){ $(ele).find("button").on("click",function(){ scope.$apply(function(){ scope.name="Mike"; }); }); } }; }); </script>
</body>

这个例子中引入了jQuery。
解析:将indexController的scope中的username的值传递给了自定义指令hello,input框绑定的是indexController的scope中的模型,指令我们为指令模板中的button添加点击事件,当点击按钮时,改变私有scope中name的值。
运行结果:当我们在input框中改变可以看到指令所有scope中的值也会响应变化,因为@绑定策略就是将模型的值传递给指令,字符串传递。但是当我们点击按钮改变私有scope的name的值的时候,indexController的scope是不会响应这种变化的

‘=’ 策略:模型对象传递
<!-- 修改上述例子的指令里面的scope中的name绑定策略为 ‘=’-->
...
scope:{
    name:'@'
    ...
}
...

<!--页面上绑定的方式改为:-->
...
<p hello name="username">
...

解析:‘=’绑定策略传递的是对象
运行结果:两个scope中模型的值是双向绑定的,改变任何一个,另一个也会响应这种变化。

‘&’ 策略:函数传递,指令调用
<body ng-app="main">
    <div ng-controller="indexController">
        <p hello say="sayEnglish()"></p>
    </div>
    <script type="text/javascript" src="../js/angular-1.6.4/angular.min.js"></script>
    <script type="text/javascript"> var app = angular.module("main", []); app.controller("indexController", ['$scope', function($scope) { $scope.sayEnglish=function(){ alert('Good Morning!'); }; }]); app.directive('hello',function(){ return { scope:{ say:'&' }, restrict:'A', relplace:true, template:'<p><button class="btn btn-primary" ng-click="say()">打声招呼</button></p>' }; }); </script>
</body>

解析:将外层indexController的scope中的sayEnglisg()方法传递给了指令,指令内部在调用say() 方法时,实际调用的是 sayEnglish();