模型 - 视图 - 控制器或MVC,MVC是普遍的叫法,是一种软件设计模式,用于开发Web应用程序。模型- 视图 - 控制器模式是由以下三部分组成:
模型/Model - 一个负责维护底层数据模式。模型是负责管理应用程序的数据。它响应来自视图的请求,同时也响应指令从控制器进行自我更新。
视图/View - 负责显示所有或数据到用户的部分。在一个特定的格式的演示数据,由控制器决定触发显示数据。
控制器/Controller - 软件代码控制Model和View之间的相互作用。
在angular.js载入并加载完成之后,寻找ng-app指令,并将存在这个指令的标签作为管理边界,继续寻找子层标签的其他ng指令并进行编译操作。
一、控制器controller
AngularJS中的控制器是一个函数,用来向视图的作用域中添加额外的功能。我们用它来给作用域对象设置初始状态,并添加自定义行为。
控制器可以将与一个独立视图相关的业务逻辑封装在一个独立的容器中。尽可能地精简控制器是很好的做法。作为AngularJS开发者,使用依赖注入来访问服务可以实现这个目的
1 . 控制器基础:
ng-controller 指令定义了应用程序控制器。控制器是 JavaScript 对象,由标准的 JavaScript 对象的构造函数 创建。
$scope对象用来从数据模型向视图传递信息。同时,它也可以用来设置事件监听器,同应用的其他部分进行交互,以及创建与应用相关的特定业务逻辑。
当我们在页面上创建一个新的控制器时, AngularJS会生成并传递一个新的$scope
给这个控制器。控制器的 $scope (相当于作用域、控制范围)用来保存AngularJS Model(模型)的对象。AngularJS允许在$scope
上设置包括对象在内的任何类型的数据,并且在视图中还可以展示对象的属性可以在这个控制器里初始化$scope
。
初始化控制器合理的方式是创建一个模块,然后在模块中创建控制器
下面的例子展示了控制器初始化:
//例如,我们在MyController上创建一个person对象,这个对象只有name这一个属性:
app.controller('MyController', function($scope) {
$scope.person = {
name: 'Ari Lerner'
};
});
<div ng-app="myApp" ng-controller="myCtrl"> //AngularJS 应用程序由 ng-app 定义。应用程序在 <div> 内运行。
//ng-controller定义一个控制器
名: <input type="text" ng-model="firstName"><br> //ng-model 指令绑定输入域到控制器的属性(firstName 和 lastName)。
姓: <input type="text" ng-model="lastName"><br>
<br>
姓名: {{firstName + " " + lastName}}
</div>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) { //AngularJS 使用$scope 对象来调用控制器。
$scope.firstName = "John";
$scope.lastName = "Doe";
});
</script>
2 . 控制器中可以有方法(变量和函数):
<div ng-app="myApp" ng-controller="personCtrl">
名: <input type="text" ng-model="firstName"><br>
姓: <input type="text" ng-model="lastName"><br>
<br>
姓名: {{fullName()}} //使用 表达式 把数据绑定到 HTML
</div>
<script>
var app = angular.module('myApp', []);
app.controller('personCtrl', function($scope) {
$scope.firstName = "John";
$scope.lastName = "Doe";
$scope.fullName = function() {
return $scope.firstName + " " + $scope.lastName;
}
});
</script>
用内置指令ng-click可以将按钮、链接等其他任何DOM元素同点击事件进行绑定。 按钮和链接可以被绑定在了内部$scope的一个操作上,当点击元素时AngularJS会调用相应的方法。
html代码:
<button class="btn btn-default" ng-click="add()">提交</button>
js:
angular.module('todoList',[])
.controller('TaskCtrl',function($scope){
$scope.task="";
$scope.tasks=[];
$scope.add=function(){
$scope.tasks.push($scope.task);
};
});
3 . 外部文件中的控制器
在大型的应用程序中,通常是把控制器存储在外部文件中。
只需要把 <script>
标签中的代码复制到名为 personController.js 的外部文件中即可:
<div ng-app="myApp" ng-controller="personCtrl">
First Name: <input type="text" ng-model="firstName"><br>
Last Name: <input type="text" ng-model="lastName"><br>
<br>
Full Name: {{firstName + " " + lastName}}
</div>
<script src="personController.js"></script>
4 . controller控制器的嵌套
AngularJS应用的任何一个部分,无论它渲染在哪个上下文中,都有父级作用域存在。对于ng-app所处的层级来讲,它的父级作用域就是$rootScope(根作用域)。
默认情况下, AngularJS在当前作用域中无法找到某个属性时,便会在父级作用域中进行查找。如果AngularJS找不到对应的属性,会顺着父级作用域一直向上寻找,直到抵达 $rootScope
为止。如果在$rootScope中也找不到,程序会继续运行,但视图无法更新。
通过例子来看一下这个行为。创建一个ParentController,其中包含一个user对象,再创建一个ChildController来引用这个对象:
<!DOCTYPE HTML>
<html ng-app="app">
<head><script src="./angular.min.js"></script></head>
<body>
<h1>控制器嵌套</h1>
<div ng-controller="ParentController" style="border:1px solid black">
<span>{{person}}</span>
<div ng-controller="ChildController" style="border:1px solid red"> //ChildController的$scope对象的父级作用域就是ParentController的$scope对象。根据原型继承的机制,我们可以在子作用域中访问ParentController的$scope对象。
<a ng-click="addName()">I add a name</a>
</div>
</div>
<script type="text/javascript">
var app= angular.module("app",[]);
app.controller('ParentController', function ($scope) {
$scope.person = {
age:15
};
});
app.controller('ChildController',function ($scope){
$scope.ladygaga="ladygaga";
$scope.addName = function(){
$scope.person.name=$scope.ladygaga;
}
});
</script>
</body>
</html>
结果{{person}}为 {age:15,name:”ladygaga”}——点击按钮时,可以在ChildController中访问ParentController中$scope.person
的值,就好像person对象定义在ChildController的$scope中一样。
5 . 注意
(1)不要复用controller,一个控制器一般只负责一小块视图
(2)AngularJS同其他JavaScript框架最主要的一个区别就是,控制器并不适合用来执行DOM操作(应该封装并交给指令directive)、格式化(ng有很好的表单控件)或数据操作(ng有很好的过滤$filter
服务),以及除存储数据模型之外的状态维护操作。它只是视图和
scope之间的桥梁。(3)一般controller不会互相调用(不会强耦合),控制器之间交互通过事件进行(
scope或者数据模型上的事件)
二、Model
在AngularJS中,模块是定义应用的最主要方式。模块包含了主要的应用代码。一个应用可以包含多个模块,每一个模块都包含了定义具体功能的代码。
使用模块能给我们带来许多好处,比如:
保持全局命名空间的清洁;
编写测试代码更容易,并能保持其清洁,以便更容易找到互相隔离的功能;
易于在不同应用间复用代码;
使应用能够以任意顺序加载代码的各个部分。
在标签中使用ng-module=”…” 指令,生成…数据模型,这个数据模型存在于$rootScope(根作用域)中,ng-app里的所有地方都可以获取此数据值。
AngularJS允许我们使用angular.module()方法来声明模块,这个方法能够接受两个参数:
angular.module('myApp', []);
第一个是模块的名称,字符串变量。
第二个是依赖列表,也就是可以被注入到模块中的对象列表。requires包含了一个字符串变量组成的列表,每个元素都是一个模块名称,本模块依赖于这些模块,依赖需要在本模块加载之前由注入器进行预加载。
三、视图view
视图复用由指令实现
AngularJS支持通过在单个页面上的多个视图的单页应用。要做到这一点AngularJS提供ng-view 和 ng-template指令,以及 $routeProvider 服务。
**总之:**angularjs的MVC是借助于$scope(作用域)实现的!
四、作用域Scope
AngularJS 应用组成如下:
View(视图), 即 HTML。
Model(模型), 当前视图中可用的数据。
Controller(控制器), 即 JavaScript 函数,可以添加或修改属性。
作用域存在层次结构,作用域包含了渲染视图时所需的功能和数据,它是所有视图的唯一源头。可以将作用域理解成视图模型(view model)。
scope 是模型。如果你修改了视图,模型和控制器也会相应更新,$scope
对象在AngularJS中充当数据模型,但与传统的数据模型不一样, $scope并不负责处理和操作数据,它只是视图和HTML之间的桥梁,它是视图和控制器之间的胶水。
1、scope的使用
当你在 AngularJS 创建控制器时,你可以将 $scope 对象当作一个参数传递:
<div ng-app="myApp" ng-controller="myCtrl">
<h1>{{carname}}</h1> //视图中,你不需要添加 $scope 前缀, 只需要添加属性名即可,如: {{carname}}。
</div>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) { //创建控制器时作为参数传入
$scope.carname = "Volvo";
});
</script>
2.神奇的$scope:
(1)实质是一个js对象,有可用的方法和属性。;
(2)是表达式的执行环境(作用域),是应用在 HTML (视图) 和 JavaScript (控制器)之间的纽带。;
(3)是一个树形结构,与DOM标签平行,子层标签可以继承父标签的属性和方法;
(4)每个angular应用只有一个根
scope对象,一般位于ng−app上;(5)
scope可以向上或向下传播事件;
(6)$scope不仅是mvc的基础,更是实现双向数据绑定的基础;如果你修改了视图,模型和控制器也会相应更新:
可以用angular.element($0).scope()进行调试,获取当前元素上的scope是什么!
3、作用域
作用域有以下的基本功能:
提供观察者以监视数据模型的变化;
可以将数据模型的变化通知给整个应用,甚至是系统外的组件;
可以进行嵌套,隔离业务功能和数据;
给表达式提供运算时所需的执行环境。
开发AngularJS应用的大部分工作内容,就是构建作用域及其相关的功能
(1)根作用域
AngularJS启动并生成视图时,会将根ng-app元素同$rootScope
进行绑定。
rootScope是所有
scope对象的最上层。
$rootScope 可作用于整个应用中。是各个 controller 中 scope 的桥梁。用 rootscope 定义的值,可以在各个 controller 中使用。
angular.module('myApp', [])
.run(function($rootScope) {
$rootScope.name = "World";
});
(2)嵌套作用域
我们可以不将变量设置在$rootScope上
,而是用控制器显式创建一个隔离的子$scope对象,
把它设置到这个子对象上。使用ng-controller指令可以将一个控制器对象附加到DOM元素上,
如下所示:我们可以创建一个控制器来管理与其相关的变量,而不用将name变量直接放在$rootScope上:
<div ng-app="myApp">
<div ng-controller="MyController">
<h1>Hello {{ name }}</h1>
</div>
</div>
ng-controller指令为这个DOM元素创建了一个新的$scope
对象,并将它嵌套在$rootScope中。
angular.module("myApp", [])
.controller('MyController',
function($scope) {
$scope.name = "Ari";
});
4、生命周期
scope对象的生命周期处理有四个不同阶段。(1)创建在创建控制器或指令时,AngularJS会用
injector创建一个新的作用域,并在这个新建的控制器或指令运行时将作用域传递进去。
(2)链接
当Angular开始运行时,所有的$scope
对象都会附加或者链接到视图中。所有创建
scope对象的函数也会将自身附加到视图中。这些作用域将会注册当Angular应用上下文中发生变化时需要运行的函数。这些函数被称为
watch函数, Angular通过这些函数获知何时启动事件循环。
(3)更新
当事件循环运行时,它通常执行在顶层$scope
对象上(被称作$rootScope
),每个子作用域都执行自己的脏值检测(ng-model 指令为应用数据提供状态$dirty
,值改变时为true)。每个监控函数都会检查变化。如果检测到任意变化,
scope对象就会触发指定的回调函数(4)销毁当一个
scope在视图中不再需要时,这个作用域将会清理和销毁自己。
尽管永远不会需要清理作用域(因为Angular会为你处理),但是知道是谁创建了这个作用域还是有用的,因为你可以使用这个$scope
上叫做$destory()的方法来清理这个作用域。
5、指令和作用域
指令在AngularJS中被广泛使用,指令通常不会创建自己的$scope,但也有例外。比如ng-controller和ng-repeat指令会创建自己的子作用域并将它们附加到DOM元素上。