0%

generator简单说是一种可以控制函数内部执行状态的函数

funtion* 定义了generator函数的语法

yield定义了函数内部实现不同的状态

demo1

1
2
3
4
5
6
7
function* build(){
yield 'step1';
return 'done';
}
var generator = build();
generator.next(); //{value:'step1',done:false}
generator.next(); //{value:'done',done:true}

这里前者是函数体yield后面的值,后者代表函数是否完成

加入函数体内不包含任意一个yield,则函数只是一个延迟执行的函数而已

1
2
generator.next(); //{value:done,done:true}
generator.next(); //{value:undefined,done:true}

yield表达式还可以返回值在下一个next里调用,返回值只能通过next函数传参进来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function* build(){
yield 'step1';
var step = yield 'step2';
if(step == 3){
yield 'step3';
}else{
yield 'step4';
}
yield 'step5';
return '执行完毕!';
}
var generator = build();
console.log(generator.next(3)); // => {value: 'step1', done: false}
console.log(generator.next(3)); // => {value: 'step2', done: false}
console.log(generator.next(3)); // => {value: 'step3', done: false}
console.log(generator.next(3)); // => {value: 'step5', done: false}
console.log(generator.next(3)); // => {value: '执行完毕', done: true}

注意: next传递的参数值是传递给上一次yield表达式,所以上面例子里第三次调用next传递参数其实是把值传递给第二个yield表达式

异常处理

1
2
3
4
5
6
7
8
9
10
function* build(){
try{
throw new Error('from build');
yield 'haha';
}catch(err){
console.log('inside error'+ err);
}
}
var it = build();
it.next() // => inside error: Error: from build!

上面的例子就是在generator里面增加try…catch语句来捕获函数内部的异常,然后generator本身也提供了一个方法用于向外抛出异常

throw 此方法可以向外抛出异常,由外部的try…catch来捕获

1
2
3
4
5
6
7
8
9
10
11
12
13
function * build(){
try{
yield 'haha'; //没有抛出异常
}catch(err){
console.log('inside error: ' + err);
}
}
var it = build(); //外部的try...catch进行捕获
try{
it.throw('from it');
}catch(err){
console.log('out error: ' + err); // => out error from it
}

yield* 后面跟上别的generator实例就可以遍历别的generator里的yield了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function* build(){
var a = yield 1;
var b = yield 2;
var c = yield* child();
console.log(a, b, c);
}
function* child(){
var x = yield 3;
var y = yield 4;
console.log(x, y);
return 'child';
}

var it = build();
it.next();
it.next('a');
it.next('b');
it.next('c');
it.next('d');
// => c d
// => a b child

当调用$digest的时候,只触发当前作用域和它的子作用域上的监控,但是当调用$apply的时候,会触发作用域树上的所有监控

1
2
3
$scope.$watch("arr", function(newValue) {
alert("deep:" + newValue.join(","));
}, true);

注意,这里我们在$watch函数中,添加了第三个参数,这个参数用于指示对数据的深层监控,包括数组的子元素和对象的属性等等,本例如果没有第三个参数只会监听整个arr数组的变化,而不会监听到数组内部的子元素的变化。

1
2
3
4
angular.element(document).ready(function(){
angular.bootstrap(document.getElementById('app1'),['test']);
angular.bootstrap(document.getElementById('app2'),['test']);
})
1
2
3
4
5
6
<div id="app1" ng-controller='testctrl'>
{{x}}
</div>
<div id="app2" ng-controller='testctrl'>
{{x}}
</div>

这样在同一个页面创建同一个module的不同实例,互不干涉

初识Angular2

标签:ES6 Class 组件化

import是ES6的关键字,从模块中引入类型定义;

实现一个angular2组件,需要定义一个类,然后给这个类添加注解

1
2
3
[@Component](/user/Component)({selector:'my-app'})
[@View](/user/View)({template:'<h1>hello world</h1>'})
class MyApp{}

class是ES6的关键字,用来定义一个类,而@Component和@View是附加给类的信息,称为注解。前者通过selector属性指定要渲染的Dom对象,后者为指定渲染的模板。

bootstrap(MyApp) 这个函数通知angular2将MyApp组件渲染到Dom树上。它在angular1.x中是围绕dom元素展开的,而在angular2.x中是围绕组件开始的,如果没有组件,也就无法启动angular2

显示数据

属性绑定机制把数据显示到用户界面上

使用插值表达式显示组件属性,与angular1.x一样,把属性名包裹在双花括号里面放进视图模板,如.

模板是包在es6反引号中的一个多行字符串,使html模板更容易阅读。

组件模板也可以由两种选择,分别是内联模板和模板文件,templateUrl属性,在组件元数据中把他链接到组件。

初始化组件可以有变量赋值和使用构造函数声明和初始化两种,下面分别为demo

1
2
3
export class AppComponent{
title : string = 'angular2'
}

使用构造函数初始化

1
2
3
4
5
6
export class AppComponent{
title:string;
constructor(){
this.title = 'angular2';
}
}

使用NgFor显示数组属性,*ngFor是angular的迭代指令,他将元素及其子级标记为迭代模板;

使用NgIf进行条件显示,它会根据一个布尔条件来显示或移除一个元素。

依赖注入

依赖注入是重要的设计模式,angular离开了他,几乎没法构建angular应用,它使用的很广泛,简称为DI。

@Injectable()装饰器

注册提供商

angular directive

语义化标签,复用、封装,将逻辑与dom结合在一起,即插即用,指令化即模块化与通用化

demo1 (以下全部简写)

1
<hello-world></hello-world>
1
2
3
4
5
6
7
8
var app = angular.moudle('app',[])
.directive('helloWorld',function(){
return {
restrict: 'ECMA',
replace : true,
template : '<div>hello world</div>'
}
})


这里的ECMA是声明格式,默认为EA,其分别为

E - 元素名称: <my-directive></my-directive>

A - 属性: <div my-directive></div>

C - 类名: <div class='my-directive'></div>

M - 注释: <!--directive:my-directive exp--> (很少使用,exp代表一个空格,必须存在,不然没效果)

这里的一个小坑是驼峰命名,指令若是驼峰命名,视图需写成上述样式,中间用下划线相连

replace, 是否替换掉自定义的指令, 默认是false

template是字符串模板,一般情况下都是templateUrl,毕竟代码量大了成了一坨;



他还有一个属性transclude,transclude字面意思就是嵌入,需要将你的指令内部的元素(注意不是指令的模板)嵌入到你的模板中,也就是说将它从DOM元素中获取的内容放到它发现ng-transclude 指令的地方。transclude默认为false,如果设置为true,那么就需要配合ng-transclude指令来进行使用,例如上一个例子需要在模板里面写入

1
template: '<div>hello world <span ng-transclude></span></div>'

对应的html变为

1
2
3
4
<hello-world>
<span>i'm here</span>
<span>i'm here too</span>
</hello-world>

这个属性可以理解为把hello-world标签换成我们所编写的html模板,但是标签内部内容不变。

对应视图则变为“hello world i’m here i’m here too”



绑定策略

scope:{} 代表了独立作用域;

本地作用域属性:使用@符号将本地作用域同Dom属性的值进行绑定,指令内部作用域可以使用外部作用域的变量

双向绑定:通过=可以将本地作用域上的属性同父级作用域上的属性进行双向的数据绑定。就像普通的数据绑定一样,本地属性也会反映出父数据模型上所发生的改变

父级作用域绑定: 使用&符号可以对父级作用域进行绑定,以便在其中运行函数。这就意味着这个值进行设置时会生成一个指向父级作用域的包装函数,下面请看分别的例子


1
2
3
4
5
6
7
8
9
.directive("direct",function(){
return{
restrict: 'ECMA',
template: '<div>指令中:{{ name }}</div>',
scope:{
name:'@forName'
}
}
})
1
2
<direct for-name="{{ Name }}"></direct>
<input ng-model='Name'>
1
2
3
.controller('ctrl',function($scope){
$scope.Name = '';
})

这样成功地与父控制器中的Name绑定起来了,也可以写成name:‘@’,这样就默认属性名为name了即



1
2
3
4
5
6
7
8
9
.directive("direct",function(){
return{
restrict: 'ECMA',
template: '<div>指令中:<input ng-model="Name"/></div>',
scope:{
Name:'=forName'
}
}
})
1
2
<direct for-name="Name"></direct>
<input ng-model='Name'>

这样就实现了双向数据的绑定



最后是&它的含义是对父级作用域进行绑定,并将其中的属性包装成一个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.directive("direct",function(){ 
return{
restrict: 'ECMA',
template: '<div><input ng-model="model"/>\
<button ng-click="show({name:model})">show</button></div>',
scope:{
show:'&'
}
}
})
.controller("nameController",function($scope){
$scope.showName=function(name){
alert(name);
}
});
1
<direct show="showName(name)"></direct>

这个例子中,通过模板中的ng-click触发了show函数并将一个叫做model的对象作为name参数传递了进去,而在html中,我们把show的属性值设为showName(name)。



为什么Angular要为我们提供这样一套绑定策略呢?就是因为它想让我们在为指令创建隔离作用域的同时,还能访问到父级中的属性,这就像,你在隔离作用域身上打了一个洞,然后用一条管道,把指令内部和外界的属性给连起来(绑定),并且一切的通信都只能通过这条管道来实行



compile阶段进行标签解析和变换,link阶段进行数据绑定等操作

在编译的阶段,angularJs会遍历整个的文档并根据JavaScript中指令定义来处理页面上什么的指令。在遍历的过程中,有可能一层套着一层,一直延深处遍历。一但遍历和编译完毕就会返回一个叫做模板函数的函数。在这个函数没被返回(return)之前我们可以对编译后的DOM树进行修改。通常情况下,如果设置了compile函数,说明我们希望在指令和实时数据被放到DOM中之前进行DOM操作,在这个函数中进行诸如添加和删除节点等DOM操作是安全的。



1
2
3
<hello autohello=5>
<p>小平你好</p>
</hello>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
app.directive('hello',function(){
return {
restrict:'EA',
compile : function(ele,attrs){
var tpl = ele.children().clone();
for(var i=0;i<attrs.autohello -1;i++){
ele.append(tpl.clone())
};
return function(scope,ele,attrs,ctrl){

}
}
}
})



我们写了一个DIV标签,定义了一个autohello的一个属性,属性值5.我们想把 “小平你好的这几句话”输出5遍。我们定义个一个compile 的函数,里面进行了一些DOM操作。在最后return一个闭包函数就是link函数,如果同时设置这两个选项,会将compile返回的函数当做链接函数,而link函数不起作用



compile与link函数的区别:compile函数的作用是对指令的模板进行转换,而link函数是在模型和视图之间建立关联,包括事件监听,即scope之后再链接阶段才能绑定到元素上,对于同一个指令的多个实例,compile只会执行一次,而link对于指令的每次实例都会执行一次,一般情况下,compile函数用的不多,只要编写link函数就够了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var expanderModule=angular.module('expanderModule', [])
expanderModule.directive('expander', function() {
return {
restrict : 'EA',
replace : true,
transclude : true,
scope : {
title : '=expanderTitle'
},
template : '<div>'
+ '<div class="title" ng-click="toggle()">{{title}}</div>'
+ '<div class="body" ng-show="showMe" ng-transclude></div>'
+ '</div>',
link : function(scope, element, attrs) {
scope.showMe = false;
scope.toggle = function toggle() {s
scope.showMe = !scope.showMe;
}
}
}
});
1
2
3
4
expanderModule.controller('SomeController',function($scope) {
$scope.title = '点击展开';
$scope.text = '这里是内部的内容。';
});

html代码

1
2
3
4
5
6
7
8
9
<html ng-app='expanderModule'>
<body>
<div ng-controller='SomeController'>
<expander class='expander' expander-title='title'>
{{text}}
</expander>
</div>
</body>
</html>


经常使用的一个指令例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
home.directive('compileNav',['$http','$compile',function($http,$compile){
return{
restrict: 'EA',
replace: true,
controller: 'homeController',
link : function(scope,element,attrs,ctrl){
scope.$watch(function(){
return ctrl.mainservice.NavSrc; //进行监听,若有模板存在,则通过$http进行加载,
},function(newVal){
if(newVal){
$http.get(newVal).then(function(response){
$('.main-menu').html($compile(response.data)(scope));
});
}
});
}
};
}]);

重点:angular核心之一service

Service

自定义或内建服务,它是一个函数或对象,

$http服务

1
2
3
4
5
app.controller('ctrl',function($scope,$http){
$http.get('post/json').then(function(data){
$scope.name = data.name
})
})


创建自定义服务,五种方法,constant,value,factory,service,provider,一般情况下factory与service使用较多,angular内置服务都是以$开头,所以自定义的服务应该避免以$开头,造成混乱。

所有类型的service都是单体,单体是什么?单体是一种设计模式,它限制了每一个类仅能够实例化为一个对象,无论我们在什么地方注入,将永远使用同一个实例,换句话就是,如果我们在一个地方改变了其中的某个值,其他地方也会发生相应的改变;
providerToservice

constant

1
2
3
app.constant('myconstant',{
config1 : 'defaultValue'
})
  • constant是用来在指令中提供默认配置,放入的值不会改变;


value

1
2
3
app.value('myvalue',{
config2: 'changeValue'
})
  • value经常用来保存值,数值是可以改变的,这是和constant最大区别,但是不能对其中的值进行计算


provider

1
2
3
4
5
app.provider('myprovider',function(){
this.$get = function(){

}
})
  • 通过provider方法创建的服务一定要包含$get方法,否则会报错,provider注入的结果就是$get返回的结果,并且在三种创建服务的方法中,只有provider方法创建的服务可以传进config函数,以用于在对象启动前,对模块进行配置,但是在config中进行配置的只能在是$get函数之外定义的变量,注入config函数时,参数名必须是 服务名+Provider,例如下面代码只有color能被访问到,getcolor却不能
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    app.provider('myprovider',function(){
    this.color = '';
    this.$get = function(){
    var that = this;
    return {
    getcolor : function(){
    return that.color
    }
    }
    }
    })
    1
    2
    3
    app.config(function(myproviderProvider){
    myproviderProvider.color = 'red';
    })


factory

1
2
3
4
app.factory('myfactory',function(){
var factory = {};
return factory;
})
  • 通过factory创建的服务必须有返回值,即return函数,factory方法注入的结果即是return返回的结果,可以使用注入对象的定义的方法,例如
    1
    2
    3
    4
    5
    6
    7
    	var factory = {};
    var _name = 'lee';
    factory.getname = function(){
    return _name;
    };
    return factory;
    })


service

1
2
3
app.service('myservice',function(){
this.saysomething = function(){}
})
  • 通过service方法创建的服务可以不用返回任何值,因为service本身返回一个构造器,系统会用new关键字来创建一个对象,所以我们可以在service内部使用this来进行扩展。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    app.service('myservice',function(){
    this.name = '';
    this.getname = function(){
    return this.name;
    }
    this.setname = function(name){
    this.name = name;
    }
    })



上面的代码也可以用以下方式表示

1
2
3
4
5
6
7
8
9
10
11
12
app.factory('myservice',function(){
return new Foobar();
})
function Foobar(){
this.name = '';
this.getname = function(){
return this.name
}
this.setname = function(name){
this.name = name
}
}

Foobar是一个’类’,如果已经有个一个类,诸如Foobar,只需要注入进服务

1
app.service('myservice',Foobar);


官方小例子:

1
2
3
4
5
app.service('lee',function(){
this.myfunc = function(x){
return x.tostring(16)
}
});
1
2
3
app.controller('ctrl',function($scope,lee){
$scope.hex = lee.myfunc(255);
})
1
2
3
<div ng-controller="ctrl">
<div>{{hex}}</div>
</div>


项目中的服务例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
angular.module('clientModule',['home'])
.factory('clientService',['$http','$q','mainservice',function($http,$q,mainservice){
//$q是angular内置服务,可以使你异步地执行函数
var communicateIp = 'http://10.0.1.8:8010/';
mainservice.ip = communicateIp;
function handleRequest(method,url,data){ //定义一个接口管理的方法
var deferred = $q.defer();
// defer的字面意思是延迟,$q.defer(),可以创建一个deferred实例。
var config = {
method : method,
url : url
};
if('POST'===method){
config.data = data;
}else if('GET'=== method){
config.params = data;
}else if('DELETE'=== method){
config.params = data;
}
$http(config).success(function(data){
deferred.resolve(data);
}).error(function(err){
deferred.reject(err);
});
return deferred.promise;
//通过deferred延迟对象,可以得到一个承诺promise,而promise会返回当前任务的完成结果
};
return {
exit: function(params){
return handleRequest('GET', communicateIp+'api/pre/demo/loginout', params);
},
changepersonalPassword: function(params){
return handleRequest('GET', communicateIp+'api/pre/demo/updatePassword', params);
},
checkuser: function(params){
return handleRequest('GET', communicateIp +'api/pre/demo/login',params);
}
};
}]);