1、Angular是一款优秀的前端js框架,用它可以轻松构建SPA(Single Page Application,单页面应用程序) ,它的特征是:具有mvc的架构模式,便于模块化开发,自动化双向数据绑定和特有的指令系统。它最大限度的解放了Dom操作,让js代码更专注于业务逻辑的实现,通过简单的指令,结合页面结构和逻辑数据,通过自定义指令,实现组件化编程,代码结构更合理,维护成本更低。
2、Angular的应用完全没有自己写dom操作的地方。通过一些ng指令来完成事件绑定以及赋值,当我们写网页或者其他Angular应用时应注意的就是程序逻辑的地方,而不用去关注dom操作,减少了大量的代码量。这更符合面向对象编程,让框架去处理dom而我们只去处理逻辑。
3、如何使用Angular
在HTML代码中引入Angular.js包
在HTML代码中将刚刚定义的模块通过ng-app="yourModuleName"指令的方式作用到一个特定的元素上
在JS代码中通过angular.module('yourModuleName', []) 注册一个模块
根据当前页面的情况(业务块)划分控制器
在HTML代码中将刚刚定义的控制器通过ng-controller="ControllerName"作用到特定的元素上
建模(根据界面原型抽象一个数据模型)得到一个视图模型(ViewModel)
在JS代码中通过$scope暴露需要提供到页面的数据成员
在HTML代码中将刚刚暴露出来的数据通过类似ng-model/{{}}/ng-click之类的指令绑定到特定的元素上
在JS中完成业务逻辑
4、ng中是怎么样处理指令的
当浏览器渲染一个页面时,本质上是读html标识,然后建立dom节点,当dom树创建完毕之后广播一个事件给我们.
当你在页面中使用script标签加载ng应用程序代码时,ng监听上面的dom完成事件,查找带有ng-app属性的元素.
当找到这样的元素之后,ng开始处理dom以这个元素的起点,所以假如ng-app被添加到html元素上,则ng就会从html元素开始处理dom.
从这个起点开始,ng开始递归查找所有子元素里面,符合应用程序里定义好的指令规则.
5、angular程序的控制器必须在ng-app的范围之内,否则失效。
ng-model是用于表单的模型,用于代替表单的值,用作和程序之间交流的模型,可以进行双向的数据绑定 。
{{}}表达式只能够单向的绑定数据。实现从逻辑层到视图层的单向绑定,呈现数据。
6、service
在angular中,service都是单例的,几乎每一个学习的教程中都会提到这一点,
并加以强调。单例的意思是说,在整个的应用中,每一个service对象只会被实
例化一次,其他地方对这个对象的操作,会影响到全局。
首先,有个东西需要说一下。以往在Js中,我们写了大量的代码来存取变量值,在angular中我们找到了一个比较好的
方法。然后,刚开始写angular的时候,我们容易把数据放的乱七八糟,有时候各种$scope, 各种域, 各种controller
的问题,这样其实就失去了angular的优势,angular的设计中 controller层是很薄的,一些业务逻辑的处理和数据持
久化的问题需要放到service中。出于内存的考虑,controller只有在需要的时候才会被初始化,不需要的的时候就会
被抛弃。所以说,当你刷新页面时,controller就会被清空。而service就可以用来保存应用数据,并且可以在不同的
域中被使用。
(1)用Factory就是创建一个对象,添加了一定的属性之后,把它返回回来。你把service传进controller之后,在
controller里这个对象里的属性就可以通过factory使用了。
app.controller('myFactoryctrl', function($scope, myfactory){
$scope.artist = myFactory.getArtist();
});
app.factory('myfactory', fuction(){
var _artist = '';
var service = {};
service.getArtist = fuction(){
return _artist;
}
return service;
});
(2) service 是用‘new’ 关键字实例化的。因此,你应该给‘this’添加属性,然后 service返回‘this’,把service传进cont
roller之后,在controller里的‘this’ 上的属性就可以通过service来使用了。
app.controller('myServicectrl',function($scope, myService){
$scope.artist =myService.getArtist();
});
app.service('myService', function(){
var _artist = 'Nelly';
this.getArtist = function(){
return _artist;
}
});
(3)provider 是唯一一种可以传进config()函数的service。当你想要在service对象启用之前,先进行模块范围的配
置,那就应该使用provider.
app.controller('myProviderCtrl', function($scope, myProvider){
$scope.artist = myProvider.getArtist();
$scope.data.thingFromConfig = myProvider.thingOnConfig;
});
app.provider('myProvider', function(){
this._artist = '';
this.thingFromConfig = '' ;
this,$get = function(){
var that = this;
return {
getArtist:function(){
return that._artist;
}
thingOnConfig: that.thingFromConfig
}
}
});
app.config(function(myProviderProvider){
myProviderProvider.thingFromConfig = 'this was set in config()';
});
最后,强调一点,当我们需要在不同的域中共享数据的时候,我们最好用 service来实现.
8、关于directive里的link和controller区别?
(1)、执行顺序:先controller后link
(2)、何时使用controller:一般场景下都不想要使用controller,只需要把逻辑写在link中就可以了;用controller的场景就是该指令(假设为a)会被其他指令(假设为b)require的时候,这样就会在b指令的link函数中传入这个controller(如果require多个的话,传入的是一个数组,数组中存放的是每一个require的指令对应的controller),目的很显然是为了指令间进行交流的。
9、关于非directive的scope能否用link?
在指令中才存在调用link的时候,也就是说link是该指令在compile之后和scope进行绑定的时候调用的。
那只有在指令定义的那个地方用到link了,其他地方也可以用,例如做弹出框的时候就需要,拿到模板tpl,然后调用var linkFn = $compile(angular.element(tpl));
此时返回的就是一个link的函数,然后linkFn(scope)
,这里的scope是你需要指定的scope,可以是新创建的,也可以是已经存在的。
10、为什么编译的过程要分成compile和link?
简单的说就是为了解决性能问题,特别是那种model变化会影响dom结构变化的,而变化的结构还会有新的scope绑定及事件绑定,比如ng-repeat
compile
和link
的形式
compile
function compile(element, attrs, transclude) { ... }
- 在compile阶段要执行的函数,返回的function就是link时要执行的function
- 常用参数为
element
和attrs
,分别是dom元素和元素上的属性们,其它的以后细说 - 较少使用,因为大部分directive是处理dom元素的行为绑定,而不是改变它们
link
function link(scope, element, attrs, controller) { ... }
- 在link阶段要执行的函数,这个属性只有当compile属性没有设置时才生效
- 常用参数为
scope
,element
和attrs
,分别是当前元素所在的scope,dom元素和元素上的属性们,其它的以后细说 - directive基本上都会有此函数,可以注册事件,并与scope相绑
compile
和link
的使用时机
compile
- 想在dom渲染前对它进行变形,并且不需要scope参数
- 想在所有相同directive里共享某些方法,这时应该定义在compile里,性能会比较好
- 返回值就是link的function,这时就是共同使用的时候
link
- 对特定的元素注册事件
- 需要用到scope参数来实现dom元素的一些行为
11、作用域和控制器
控制器是通过增强作用域来提供业务逻辑的代码。这是我们在作用域这个角度为它下的一个定义。注册的控制器在
ng-controller指令连接到AngularJS模板时被实例化的。每个控制器都会有一个自己的单独的子作用域,至于在自定
义指令中的$scope,是与控制器作用域同级的,和控制器作用域并没有任何的隶属关系。
12、作用域和模板
模板是用来为Angular提供视图的。在模板中定义AngularJS指令时,可以使用作用域属性和函数。在模板的表达式中
也可以引用作用域中的属性。
13、作用域生命周期
创建->监视器注册->模型变化->变化观察->作用域销毁
创建阶段发生在作用域初始化时。启动应用时将创建一个根作用域,当遇到ng-controller或者ng-repeat指令时,控制
器或者指令链接到模板时,将会创建子作用域。在创建阶段,将会创建一个digest循环,用于与浏览器时间循环交
互。该digest循环负责使用模型的变化更新DOM元素,并执行所有已经注册的监视器函数。如果需要手动的执行dig
est循环,可以通过执行$digest()方法实现. 例如,下面的代码将执行所有的异步改动,然后执行作用域中的监视函
数。
$scope.$digest()
我们可以用$watch()方法在作用域上注册自己的监视函数。该方法接受两个参数,一个是作用域属性的名字,一个
是回调函数。
$scope.$watch('name', function(newValue, oldValue) {
});
3.作用域层次问题
这里有一个来自《Angular Js开发秘籍》的Demo,简单明了。
scopeDemo.js
angular.module('myApp',[])
.controller('LevelA', function($scope){
$scope.title = "LevelA";
$scope.valueA = 1;
$scope.inc = function(){
$scope.valueA++;
};
})
.controller('LevelB', function($scope){
$scope.title = "LevelB";
$scope.valueB = 1;
$scope.inc = function(){
$scope.valueB++;
};
})
.controller('LevelC', function($scope){
$scope.title = "LevelC";
$scope.valueC = 1;
$scope.inc = function(){
$scope.valueC++;
};
});
HTML:
<!doctype html>
<html ng-app="myApp">
<head>
<title>AngularJS Scope</title>
</head>
<body>
<div ng-controller="LevelA">
<h3>{{h3}}</h3>
ValueA = {{valueA}}<input type="button" ng-click="inc()" value="+" />
<div ng-controller="LevelB"><hr>
<h3>{{title}}</h3>
ValueA = {{valueA}}<br>
ValueB = {{valueB}}<br>
<input type="button" ng-click="inc()" value="+" />
<div ng-controller="LevelC"><hr>
<h3>{{title}}</h3>
ValueA = {{valueA}}<br>
ValueB = {{valueB}}<br>
ValueC = {{valueC}}
<input type="button" ng-click="inc()" value="+" />
</div>
</div>
</div>
<script src="js/angular.min.js"></script>
<script src="scopeDemo.js"></script>
</body>
</html>
14、在debugger中查看scope:
(1). 在浏览器中,对着感兴趣的元素点击右键,选择“查看元素”。我们可以看到浏览器debugger高亮了我们选中的元素。
(2). debugger允许我们在console中通过$0变量去访问当前选择的元素。
(3). 想查看关联的scope,我们可以在console中输入:angular.element($0).scope()
- $emit只能向parent controller传递event与data
- $broadcast只能向child controller传递event与data
- $on用于接收event与data
在一个controller里面通过事件触发一个方法,在方法里面通过$broadcast或$emit来定义一个变量,在父,子controller里面通过$on来获取。
parent用$broadcast赋的值,只能子级得到值;$emit赋的值,只能父级得到;而平级的什么都不能得到。
例子:
<!DOCTYPE HTML>
<html lang="zh-cn" ng-app = "myApp">
<head>
<meta charset="UTF-8">
<title>scope-event-propagation</title>
<style type="text/css">
.ng-cloak {
display: none;
}
.ng-scope {
border: 1px dashed red;
}
</style>
</head>
<body class="ng-cloak">
<div ng-controller="MyController">
root scope count:{{count}}
<ul>
<li ng-repeat="i in [1]" ng-controller="MyController">
<button ng-click="$emit('MyEvent')">$emit("MyEvent")</button>
<button ng-click="$broadcast('MyEvent')">$broadcast("MyEvent")</button>
<br/>
middle scope count:{{count}} //平级
<ul>
<li ng-repeat="item in [1,2]" ng-controller="MyController">
Leaf scope count:{{count}}
</li>
</ul>
</li>
</ul>
</div>
</body>
<script src = "js/angular.min.js"></script>
<script src="demo.js">
</script>
</html>
demo.js
angular.module('myApp', [])
.controller("MyController", function ($scope) {
$scope.count = 0;
$scope.$on("MyEvent", function() {
$scope.count++;
});
})
16、我们通过使用angular.element(aDomElement).scope()查看任意DOM元素的scope。
17、模块定义
angular.module("app", []);
angular.module("app").controller("testCtrl", ["$scope", function ($scope) {
$scope.currentMenu = "menu1";
$scope.selectMenu = function (menu) {
$scope.currentMenu = menu;
}
}]);
- 我给ng-app指定了一个名称叫”app“,同时js代码使用
angular.module("app", []);
定义了一个名称为”app“的module,同时用这个app module 的controller方法定义了一个testCtrl;定义module函数是angular对象上的静态方法,第一个参数传名称,第二个参数是一个数组,这个数组表示这个module所引用的其他module,在这个例子中我们没有使用任何其他的module,所以传入一个空的数组,如果第二个参数不传,表示获取名称为”app“的module;