AngularJS是一款优秀的前端JS框架,已经被用于Google的多款产品当中。AngularJS有着诸多特性,最为核心的是:MVC、模块化、自动化双向数据绑定、语义化标签、依赖注入等等。
AngularJS现行主要区分为2个版本系列,分别是AngularJS1.x和AngularJS2,两个版本间的使用上有不少的区别,虽说AngularJS2是对AngularJS1.x的升级和精简,但在资料文档及稳定性上还暂时不如AngularJS1.x,所以我们现在较多的web应用还是用AngularJS1.x开发。从个人了解AngularJS1.x的层次上讲,想要学习AngularJS2,最好还是先从AngularJS1.x开始学习,磨刀不误砍柴工。
AngularJS1.x最大的使用特点就是在标签元素上添加"ng-"指令,通过两个大括号"{{}}"作为表达式,实现对变量的引用及数值显示。本博客的所有代码均使用当前最新版AngularJS v1.6.5版本。下面是一个简单的hello world测试代码,这也是最常见的基础格式:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script type="text/javascript" src="js/libs/angular/angular.js"></script> </head> <body> <div ng-app="myApp" ng-controller="myCtrl"> <input type="text" ng-model="myInput" /> </div> <script> var app = angular.module("myApp", []); app.controller("myCtrl", function($scope) { $scope.myInput = "hello world"; }); </script> </body> </html>
上述例子中,用到了ng-app、ng-controller,这两个指令几乎是每个使用Angular框架页面必备的指令,分别表示对该页面的定义与控制。ng-model表示定义一个变量"myInput"与<input>元素绑定,而在<script>代码块中,,$scope.myInput表示了对变量"myInput"的赋值。最终的页面的显示结果如下:
一. ng-?指令
ng-?指令表示angular框架对元素的操作,我总结了些常用的ng-?指令,供大家可以对指令有一个快速了解过程。至于每个指令的详细使用代码,由于太多这里就不贴出来了,可以留言交流或者百度一下,基本都能找到。
<!-- 描述:ng-?指令表示angular框架对元素的操作,常见的有如下几种: ----------------------------------------------------------- ng-app:定义应用程序的根元素,若不为""时需在js脚本中初始化。 ng-bind:把应用程序变量绑定到某个元素的 innerHTML。 ng-controller:定义应用的控制器对象,可以控制的服务有$scope,$rootScope,$location,$http,$timeout,$interval,其中$scope.$watch('lastName', function() {)); 可以监控变量的变化 ng-model:绑定 HTML 控制器的值到应用数据,即angular变量值,变量值可以是字符串、对象甚至是对象属性。 ng-init:定义应用的初始化值,一般为angular变量值。 ng-blur:规定 blur 事件的行为。 ng-change:规定在内容改变时要执行的表达式。 ng-checked:用于设置复选框(checkbox)或单选按钮(radio)的 checked 属性。 ng-class:指定 HTML 元素使用的 CSS 类。 ng-class-even:类似 ng-class,但只在偶数行起作用,常用table/tr/td、ul/li等元素配合使用 ng-class-odd:类似 ng-class,但只在奇数行起作用 ng-click:规定click 事件的行为。 *ng-cloak:在应用正要加载时防止其闪烁。如<p ng-cloak>{{ 5 + 5 }}</p> ng-copy:用户触发拷贝事件时,规定拷贝事件的行为。 ng-cut:用户触发剪切事件时,规定剪切事件的行为。 ng-paste:用户触发粘贴事件时,规定粘贴事件的行为 ng-dblclick:用户触发双击事件时,规定双击事件的行为。 ng-disabled:可设为true|false时,规定一个元素是否被禁用,但不会隐藏。 ng-focus:规定focus焦点事件的行为。 ng-hide:隐藏或显示 HTML 元素。 ng-show:显示或隐藏 HTML 元素。 ng-href:为<a>元素指定链接。 ng-if:如果条件为 false 移除 HTML 元素。 ng-include:在应用中包含 HTML 文件,如<div ng-include="'myFile.htm'"></div>,但不能执行js代码,引入格式如下 <style> p { color: red; } </style> <p>大家好</p> ng-keydown:规定按下按键事件的行为。 ng-keypress:规定按下按键事件的行为,通常情况下会用ng-keydown。 ng-keyup:规定松开按键事件的行为。 *ng-list:输出时将文本转换为列表 (数组),输入文本时用逗号隔开。 <input ng-model="customers" ng-list/> <pre>{{customers}}</pre> *ng-model-options:规定如何更新模型,option 指定了绑定数据的规则,规则如下: {updateOn: 'event'}规则指定事件发生后绑定数据,如ng-model-options="{updateOn: 'blur'}" {debounce : 1000} 规定等待多少毫秒后绑定数据 {allowInvalid : true|false} 规定是否需要验证后绑定数据 {getterSetter : true|false} 规定是否作为 getters/setters 绑定到模型 {timezone : '0100'} 规则是否使用时区 ng-mousedown:规定按下鼠标按键时的行为。 ng-mouseenter:规定鼠标指针穿过元素时的行为。 ng-mouseleave:规定鼠标指针离开元素时的行为。 ng-mousemove:规定鼠标指针在指定的元素中移动时的行为。 ng-mouseover:规定鼠标指针位于元素上方时的行为。 ng-mouseup:规定当在元素上松开鼠标按钮时的行为。 *ng-non-bindable:规定元素或子元素不能绑定数据,如<p ng-non-bindable>不使用 AngularJS: {{ 5+5 }}</p>显示的是“5+5”而不是“10” *ng-open:指定元素的 open 属性,可设值为true|false,常与details等具展开效果的元素配合使用。 <details ng-open=true> <summary>学的不仅是技术,更是梦想!</summary> <p> - 菜鸟教程</p> </details> ng-options:在 <select> 下拉列中指定 <options>,如<select ng-model="selectedName" ng-options="item for item in names"></select> 数据格式为字符串数组["",""] 使用x for x in names 数据格式为单独的对象{"a":1,"b":2} 使用x for (x,y) in names 数据格式为对象数组[{},{}] 使用x.attr for x in names ng-selected:指定元素的 selected 属性,表示当前选择项,常需与select元素配合使用,类似于ng-checked。 ng-readonly:指定元素的 readonly 属性。 ng-repeat:定义集合中每项数据的模板,该参数还常与ng-click、ng-class配合使用 <option ng-repeat="x in address">{{x}}</option> ng-src:指定 <img> 元素的 src 属性。 *ng-srcset:指定 <img> 元素的 srcset 属性。H5的新属性,允许输入多张图片地址以匹配不同w像素值宽度的容器。 <img ng-src="source.jpg" width="100%" ng-srcset="source_400.jpg 400w, source_600.jpg 600w, source_1280.jpg 1280w"> ng-style:指定元素的 style 属性,可在控制器中为ng-style所在变量赋值。 ng-submit:规定submit 事件的行为。 ng-switch:规定显示或隐藏子元素的条件。常与ng-switch-when配合使用,类似于switch和case ng-value:规定 input 元素的值。 angular.lowercase() 将字符串转换为小写 angular.uppercase() 将字符串转换为大写 angular.copy() 数组或对象深度拷贝 *angular.forEach() 对象或数组的迭代函数 var objs = [{a: 1}, {a: 2}]; angular.forEach(objs, function(data, index, array) { //data等价于array[index] console.log(data.a + '=' + array[index].a); }); var objs = {"a":1,"b":2} angular.forEach(objs, function(data, index, array) { //1 "a" {a: 1, b: 2} console.log(data,index,array); }); angular.isArray() 如果引用的是数组返回 true angular.isDate() 如果引用的是日期返回 true angular.isDefined() 如果引用的已定义返回 true angular.isElement() 如果引用的是 DOM 元素返回 true angular.isFunction() 如果引用的是函数返回 true angular.isNumber() 如果引用的是数字返回 true,如果输入框是input标签,要检测输入框内容是否为数字,则使用!isNaN($scope.myInput); angular.isObject() 如果引用的是对象返回 true angular.isString() 如果引用的是字符串返回 true angular.isUndefined() 如果引用的未定义返回 true angular.equals(a,b) 如果两个对象相等返回 true *angular.fromJson() 反序列化 JSON 字符串 *angular.toJson() 序列化 JSON 字符串 -->
二. 在一个页面里创建多个ng-app
有人说,这不是很简单吗?在不同元素标签中设置指令ng-app就可以了。其实,在Angular1.x中,框架只承认第一个ng-app,即通过var app = angular.module("myApp", []);即可获取它的操作权;此时第二个ng-app需要我们自己来初始化,代码如下:
<div id="A1" ng-app="app1"> <input ng-model="name" type="text" placeholder="请输入姓名"> <p>我的姓名: {{name}}</p> </div> <div id="A2" ng-app="app2"> <input ng-model="age" type="number" placeholder="请输入年龄"> <p>我的年龄: {{age}}</p> </div> <script type="text/javascript"> var app1 = angular.module("app1", []); //自动加载 var app2 = angular.module("app2", []); //手动加载 angular.bootstrap(document.getElementById("A2"), ['app2']); // 手动加载2 </script>
(注)正常情况下一个页面只需一个ng-app即可满足。
三. 架构中创建编译元素
在原生js中,通过脚本添加一个新的元素标签很简单,但是在Angular1.x中,添加新的元素则需要通过编译实现,编译的目的在于让添加的脚本代码支持ng-?指令及{{}}表单式。展示代码如下:
<div ng-app="myApp" ng-controller="myCtrl as ctrl"> <div id="addEle"></div> </div> <script> var app = angular.module("myApp", []); app.controller("myCtrl", function($scope, $compile) { var vm = this; vm.msg = 'hello'; // 创建编译函数 var compileFn = $compile('<div>{{ctrl.msg}}</div>'); // 传入scope,得到编译好的dom对象(已封装为jqlite对象) // 也可以用$scope.$new()创建继承的作用域 var $dom = compileFn($scope); //返回的$dom是一个jQLite(jQuery的子集)对象 console.log($dom); // 添加到文档中 var jqLite = angular.element(document.getElementById("addEle")); jqLite.append($dom); //$dom.remove(); //移除语句 }); </script>
四. jqLite的应用
jquery是js开发中常用的一个开发库,那么在Angluar1.x框架中想要使用jquery,是否需要把它引用进来?答案是不用。因为Angluar1.x已经内嵌了jquery的源码,即jqLite。
var jqEle = $("#myDiv"); //jquery写法 var jqLite = angular.element(document.getElementById("myDiv")); //jqlite写法
五. 过滤器的应用
过滤器可以用于对显示数据的转换或过滤。在Angular1.x中用一个竖条"|"表示过滤器,如{{name | uppercase}}表示将name的值在显示时全部转化为大写。下面是一个对过滤器有详细代码标注的demo,其中除了系统提供的过滤条件外,还包括了如何自定义过滤器的实现:
<!-- 描述:过滤器 uppercase:格式化字符串为大写 lowercase:格式化字符串为小写 currency:格式化为货币符号 limitTo : 正数,表示从头开始截取;负数表示从尾巴开始截取 number : 格式化为保留小数点 date : 格式化为时间 orderBy:"?":某属性按从小到大排序 ( orderBy:'-id' , id 降序排列 ) ( orderBy:'id', id 升序排列 ) filter:"?":按条件过滤,如filter:{'name':'iphone'}为查找属性name值为iphone的对象 --> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <script type="text/javascript" src="../js/libs/angular/angular.js"></script> </head> <body> <div ng-app="myApp" ng-controller="myCtrl"> <input type="text" ng-model="name" /> <span>大写:{{name | uppercase}}</span> <span>小写:{{name | lowercase}}</span> <br /> <p>货币符号:{{ 250 | currency:"RMB" }}</p> <p>正数截取4位:{{"1234567890" | limitTo :4}} </p> <p>负数截取4位:{{"1234567890" | limitTo:-4}} </p> <p>保留2位小数点:{{149016.1945000 | number:2}}</p> <p>time转化为时间:{{1490161945000 | date:"yyyy-MM-dd HH:mm:ss"}}</p> <p>排序及按条件筛选:</p> <input type="text" ng-model="inData" /> <ul> <li ng-repeat="x in names | orderBy:'-country' | filter : inData"> {{ x.name + ', ' + x.country }} </li> </ul> <p>自定义过滤器,输入一个数值,得到该值+1结果:</p> <input type="text" ng-model="inDIY" /> <p>{{inDIY|addOne}}</p> <p>{{inDIY|addOneFilter}}</p> </div> <script> var app = angular.module("myApp", []); app.controller("myCtrl", function($scope) { $scope.names = [{ name: 'Jani', country: 'Norway' }, { name: 'Hege', country: 'Sweden' }, { name: 'Kai', country: 'Denmark' } ]; }); //自定义过滤器 app.filter('addOne', function() { return function(i) { var result = 0; if(!isNaN(i)) { result = 1 + parseInt(i); } return result; } }); //自定义过滤器带参数 app.service('addOneService', function() { this.add = function(i) { var result = 0; if(!isNaN(i)) { result = 1 + parseInt(i); } return result; } }); app.filter('addOneFilter', ['addOneService', function(addOneService) { return function(i) { return addOneService.add(i); }; }]); </script> </body> </html>
六. form表单标准
AngularJS1.x有专门针对form表单的系统检验指令。主要通过myForm.*.*.type或myForm.*.*的引用,其中第一个参数为form的name属性值,第二个参数为form中元素的name属性值。如myForm.myEmail.$error.email则检验name="myEmail"的元素输入的值是否符合email的书写规范,若符合则返回ture,反之为false。更多的检验列表如下:
$error.required 唯一值验证 $error.email 文本输入内置电子邮件验证。 $error.number 带有数量验证的文本输入。也可以有最小和最大值的附加属性。 $error.date 带有输入日期文本输入。 $error.url 带有输入验证的URL文本输入。 $error.minlength,参数范围需从input中ng-minlength设置 $error.maxlength,参数范围需从input中ng-maxlength设置 $error.pattern,正则表达式需从input中ng-pattern设置 $dirty 表单有填写记录 $pristine 表单没有填写记录 $valid 字段内容合法的,如formname.$valid $invalid 字段内容是非法的标准的表单脚本如下:
<!-- 描述:基本表单元素及表单验证 ng-options:下拉列选择框 ,如 <select ng-init="selectedName = names[0]" ng-model="selectedName" ng-options="x for x in names"></select> 数据格式为字符串数组["",""] 使用x for x in names 数据格式为单独的对象{x:y} 使用x for (x,y) in names 数据格式为对象数组[{},{}] 使用x.attr for x in names ng-repeat:重复元素,如 <option ng-repeat="x in address">{{x}}</option> --> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>demo-ng-form</title> <style> input.ng-invalid { background-color: lightblue; } </style> <script type="text/javascript" src="../js/libs/angular/angular.js"></script> </head> <body> <div ng-app="myApp" ng-controller="myCtrl"> <form name="myForm"> <label for="in_text">姓名:</label> <input id="in_text" type="text" ng-model="in_text" name="myName" required/> <span ng-show="myForm.myName.$error.required">(姓名为必填项)</span> <br /> <label>性别:</label> <input type="radio" name="mySex" ng-model="in_Sex" value="男" ng-checked="true" />男 <input type="radio" name="mySex" ng-model="in_Sex" value="女" />女<br /> <label>课程:</label> <input type="checkbox" ng-model="in_checkbox1" />课程1 <input type="checkbox" ng-model="in_checkbox2" />课程2<br /> <label>邮箱:</label> <input type="email" ng-model="in_email" name="myEmail" /> <span ng-show="myForm.myEmail.$error.email">(邮箱格式不正确)</span> <br /> <label>电话:</label> <input type="tel" ng-model="in_tel" /><br /> <label>居住:</label> <!--<select ng-model="in_select" ng-init="in_select = address[0]" ng-options="x for x in address"></select>--> <select ng-model="in_select" ng-init="in_select = address[0]"> <option ng-repeat="x in address">{{x}}</option> </select><br /> <label>评价:</label> <textarea ng-model="in_textarea" cols="10" rows="10"></textarea><br /> <button ng-click="ok()">提交</button> </form> </div> <script> var app = angular.module("myApp", []); app.controller("myCtrl", function($scope) { $scope.in_text = ""; $scope.in_Sex = "男"; $scope.in_checkbox1 = $scope.in_checkbox2 = false; $scope.in_email = ""; $scope.in_tel = ""; $scope.address = ["厦门", "泉州", "福州"]; $scope.ok = function() { console.log("姓名:", $scope.in_text); console.log("性别:", $scope.in_Sex); console.log("课程1:", $scope.in_checkbox1); console.log("课程2:", $scope.in_checkbox2); console.log("邮箱:", $scope.in_email); console.log("电话:", $scope.in_tel); console.log("居住:", $scope.in_select); console.log("评价:", $scope.in_textarea); console.log("-----------------分割线--------------------"); } }); </script> </body> </html>
(注)$valid和ng-valid,返回Boolean,告诉我们这一项当前基于你设定的规则是否验证通过
(注)$invalid和ng-invalid,返回Boolean,告诉我们这一项当前基于你设定的规则是否验证未通过
七. 表格的实现
此处贴出一段表格代码,关键在于<table>标签的使用及样式的调整:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>demo-ng-table</title> <script type="text/javascript" src="../js/libs/angular/angular.js"></script> <style> table, th, td { border: 1px solid grey; border-collapse: collapse; padding: 5px; } table tr:nth-child(odd) { background-color: #f1f1f1; } table tr:nth-child(even) { background-color: #ffffff; } </style> <!--写法2--> <!--<style> table, td { border: 1px solid grey; border-collapse: collapse; padding: 5px; } </style>--> </head> <body> <div ng-app="myApp" ng-controller="customersCtrl"> <table> <tr ng-repeat="x in names"> <td>{{ $index + 1 }}</td> <td>{{ x.Name }}</td> <td>{{ x.Country }}</td> </tr> </table> <!--写法2--> <!--<table> <tr ng-repeat="x in names"> <td ng-if="$odd" style="background-color:#f1f1f1"> {{ x.Name }}</td> <td ng-if="$even"> {{ x.Name }}</td> <td ng-if="$odd" style="background-color:#f1f1f1"> {{ x.Country }}</td> <td ng-if="$even"> {{ x.Country }}</td> </tr> </table>--> </div> <script> var app = angular.module('myApp', []); app.controller('customersCtrl', function($scope, $http) { $scope.names = [{ "Name": "Alfreds Futterkiste", "City": "Berlin", "Country": "Germany" }, { "Name": "Ana Trujillo Emparedados y helados", "City": "México D.F.", "Country": "Mexico" }, { "Name": "Antonio Moreno Taquería", "City": "México D.F.", "Country": "Mexico" }, { "Name": "Around the Horn", "City": "London", "Country": "UK" }, { "Name": "B's Beverages", "City": "London", "Country": "UK" }, { "Name": "Berglunds snabbköp", "City": "Luleå", "Country": "Sweden" }, { "Name": "Blauer See Delikatessen", "City": "Mannheim", "Country": "Germany" }, { "Name": "Blondel père et fils", "City": "Strasbourg", "Country": "France" }, { "Name": "Bólido Comidas preparadas", "City": "Madrid", "Country": "Spain" }, { "Name": "Bon app'", "City": "Marseille", "Country": "France" }, { "Name": "Bottom-Dollar Marketse", "City": "Tsawassen", "Country": "Canada" }, { "Name": "Cactus Comidas para llevar", "City": "Buenos Aires", "Country": "Argentina" }, { "Name": "Centro comercial Moctezuma", "City": "México D.F.", "Country": "Mexico" }, { "Name": "Chop-suey Chinese", "City": "Bern", "Country": "Switzerland" }, { "Name": "Comércio Mineiro", "City": "São Paulo", "Country": "Brazil" }]; }); </script> </body> </html>
八. switch、switch-when的应用
switch、switch-when正体现的AngularJS1.x的强大之处,这意味着能将类似于if、else的功能写到元素标签中。下述demo实现了点击不同的单选按钮,同一个<div>里会显示不同的内容,源码如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>demo-ng-switch</title> <script type="text/javascript" src="../js/libs/angular/angular.js" ></script> </head> <body> <div ng-app="myApp" ng-controller="myCtrl"> <form> 选择一个选项: <input type="radio" ng-model="myVar" value="dogs">Dogs <input type="radio" ng-model="myVar" value="tuts">Tutorials <input type="radio" ng-model="myVar" value="cars">Cars </form> <div ng-switch="myVar"> <div ng-switch-when="dogs"> <h1>Dogs</h1> <p>Welcome to a world of dogs.</p> </div> <div ng-switch-when="tuts"> <h1>Tutorials</h1> <p>Learn from examples.</p> </div> <div ng-switch-when="cars"> <h1>Cars</h1> <p>Read about cars.</p> </div> </div> </div> <script> var app = angular.module("myApp", []); app.controller("myCtrl", function($scope) {}); </script> </body> </html>
九. 高阶知识-自定义指令
自定义指令泛指自定义元素标签、自定义元素属性等,它的使用目的是:
1. 使你的Html更具语义化,不需要深入研究代码和逻辑即可知道页面的大致逻辑。
2. 抽象一个自定义组件,在其他地方进行重用。
指令命名遵循驼峰命名法,使用时驼峰间用-分隔。自定义代码结构如下:
angular.module("app",[]).directive("directiveName",function(){ return{ //通过设置项来定义 restrict: "EACM", template: "<p>helloWorld</p>", replace: true, //渲染成功后的回调 link: function(scope, element, attrs) {} }; })
(注)template:自定义标签模板,当其值长度过大时,可以用templateUrl代替,即把模板指向一个独立的HTML文。如果想保留模板内的标签,只需加上<span ng-transclude></span>即可。
我们自定义了一个标签名为directiveName的元素,该元素显示为一段helloWorld文本。元素表现为:<directive-Name></directive-Name>
设置项restrict:EACM,每个字母表示一种使用自定义指令的方式。
设置项: restrict:E:标签使用 restrict:A:属性使用 restrict:C:类名使用 restrict:M,需同步设replace:true:注释使用基础的自定义指令完整的演示脚本如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>demo-ng-directive</title> <script type="text/javascript" src="../js/libs/angular/angular.js"></script> </head> <body ng-app="myApp"> <input type="text" ng-model="inData" ng-init="inData=0" /> <mydirective></mydirective> <div mydirective></div> <div class="mydirective"></div> <!-- directive: mydirective --> <script> var app = angular.module("myApp", []); app.directive("mydirective", function() { return { restrict: "EACM", replace: true, template: "<p>helloWorld</p>", link: function(scope, element, attrs) {} }; }); </script> </body> </html>自定义指令还有两个很常用的使用场景:
//渲染完成事件触发 <span on-ot-finish-render-filters></span> $scope.$on('ot_ngRepeatFinished', function(ngRepeatFinishedEvent) {}); ngApp.directive('onOtFinishRenderFilters', function($timeout) { return { restrict: 'A', link: function(scope, element, attr) { $timeout(function() { scope.$emit('ot_ngRepeatFinished'); }); } }; }); //重定义元素系统事件 <input type="file" id="upload" custom-on-change="uploadFile"> ngApp.directive('customOnChange', function() { return { restrict: 'A', link: function(scope, element, attr) { var onChangeHandler = scope.$eval(attr.customOnChange); element.bind('change', onChangeHandler); } }; });
十. 高阶知识-自定义服务
服务是一个函数或对象,可在你的 AngularJS 应用中使用。AngularJS 内建了30 多个服务。如常见的$scope、$http、$location、$interval、$timeout等。自定义服务的目的在于定义一些公共方法,实现便捷开发及代码重用。自定义服务常用于在控制器中,除此之外,还能用在上篇幅内容中的自定义过滤器filter。下面是一个定义了一个包含相加和相减算法的自定义服务。
<div ng-app="myApp" ng-controller="myCtrl"> <input type="number" ng-model="num1" /> <input type="number" ng-model="num2" /> <button ng-click="countFun();">计算</button> <br /> <p ng-model="jia">相加:{{jia}}</p> <p ng-model="jian">相减:{{jian}}</p> </div> <script> var app = angular.module("myApp", []); app.service("countNum", function() { this.jia = function(a, b) { return parseInt(a) + parseInt(b); } this.jian = function(a, b) { return parseInt(a) - parseInt(b); } }); app.controller("myCtrl", function($scope, countNum) { $scope.num1 = 0; $scope.num2 = 0; $scope.jia = 0; $scope.jian = 0; $scope.countFun = function() { $scope.jia = countNum.jia($scope.num1, $scope.num2); $scope.jian = countNum.jian($scope.num1, $scope.num2); } }); </script>
十一. 高阶知识-依赖注入di
依赖注入(Dependency Injection,简称DI)是一种软件设计模式,在这种模式下,一个或更多的依赖(或服务)被注入(或者通过引用传递)到一个独立的对象(或客户端)中,然后成为了该客户端状态的一部分。简而言之,就是定义一个对外开放的服务service,服务service的实现算法由第三方决定。di主要有五个核心组件:
service:对外开放的服务 value:Value 是一个简单的 javascript 对象,用于向控制器传递值(配置阶段)。 factory:是一个函数,用于向service提供计算或返回值。 provider:功能与factory相似,定义一个factory函数,用于向service提供计算或返回值。。 constant:constant(常量)用来在配置阶段传递数值,注意这个常量在配置阶段是不可用的。依赖注入有两种写法,一种是通过.factory;另一种是通过.provider。依赖注入的展示源码如下:
<div ng-app="mainApp" ng-controller="CalcController"> <p>输入一个数字: <input type="number" ng-model="number" /></p> <button ng-click="square()">X<sup>2</sup></button> <p>结果: {{result}}</p> </div> <!-- 依赖注入写法1 --> <script> var mainApp = angular.module("mainApp", []); mainApp.value("defaultInput", 5); mainApp.constant("configParam", "constant value"); mainApp.factory('MathService', function() { var factory = {}; factory.multiply = function(a, b) { return a * b; } return factory; }); mainApp.service('CalcService', function(MathService) { //或者返回一个对象 this.square = function(a) { return MathService.multiply(a, a); } }); mainApp.controller('CalcController', function($scope, CalcService, defaultInput, configParam) { $scope.number = defaultInput; $scope.result = CalcService.square($scope.number); $scope.square = function() { $scope.result = CalcService.square($scope.number); } console.log(defaultInput); console.log(configParam); }); </script> <!-- 依赖注入写法2 --> <!--<script> var mainApp = angular.module("mainApp", []); mainApp.config(function($provide) { $provide.provider('MathService', function() { this.$get = function() { var factory = {}; factory.multiply = function(a, b) { return a * b; } return factory; }; }); }); mainApp.value("defaultInput", 5); mainApp.constant("configParam", "constant value"); mainApp.service('CalcService', function(MathService) { this.square = function(a) { return MathService.multiply(a, a); } }); mainApp.controller('CalcController', function($scope, CalcService, defaultInput, configParam) { $scope.number = defaultInput; $scope.result = CalcService.square($scope.number); $scope.square = function() { $scope.result = CalcService.square($scope.number); } console.log(defaultInput); console.log(configParam); }); </script>-->
十二. 高阶知识-$http请求
http请求主要分3种方式:get、post、jsonp。
$http请求通用请求结构如下(支持get/post):
$http({ method: 'POST', url: 'url' }).then(function successCallback(response) { // 请求成功执行代码 }, function errorCallback(response) { // 请求失败执行代码 })
其他的$http请求写法如下:
$ttp请求时设置参数: $http.get('url', {params: {name:'lisa'}})。 $http.jsonp("url?callback=CALLBACK&name='lisa'") $http({ method: 'post', url: 'url', params:{'name':'lisa'}, data: blob })
在使用jsonp请求时,还需要为请求地址设置白名单才会请求成功:
//配置http访问的白名单,使用jsonp时用到 app.config(function($sceDelegateProvider) { $sceDelegateProvider.resourceUrlWhitelist([ 'self', //本域 'http://voice.yoya.com/**' //跨域 ]); });$http请求时,要注意一个跨域问题。若不在同一个域,则会因为跨域导致请求失败,此时需要在java服务端加入跨域允许脚本,前端不用修改代码:
response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods","POST"); response.setHeader("Access-Control-Allow-Headers","x-requested-with,content-type"); response.setContentType("text/html;charset=utf-8"); response.setCharacterEncoding("utf-8"); request.setCharacterEncoding("utf-8");有时候服务端会要求$http请求会自带特定头信息,可通过下面代码实现:
var app = angular.module("myApp", [], function($httpProvider) { $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8'; });$http请求拦截实现代码如下,可以在返回的http信息进行过滤后再返回业务层处理:
//http请求拦截,支持拦截post、get、jsonp app.config(function($httpProvider) { $httpProvider.interceptors.push(function( /**此处可加service*/ ) { return { 'request': function(config) { console.log(config); return config; }, 'response': function(response) { console.log(response); return response; }, 'responseError': function(Error) { console.log(Error); } } }); });三种方式完整的$http请求源码如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>demo-ng-http</title> <script type="text/javascript" src="../js/libs/angular/angular.js"></script> <script type="text/javascript" src="../js/libs/transformCode.js"></script> </head> <body> <div ng-app="myApp" ng-controller="myCtrl"> <button ng-click="requesthttp();">跨域请求语音服务</button> <p>请求结果:</p> <p ng-model="result">{{result}}</p> <audio ng-src="{{audioURL}}" controls="controls"></audio> </div> <script> var app = angular.module("myApp", [], function($httpProvider) { $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8'; }); //http请求拦截,支持拦截post、get、jsonp app.config(function($httpProvider) { $httpProvider.interceptors.push(function( /**此处可加service*/ ) { return { 'request': function(config) { console.log(config); return config; }, 'response': function(response) { console.log(response); return response; }, 'responseError': function(Error) { console.log(Error); } } }); }); //配置http访问的白名单,使用jsonp时用到 app.config(function($sceDelegateProvider) { $sceDelegateProvider.resourceUrlWhitelist([ 'self', //本域 'http://voice.yoya.com/**' //跨域 ]); }); //post、get、jsonp请求 app.controller("myCtrl", function($scope, $http, $timeout) { $scope.requesthttp = function() { //jsonp var beanObject = new Object(); beanObject.roleid = "701"; beanObject.nsspermissions = "public"; beanObject.volumn = "50"; beanObject.speed = "50"; beanObject.ttstxet = encodeURI("123"); //验证object转为json var jsonData = JSON.stringify(beanObject); $http.jsonp('http://localhost:80/ninVoiceService_V2/NewTTSJP?base64Data=' + jsonData, { jsonpCallbackParam: 'jsonpcallback' }).then(function(response) { console.log("success:", response); }, function(response) { console.log("error:", response); }); // return; // get 请求一个json文件 // $http.get("http://nss-public.yoya.com/netinnet-movie-pro/data/release_movie/201612/57832e74e4b005e960aec8ad00000000/20161223111741/58212c7ae4b0d8e28b0684d300000000/play.json") // .then(function(response) { // console.log("success:", response); // }, function(response) { // console.log("error:", response); // }); // // return; // post 请求一段TTS音频 // var msg = "大家好,欢迎来到优芽"; // var beanObject = new Object(); // beanObject.roleid = "701"; // beanObject.nsspermissions = "public"; // beanObject.volumn = "50"; // beanObject.speed = "50"; // beanObject.ttstxet = encodeURI(msg); // var jsonData = JSON.stringify(beanObject); // var param_Base64 = encode64(jsonData); //transformCode.js参数加密 // $http({ // method: 'post', // url: 'http://localhost:80/ninVoiceService_V2/NewTTS', // params: { // 'name': 'lisa' //值对格式参数 // }, // data: param_Base64 //文本流格式参数,若没有则可移除 // }).then(function(response) { // console.log("success:", response); // $timeout(function() { // var url = response.data.url; // $scope.audioURL = url.toString(); // }, 5000); // }, function(response) { // console.log("error:", response); // }); } }); </script> </body> </html>
十三. 高阶知识-上传文件
上传文件是指如上传图片、压缩包等,其上传原理与原生js上传没有太大的差异,无非是写法不一致而已。下面是一段上传图片的完整代码:
<div ng-app="myApp" ng-controller="myCtrl"> <input type="file" id="upload" custom-on-change="uploadFile"> </div> <script> var app = angular.module("myApp", []); app.controller("myCtrl", function($scope, $http) { //上传事件 $scope.uploadFile = function(event) { var files = event.target.files[0]; document.querySelector("#upload").value = ""; var fileSize = files.size; var size = fileSize / 1024; if(size > 10000) { tipsMsg("上传的图片不能大于10M"); return } var reqURL = ""; var params = {}; var data = new FormData(); data.append('image', files); $http({ method: 'post', url: reqURL, params: params, data: data, headers: { 'Content-Type': undefined }, transformRequest: angular.identity }).then(function successCallback(response) { console.log(response); }, function errorCallback(response) { console.log(response); }); } }); app.directive('customOnChange', function() { return { restrict: 'A', link: function(scope, element, attr) { var onChangeHandler = scope.$eval(attr.customOnChange); element.bind('change', onChangeHandler); } }; }); </script>(注)anjularjs对于post和get请求默认的Content-Type是application/json。通过设置‘Content-Type’: undefined,这样浏览器不仅帮我们把Content-Type 设置为 multipart/form-data,还填充上当前的boundary,如果你手动设置为: ‘Content-Type’: multipart/form-data,后台会抛出异常:the current request boundary parameter is null。 通过设置 transformRequest: angular.identity ,anjularjs transformRequest function 将序列化我们的formdata object.
十四. 高阶知识-路由的应用
AngularJS路由是指实现多视图的单页面效果。在单页Web应用中 AngularJS 通过 # + 标记 实现,例如http://***.com/#/first,当我们点击任意一个链接时,向服务端请的地址都是一样的 (http://***.com/)。 因为 # 号之后的内容在向服务端请求时会被浏览器忽略掉。 所以我们就需要在客户端实现 # 号后面内容的功能实现。因此AngularJS 路由 就通过 # + 标记 帮助我们区分不同的逻辑页面并将不同的页面绑定到对应的控制器上。
这里要值得注意的是,AngularJS1.6.x的路由与1.5.x有所不同,在配置路由时,需加上这句代码:
$locationProvider.hashPrefix(''); // 1.6.x版本使用路由功能需加上这句否则路由将无法生效,这是因为1.6.x版本的路由跳转视图时,会在跳转的视图路径后面默认多增加#!符号,导致框架无法识别该视图地址。(感觉这个更新是个坑),除了这种解决办法外,还有一种办法就只指定视图地址时,使用如下方法指定:
<a href="#!/red">红色方块</a>下面是一段完整的路由使用代码:
<html> <head> <meta charset="utf-8" /> <title>AngularJS 视图</title> <script src="../js/libs/angular/angular.js"></script> <script src="../js/libs/angular/angular-route.js"></script> </head> <body> <div ng-app="myApp"> <ul> <li> <a href="#red">红色方块视图</a> </li> <li> <a href="#blue?a=123">蓝色方块视图</a> </li> </ul> <p>内容切换:</p> <div ng-view></div> <script type="text/ng-template" id="red.html"> <p>{{message}}</p> <div style="width: 100px; height: 100px; background-color: red;"></div> </script> <script type="text/ng-template" id="blue.html"> <p>{{message}}</p> <div style="width: 100px; height: 100px; background-color: blue;"></div> </script> </div> <script> var myApp = angular.module("myApp", ['ngRoute']); myApp.config(["$locationProvider", "$routeProvider", function($locationProvider, $routeProvider) { // <a href="#!/red">红色方块</a> $locationProvider.hashPrefix(''); // 1.6.x版本使用路由功能需加上这句 $routeProvider.when('/red', { templateUrl: 'red.html', controller: 'redController' }).when('/blue', { templateUrl: 'blue.html', controller: 'blueController', //依赖的条件 resolve: { //延时1秒切换视图 delay: function($q, $timeout) { var delay = $q.defer(); $timeout(delay.resolve, 1000); return delay.promise; } } }).otherwise({ //重定向地址 redirectTo: '/red' // 默认显示红色方块视图 }); }]); //红色方块视图操作区 myApp.controller("redController", function($scope) { $scope.message = "当前显示的是红色方块视图"; // red作用域 }); //蓝色方块视图操作区 myApp.controller("blueController", function($scope, $routeParams) { $scope.message = "当前显示的是蓝色方块视图"; // blue作用域 console.log($routeParams); //传递参数 }); </script> </body> </html>(注)路由的视图页面应作为模板页显示,只包括元素和css,因此视图页不引入js文件也不执行js代码。想要控制相应视图页,可通过该视图页的定义的控制器作用域下进行控制。
以上就是我所了解的AngularJS1.x的知识,理解了这些知识,要使用AngularJS1.x框架要开发一款就不会有太多阻碍了,有需要知识交流的可留言。