理解实现代码的逻辑为主要,代码怎么写为次要。?
参考资料:
- 《AngularJS入门与进阶》,江荣波著
前端开发常用框架
React:由Facebook开发,用于构建用户界面的JavaScript库,以组件化和虚拟DOM著称。
Angular:由Google开发,是一个完整的前端框架,支持双向数据绑定、依赖注入等特性。
Vue.js:一个轻量级、灵活的前端框架,易于学习和上手,支持响应式数据绑定。
jQuery:虽然不像React、Angular和Vue.js那样是一个完整的框架,但jQuery仍然是一个流行的JavaScript库,用于简化DOM操作和处理事件。
Ember.js:一个面向生产环境的前端框架,提供了强大的工具和模式,适用于复杂的Web应用程序。
Backbone.js:一个轻量级的MVC框架,用于给Web应用程序提供结构,支持单页面应用开发。
AngularJS
- ?AngularJS是Google工程师研发的一款开放源代码的JavaScript框架
- 使用JavaScript技术,以HTML为模板语言并扩展HTML元素及属性,同其他Web前端技术结合使用
- 下载AngularJS的Release版本,在HTML页面中使用<script>标签引入即可使用
AngularJS的重点内容:
双向数据绑定:AngularJS提供了双向数据绑定机制,使得界面和数据模型之间的同步变得更加简单和高效。
MVC架构:AngularJS采用了MVC(模型-视图-控制器)架构,将应用程序的逻辑、数据和呈现分离,帮助开发者更好地组织和维护代码。
模块化:AngularJS支持模块化开发,可以将应用程序划分为多个可重用的模块,便于管理和扩展。
指令(Directives):AngularJS提供了丰富的指令,可以用于扩展HTML功能、操作DOM元素、创建自定义标签等,提高了开发效率和灵活性。
依赖注入:AngularJS内置了依赖注入机制,可以帮助开发者更好地管理组件之间的依赖关系。
单页面应用支持:AngularJS适合构建单页面应用,可以通过路由管理实现页面之间的切换而不需要重新加载整个页面。
- 双向数据绑定:在视图(DOM元素)与作用域之间建立的数据同步机制(界面的操作能实时同步到作用域中,作用域中的数据修改也能实时回显到界面中)
- 指令:
- ng-model:双向绑定,实现作用域到视图的双向数据绑定
- ng-bind:单向绑定,实现作用域到视图的单向数据绑定
- AngularJS表达式{{}}在AngularJS没有加载完也可能会执行,直接输出表达式,但ng-bind是在AngularJS加载完毕后都会执行,输出解析过的表达式
- 不用处理的用ng-bind,需要处理的用表达式
1、直接使用表达式
<!DOCTYPE html>
<!--ng-app放在<html>标签,启动angularjs应用,让angularjs管理整个页面-->
<html lang="en" ng-app>
<head>
<meta charset="UTF-8">
<title>ch2_1</title>
<!--引入angularjs库文件-->
<script type="text/javascript" src="angularjs.js"></script>
</head>
<body>
<!--ng-model通过id="uname"将界面中用户名输入框里的内容同变量uname双向绑定在一起-->
<!--ng-model指令添加在<input>标签中-->
<div>用户名:<input type="text" ng-model="uname"/></div>
<!--AngularJS会在作用域中添加一个uname属性,和input输入框绑定-->
<!--angularjs会对{{}}里表达式的内容进行解析,然后将结果输出到浏览器-->
<div>{{uname}}</div>
</body>
</html>
2、用ng-bind指令替换表达式
<!DOCTYPE html>
<!--ng-app放在<html>标签,启动angularjs应用,让angularjs管理整个页面-->
<html lang="en" ng-app>
<head>
<meta charset="UTF-8">
<title>ch2_1</title>
<!--引入angularjs库文件-->
<script type="text/javascript" src="angularjs.js"></script>
</head>
<body>
<!--ng-model通过id="uname"将界面中用户名输入框里的内容同变量uname双向绑定在一起-->
<!--ng-model指令添加在<input>标签中-->
<div>用户名:<input type="text" ng-model="uname"/></div>
<!--用ng-bind指令可以避免浏览器直接输出未经解析的表达式-->
<div ng-bind="uname"></div>
<!--或
<div class="ng-bind:uname"></div>
-->
</body>
</html>
- MVC是一种软件架构模式,独立于任何一门语言
- M:Model,模型;V:View,视图;C:Controller,控制器
- MVC的核心思想:把数据的管理、业务逻辑控制和数据的展示分离开来,使程序的逻辑性和可维护性更强
- 控制器的基本结构:
- 每个控制器对应的作用域对象只能与ng-controller指令所在标签的开始与结束之间的DOM元素建立数据绑定,下例的范围的就是<body></body>之间的区域
<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
<meta charset="UTF-8">
<title>ch3_1</title>
<script type="text/javascript" src="angularjs.js"></script>
</head>
<!--当AngularJS框架遇到ng-controller指令时会查找名为“CalcController”的构造方法,实体化构造器对象-->
<body ng-controller="CalcController">
<!--使用ng-model指令绑定作用域对象$scope和input表单-->
<div>单价:<input type="number" ng-model="price"/></div><br/>
<div>数量:<input type="number" ng-model="num"/></div><br/>
<!--通过AngularJS表达式调用totalPrice()方法-->
<div>总价:{{totalPrice()}}</div>
<script type="text/javascript">
<!--控制器对象实例化时AngularJS会创建一个名为"$scope"的作用域对象,然后把$scope对象注入控制器对象中-->
// $log参数用于向控制台输入日志信息
function CalcController($scope,$log){
// 在控制器中通过$scope对象访问表单数据
// 指定$scope对象的price属性的初值为0
$scope.price=0;
$scope.num=0;
// 增加一个totalPrice()方法用于计算总价
$scope.totalPrice=function (){
return $scope.price * $scope.num;
}
}
// 定义一个名为"app"的模块
// angular.module()方法返回一个模块实例对象,
//我们可以调用对象的controller(),directive(),filter()等方法向模块中添加控制器、指令、过滤器等组件
var app=angular.module("app",[]);
// 使用模块实例的controller()方法来声明一个控制器,第一个参数:控制器名称;第二个:匿名方法,即控制器的构造方法
app.controller("CalcController",CalcController);
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
<meta charset="UTF-8">
<title>ch3_2</title>
<!--引入bootstrap框架美化登录界面-->
<link rel="stylesheet" type="text/css" href="bootstrap/css/bootstrap.css"/>
<script type="text/javascript" src="angularjs/angular.min.js"></script>
</head>
<body>
<div class="container" ng-controller="LoginController">
<div class="login-title text-center">
<h1><small>登录</small></h1>
</div>
<div class="form">
<div class="form-group">
<div class="col-xs-12">
<div class="input-group">
<span class="input-group-addon">
<span class="glyphicon glyphicon-user"></span>
</span>
<input type="text" ng-model="uname" class="form-control" placeholder="用户名">
</div>
</div>
</div><br/><br/>
<div class="form-group">
<div class="col-xs-12">
<div class="input-group">
<span class="input-group-addon">
<span class="glyphicon glyphicon-lock"></span>
</span>
<input type="text" ng-model="pword" class="form-control" placeholder="密码">
</div>
</div>
</div><br/><br/>
<div class="form-group form-actions">
<div class="col-xs-4 col-xs-offset-4">
<button ng-click="login()" type="submit" class="btn btn-sm btn-info">
<span class="glyphicon glyphicon-log-in"></span>
登录
</button>
<button type="reset" class="btn btn-sm btn-info">
<span class="glyphicon glyphicon-off"></span>
重置
</button>
</div>
</div>
</div>
</div>
<script>
function LoginController($scope){
$scope.login=function(){
if($scope.uname=="admin" && $scope.pword=="admin"){
alert("登录成功!")
}else{
alert("用户名或密码错误,请重试!")
}
}
}
var app=angular.module("app",[]);
app.controller("LoginController",LoginController);
</script>
</body>
</html>
应用模块划分能够使程序结构良好、提高代码可维护性,同时还能避免命名冲突
//定义一个无依赖的模块
angular.module('appModule',[])
//定义一个依赖module1, module2的模块
angular.module('appModule',['module1','module2']);
引入模块
<html ng-app="appModule">
login.html
<!DOCTYPE html>
<!--ng-app指定页面使用loginModule模块中的内容-->
<html lang="en" ng-app="loginModule">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
<script src="../angularjs/angular.min.js"></script>
<script src="common.js"></script>
</head>
<body>
<div ng-controller="UserController">
<div>用户名:<input type="text" ng-model="uname" placeholder="用户名"></div><br/>
<div>密码:<input type="password" ng-model="pword" placeholder="密码"></div><br/>
<div><button ng-click="submit()">登录</button></div>
</div>
</body>
</html>
register.html
<!DOCTYPE html>
<!--ng-app指定页面使用registerModule模块中的内容-->
<html lang="en" ng-app="registerModule">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
<script src="../angularjs/angular.min.js"></script>
<script src="common.js"></script>
</head>
<body>
<div ng-controller="UserController">
<div>用户名:<input type="text" ng-model="uname" placeholder="用户名"></div><br/>
<div>密码:<input type="password" ng-model="pword" placeholder="密码"></div><br/>
<div><button ng-click="submit()">注册</button></div>
</div>
</body>
</html>
common.js(使用模块化管理解决了命名冲突,突出模块化管理的可维护性)
// 使用AngularJS模块对两个控制器进行划分
// 因为在不同的模块下,所以控制器同名也不冲突
// 使用loginModule模块下的UserController控制器
var loginModule=angular.module("loginModule",[]);
loginModule.controller("UserController",function ($scope,$log){
$scope.uname="login";
$scope.pword="admin";
$scope.submit=function (){
alert("登录模块:UserController");
}
});
// 使用registerModule模块下的UserController控制器
var registerModule=angular.module("registerModule",[]);
registerModule.controller("UserController",function ($scope,$log){
$scope.uname="register";
$scope.pword="admin";
$scope.submit=function (){
alert("注册模块:UserController");
}
});
- AngularJS作用域本质上就是一个普通的JavaScript对象(key-value键值对)
- 当AngularJS遇到ng-app时,会自动创建一个根作用域对象$rootScope
- 当AngularJS遇到ng-controller时,会自动创建一个子作用域对象$scope
- 子作用域对象继承父作用域对象的所有属性
- 主要功能:存放模型数据
// 定义父作用域
var parent=$rootScope;
// $new()方法用于创建子作用域
var child=parent.$new();
- 对作用域中的属性进行监视,并对其变化做出相应的处理
- 监视的方法:
- 参数一:需要监视的属性
- 参数二:在监视属性发生变化时需要回调的方法
- 参数三:默认为false
- 1、$watch(参数一,参数二,false)
- 引用监视,只有监视对象的引用发生变化时,才会有反应
- 2、$watch(参数一,参数二,true)
- 全等监视,只要监视对象的内容发生变化,就反应
- 3、$watchCollection(参数一,参数二)
- 监视数组或集合
- 监视的数组或集合里某项的内容改变时,不反应;减少项或增多项时,才反应
- 性能:1>3>2
- 调用$watch()监视方法后,会返回一个方法A,满足某条件后,调用这个方法A即可解除监视
(1)添加监视
<!DOCTYPE html>
<html lang="en" ng-app="watchModule">
<head>
<meta charset="UTF-8">
<title>ch5_1</title>
<script type="text/javascript" src="../angularjs/angular.min.js"></script>
</head>
<body>
<!-- 用于输入名称的文本框,绑定到name模型 -->
<input type="text" ng-model="name">
<!-- 用于显示count变量的改变次数 -->
<div>change count:{{count}}</div>
<!--定义了一个控制器-->
<script>
angular.module('watchModule',[])
.run(['$rootScope',function ($rootScope){
// 初始化count和name属性
$rootScope.count=0;
$rootScope.name='江荣波';
// 使用$watch()方法对name属性进行监视,
// 每当name属性改变,count就加一
$rootScope.$watch('name',function (){
$rootScope.count++;
})
}]);
</script>
</body>
</html>
(2)解除监视
<!DOCTYPE html>
<html lang="en" ng-app="watchModule">
<head>
<meta charset="UTF-8">
<title>ch5_1</title>
<script type="text/javascript" src="../angularjs/angular.min.js"></script>
</head>
<body>
<input type="text" ng-model="name"> <!-- 输入框绑定到 "name" 模型 -->
<div>change count:{{count}}</div> <!-- 显示 "count" 变量的值 -->
<script>
angular.module('watchModule',[])
.run(['$rootScope',function ($rootScope){
$rootScope.count=0; // 初始化 "count" 变量
$rootScope.name='江荣波'; // 初始化 "name" 变量
var unbindWatcher=$rootScope
.$watch('name',function (newValue,oldValue){ // 监视 "name" 变化
//比较字符串用===,比较数值用==
if(newValue==='江'){ // 如果新值为 "江"
//调用$watch()返回的方法unbindWatcher()解除监视
unbindWatcher(); // 解除监视
}
$rootScope.count++; // "count" 变量加一
})
}]);
</script>
</body>
</html>
$digest循环,$apply()方法
- $digest循环:AngularJS在监视时,会周期性地运行一个函数来检查scope模型中的数据是否发生了变化
- 当AngularJS作用域中的模型数据发生变化时,AngularJS会自动触发$digest循环,自动更新视图
- 只有使用AngularJS内置的指令或服务时,它才知道我们修改了哪里
- 当我们使用非AngularJS的内置指令或服务时,AngularJS不知道我们修改了哪里,就需要手动调用$apply()方法,来触发$digest循环
- 比如下例中的JavaScript的原生方法setTimeout(),setInterval()就不属于AngularJS的内置指令或服务
- 但setTimeout(),setInterval()分别可用AngularJS的内置服务$timeout, $interval替代
- 在实际项目时,遇到作用域属性修改但视图没有更新,可以考虑一下是否是$digest循环没有触发
<!DOCTYPE html>
<html lang="en" ng-app="msgModule">
<head>
<meta charset="UTF-8">
<title>ch5_2</title>
<script type="text/javascript" src="../angularjs/angular.min.js"></script>
</head>
<body>
<div ng-controller="MsgController">
<!--单击按钮,3秒后作用域中message属性内容回显到页面中-->
<div>
<button ng-click="scheduleTask()">
3秒后回显信息
</button>
</div>
<div>
{{message}}
</div>
</div>
<script>
angular.module('msgModule',[]).controller('MsgController',
function ($scope){
$scope.scheduleTask=function (){
// 此处引用了setTimeout()方法来更新模型数据,它不属于AngularJS内置的指令或服务
// 所以AngularJS不知道我们改了什么,就需要使用$apply()来触发$digest循环
setTimeout(function (){
// 把修改作用域属性的代码移到一个匿名方法中,
// 然后把该匿名方法作为$apply()方法的参数,即可触发$digest循环
$scope.$apply(function(){
$scope.message='信息内容';
console.log('message='+$scope.message);
})
},3000);
}
});
</script>
</body>
</html>
// ...
angular.module('msgModule',[]).controller('MsgController',
// $timeout属于AngularJS的内置服务,可以自动触发$digest循环
function ($scope,$timeout){
$scope.scheduleTask=function (){
$timeout(function (){
$scope.message='信息内容';
console.log('message='+$scope.message);
},3000);
}
});
//...
- 作用域事件机制用于传递数据
- 两种事件传播机制:
- 1、事件从子作用域路由到父作用域
- 2、事件从父作用域广播到所有子作用域
- 相关方法:
- 1、$emit()
- 事件从子作用域路由到父作用域
- 参数一:事件名称;
- 参数N:传递内容
- 2、$on()
- 在父作用域中注册一个事件监听器,监听子作用域路由的事件
- 参数一:要监听事件的名称
- 参数二:事件处理方法(event事件对象还有一些实用的属性和方法,可以通过它获取更多关于事件的信息)
$scope.$on("infoEvent",function (event,data){ });
- 3、$broadcast()
- 向子作用域广播事件
- 有的子作用域只要注册了事件监听器就能收到父作用域的广播事件
<!DOCTYPE html>
<html lang="en" ng-app="eventModule">
<head>
<meta charset="UTF-8">
<title>ch5_3</title>
<script type="text/javascript" src="../angularjs/angular.min.js"></script>
<!--设置两个框的样式-->
<style>
#parent{
width:350px;
height:250px;
border:3px solid #ccc;
}
#child{
width:300px;
height:200px;
border:3px solid #ccc;
margin:10px auto;
}
</style>
</head>
<body>
<div id="parent" ng-controller="ParentController">
父作用域
<div id="child" ng-controller="ChildController">
子作用域
<!--单击Emit按钮时,会触发postEvent()方法-->
<button ng-click="postEvent()">Emit</button>
</div>
</div>
<script>
var app=angular.module('eventModule',[])
// 控制器对象实例化时会创建两个具有父子关系的作用域对象
app.controller('ParentController',
function ($scope){
// 父作用域注册一个监听器,
// 监听事件的名称:infoEvent
// $on()方法的参数二又包含了两个参数,分别是事件对象,传递的数据
$scope.$on("infoEvent",function (event,data){
console.log("接收到子作用域事件...");
console.log(data);
});
});
app.controller('ChildController',
function ($scope){
// postEvent()方法会调用$emit()方法向父作用域路由事件
// 事件名称:infoEvent
// 事件内容:一个JavaScript对象,属性name的值为Jane,age:23
$scope.postEvent=function (){
$scope.$emit("infoEvent",{name:"Jane",age:23});
}
});
</script>
</body>
</html>
打开浏览器的控制台,查看输出信息
<!DOCTYPE html>
<html lang="en" ng-app="eventModule">
<head>
<meta charset="UTF-8">
<title>ch5_4</title>
<script type="text/javascript" src="../angularjs/angular.min.js"></script>
<!--设置两个框的样式-->
<style>
#parent{
width:450px;
height:250px;
border:3px solid #ccc;
}
.child{
width:150px;
height:200px;
border:3px solid #ccc;
float:left;
margin-left:20px;
}
</style>
</head>
<body>
<div id="parent" ng-controller="ParentController">
<div>
父作用域
<button ng-click="postEvent()">Broadcast</button>
</div>
<div class="child" ng-controller="Child1Controller">
子作用域1
</div>
<div class="child" ng-controller="Child2Controller">
子作用域2
</div>
</div>
<script>
var app=angular.module('eventModule',[])
// 控制器对象实例化时会创建三个具有父子关系的作用域对象,一父二子
app.controller('ParentController',
function ($scope){
// 对按钮触发的单击事件进行处理
$scope.postEvent=function (){
// 向子作用域广播事件
$scope.$broadcast("infoEvent",{name:"Jane",age:23});
}
});
app.controller('Child1Controller',
function ($scope){
// 监听事件
$scope.$on("infoEvent",function (event,data){
console.log("子作用域1接收到父作用域广播事件……");
console.log(data);
});
});
app.controller('Child2Controller',
function ($scope){
// 监听事件
$scope.$on("infoEvent",function (event,data){
console.log("子作用域2接收到父作用域广播事件……");
console.log(data);
});
});
</script>
</body>
</html>
- 建立URL到AngularJS视图的映射
- $routeProvider对象,用于创建路由映射
- 把控制器、视图模板、URL三者关联起来
- 1、when(path,route)方法
- 匹配路由中已定义的URL
- path:URL
- route:控制器、视图
- 2、otherwise(params)方法
- 匹配路由中未定义的URL
add-order.html
<!--视图一-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<h3>新增订单页面</h3>
</div>
</body>
</html>
show-orders.html
<!--视图二-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<h3>显示订单列表页面</h3>
</div>
</body>
</html>
app.js
// angular.module()方法返回一个模块实例
// 需要使用到AngularJS的路由模块,该模块定义在angular-route.js文件中,需要添加ngRoute模块依赖
var routeModule=angular.module('routeModule',['ngRoute']);
// 调用模块实例的config()方法对路由进行配置
routeModule.config(['$routeProvider',
// 向$routeProvider是AngularJS的一个内置对象,用于创建路由映射
// when(path,route)和otherwise(params)方法用于把控制器、视图模板、URL三者关联起来
function ($routeProvider){
$routeProvider.
// 定义路由一,URL为/addOrder,映射到页面templates/add-order.html
when('/addOrder',{
templateUrl:'templates/add-order.html',
controller:'AddOrderController'
}).
// 定义路由二,URL为/showOrders,映射到页面templates/show-orders.html
when('/showOrders',{
templateUrl:'templates/show-orders.html',
controller:'ShowOrdersController'
}).
//未定义的URL
otherwise({
redirectTo:'/addOrder'
});
}]);
index.html
<!--实现多视图的切换-->
<!DOCTYPE html>
<html lang="en" ng-app="routeModule">
<head>
<meta charset="UTF-8">
<title>ch6_1</title>
<link rel="stylesheet" href="../../bootstrap/css/bootstrap.min.css"/>
<link rel="stylesheet" href="../../bootstrap/css/bootstrap-theme.min.css"/>
<script src="../../angularjs/angular.js"></script>
<script src="../../angularjs/angular-route.js"></script>
<script src="js/app.js"></script>
<style type="text/css">
.mainDiv{
margin:25px 50px 75px 100px;
}
</style>
</head>
<body>
<div class="mainDiv">
<!--导航栏-->
<ul class="nav nav-tabs">
<li>
<a href="#!/showOrders">
<span class="glyphicon glyphicon-th-list"></span>
订单列表
</a>
</li>
<li>
<a href="#!/addOrder">
<span class="glyphicon glyphicon-plus"></span>
新增订单
</a>
</li>
</ul>
<!--ng-view是AngularJS的一个内置指令,用于定义一个视口,加载视图内容-->
<div class="ng-view"></div>
</div>
</body>
</html>
- ?$routeParams服务
app.js
var routeModule=angular.module('routeModule',['ngRoute']);
routeModule.config(['$routeProvider',
function ($routeProvider){
$routeProvider.
// 在URL中添加一个参数orderId,用冒号隔开
when('/showOrder/:orderId',{
templateUrl:'templates/order-details.html',
controller:'ShowOrderController'
})
}]);
// 在控制器注入$routeParams服务
routeModule.controller("ShowOrderController",function ($scope,$routeParams){
// 通过$routeParams.orderId属性访问URL传递的参数,并保存在$scope对象的order_id属性中
$scope.order_id=$routeParams.orderId;
});
order-details.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ch6_2</title>
</head>
<body>
<!--把URL传递的orderId参数回显到页面中-->
<h2>订单号 #{{order_id}}</h2>
此处为订单明细…… <b>#{{order_id}}</b>
</body>
</html>
?index.html
<!--实现多视图的切换-->
<!DOCTYPE html>
<html lang="en" ng-app="routeModule">
<head>
<meta charset="UTF-8">
<title>ch6_1</title>
<link rel="stylesheet" href="../../bootstrap/css/bootstrap.min.css"/>
<link rel="stylesheet" href="../../bootstrap/css/bootstrap-theme.min.css"/>
<script src="../../angularjs/angular.js"></script>
<script src="../../angularjs/angular-route.js"></script>
<script src="js/app.js"></script>
<style type="text/css">
.mainDiv{
margin:25px 50px 75px 100px;
}
</style>
</head>
<body>
<div class="mainDiv">
<!--导航栏-->
<ul class="nav nav-tabs">
<li>
<a href="#">
<span class="glyphicon glyphicon-th-list"></span>
订单列表
</a>
</li>
</ul>
<div>
<table class="table">
<thead>
<tr>
<th>订单号</th>
<th>商品名称</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr class="success">
<td>1102</td>
<td>笔记本电脑</td>
<!--在URL中传递不同的订单号,单击链接后加载订单明细视图-->
<td><a href="#!/showOrder/1102">查看明细</a></td>
</tr>
<tr class="info">
<td>4531</td>
<td>移动硬盘</td>
<td><a href="#!/showOrder/4531">查看明细</a></td>
</tr>
<tr class="active">
<td>1036</td>
<td>iPone</td>
<td><a href="#!/showOrder/1036">查看明细</a></td>
</tr>
</tbody>
</table>
</div>
<div class="ng-view"></div>
</div>
</body>
</html>