angular中controller在不同场景下的状态

来源:互联网 发布:linux服务器优点 编辑:程序博客网 时间:2024/06/05 18:43
  • 一个controller写两遍会发生什么
  • 都是controller,Directive和DOM谁先跑
  • Directive link里的controller究竟是什么

angular的设计者提供了一种很独特的编码方式,让DOM变得更友好了。以前的DOM(至少我觉得)就是一堆堆起来的元素,光看DOM看不出来这页面到底要干嘛(超神的程序员除外),angular的DOM像是一篇能直接阅读的文章,他把方法直接写在了DOM里面,如果英语好的话(当然代码的编辑者也得有合适的命名规范),一个不懂前端的人估计都能直接读懂这个页面要实现的功能。
我认为提供这种能力最直观的就是controller(虽然真正干活的是以compileprovider,不过他们一般在幕后),而controller在不同使用场景下,也会有不一样的表现。
这次我就分享一些我在controller学习使用中碰到的一些有意思的事情。

一个controller写两遍会发生什么?

controller在DOM中的情况挺简单的,不过也有一些有意思的地方,比如controller的属性究竟能在多大范围内起作用?

比如下面这段代码:通过改变输入框内的值改变页面上显示的名字,和统计次数的文字。
当然这个功能不用controller也可以实现,不过居然是说明controller的用途,就这么写了,controller在这里有两个方法:
init:初始化数据
saying:把输入值整理成一串字符串

HTML 绑定

<div ng-init="init()" ng-controller="hello">            <p>Name : <input type="text" ng-model="name"></p>            <h1>Hello <span ng-bind="name | uppercase"></span></h1>            <input type="text" ng-model="number">            <h2 ng-bind='saying()'></h2></div>          

JS定义

var app=angular.module('tutorial',[]);app.controller('hello',function($scope){    $scope.init=function(){        $scope.name='Wang';        $scope.number=1;    }    $scope.saying=function(){        var time=parseInt($scope.number)+1;        return 'Mr.'+$scope.name+',this is your '+time+' time to here';    }});

在html中绑定controller的逻辑基本就是这样:
DOM 通过ng-controller指定能操作自己的controller,
controller通过$scope将controller中定义的各种方法映射到DOM元素上(当然还得借助一大堆的ng兄弟去实现具体的工作)。

Created with Raphaël 2.1.0DOMDOMJSJSng-controller:init(),saying()$scope对象的方法属性:$scope:name,number,init,saying

看着貌似挺简单,而且也很符合angular增强HTML功能的设计初衷。

不过下面我们来考虑一个有意思的问题
在同一个controller绑定在两个DOM元素上的时候,会发生什么?

<div ng-init="init()" ng-controller="hello">            <p>Name : <input type="text" ng-model="name"></p>            <h1>Hello <span ng-bind="name | uppercase"></span></h1>            <input type="text" ng-model="number">            <h2 ng-bind='saying()'></h2></div>  //和上面一模一样的重复<div ng-init="init()" ng-controller="hello">            <p>Name : <input type="text" ng-model="name"></p>            <h1>Hello <span ng-bind="name | uppercase"></span></h1>            <input type="text" ng-model="number">            <h2 ng-bind='saying()'></h2></div>  

虽然在现实情况下不太可能发生这种复制一遍的情况,但如果存在一个功能超全的controller,包含了很多能复用的方法(如果真有这种controller,你可以考虑把那些方法扔进service里了),那么他们的属性、数据会共享吗?比如在这种情况下,修改其中任意一个输入框的值,会有几个地方同步改变呢?

重复controller

只会有一个改变,对于一个controller而言,不管他在多少个DOM上被绑定, 他的作用域(生命周期、或者别的什么,叫什么都行)只在这一块内,两块controller之间没有联系(就算都是修改scope scope只是看着一样),改变其中的一个对另一个不会有影响

改变其中的一个对另一个不会有影响,这话是不是听着特别耳熟(好像在面向对象里面见过)。没错,虽然你看着只是写了一遍controller,但其实angular在使用的时候,还帮你new了一把,所以他们是彼此独立的两个对象。

如果你把代码改成这样,你能看到控制台输出两遍start。

app.controller('hello',function($scope){    //加个log    console.log('controller start');    $scope.init=function(){        $scope.name='Wang';        $scope.number=1;    }    $scope.saying=function(){        var time=parseInt($scope.number)+1;        return 'Mr.'+$scope.name+',this is your '+time+' time to here';    }}   

都是controller,Directive和DOM谁先跑?

除了直接绑在DOM上,controller还能直接绑在Directive上。
比如下面这串代码:

HTML部分

<foo-directive foo='foo'></foo-directive>

JS部分

app.controller('hello2',function($scope,$http){    console.log('controller2 start');});app.directive("fooDirective", ['$http',function($http) {    return {        controller:'hello2',        template : "<h1>自定义指令!</h1>"    };}]);

页面上的效果就是打出一段自定义指定的文字。
hello2这个controller在这里可以说完全没有任何功能,可是已经足够说明问题,除了ng-controller以为,angular还能通过directive的controller属性去绑定controller,这个controller不用显式地写在页面上(看着好像有点违背angular的设计初衷,其实人家一开始的设计就是Directive为主,controller为辅的,只是我们一般都从controller开始学而已)。
同样,在这个Directive出现在页面上的时候,这个controller也会被new一份。
这种绑定在Directive上的controller还有一个特色,如果你把这个Directive和之前绑在DOM上的controller写在一个页面上,绑定DOM的controller和绑定Directive的controller谁会先被new?无论这两个DOM元素的先后顺序是什么样的,Directive里的controller都会先跑。
这其实涉及到angular的一个核心部件$compile,以后有机会再细说,这里就先知道Directive里的controller有执行的优先权就行(虽然貌似没啥大用,不过一些异步方案和未来的多线程可以优先放在Directive里)。

Directive link里的controller究竟是什么?

除了写在DOM里的ng-controller和写在Directive里的controller,还有一个常见的会出现controller的地方:Directive里的link。

//官方的link代码,5个默认参数function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }

link其实是Directive被$compile编译后执行的一个回调方法,一般用在对DOM元素进行改变的工作上。
个人觉得可以把link直接理解为jquery(当然只是功能上),选中一段DOM结构后对其进行操作
一般来说link都被用来注册事件,关联数据什么的,不过这次我们关心的是link的第四个默认参数controller,这个controller和之前绑定在DOM或者Directive上的controller有什么关系?

一般来想,这个controller应该拥有绑定的controller(这里是hello2)所有的属性方法吧?

先做一个小实验:
试着打印看看controller究竟有什么。

app.directive("fooDirective", ['$http',function($http) {    return {        controller:'hello2',        link:function(scope, $element, attrs, controllers) {            console.log(controllers);        },        template : "<h1>自定义指令2!</h1>"    };}]);

打印出来一个空对象。。。hello2虽然没有什么功能可也不至于是个空对象吧,看来和我们理解的不太一样,这个controller不是hello2?那他是谁,来做什么?
在官方的解释中,这个controller貌似是个很强大的存在:

controller - the directive’s required controller instance(s) - Instances are shared among all directives, which allows the directives to use the controllers as a communication channel. The exact value depends on the directive’s require property
这么强大的说辞是个空是不是有点说不过去?等会儿,我们好像漏看了一些关键词:instance,controller as a communication channel。
angular的意思难道是要把controller的实例对象本身作为一个传递中介?
既然是实例对象,我把hello2稍微改动了一下:

app.controller('hello2',function($scope,$http,wiki){    console.log('controller2 start');    $scope.hello=function(a){        var addd=a;        console.log(addd);    }    return {        hello3:function(a){            alert(a);        }    }});

然后再跑一遍代码:
控制台打出来hello3 function,好激动啊,原来实例指的是这个。那么在实际使用中,我们就可以用controller通过$scope取得不同DOM的值,然后返回给Directive,这是不是就是所谓的communication channel?

居然是communication channel,而且官方的解释还是controllers,那是不是意味着可以传多个controller进去,实现不同controller直接的交互?
果断试试这个,require注入另一个controller hello(require要注入多个controller第一位必须得是自身)

app.directive("fooDirective", ['$http',function($http) {    return {        controller:'hello2',        require:['fooDirective','hello'],        link:function(scope, $element, attrs, controllers) {            console.log(controllers);        },        template : "<h1>自定义指令2!</h1>"    };}]);

果断报错,controller不能解析。

其实当然不能解析,hello这个controller还没有实例化怎么可能解析,而且官方也说这是用在Directive直接的controller信息传递,也就是说是用在Directive中实例化的controller上。
那么是不是可以改成这样?

//一个新的directive<runoob-directive aaa='aaa'>            <foo-directive foo='foo'></foo-directive></runoob-directive>
app.controller('hello',function($scope){    console.log('controller3 start');    return {        hello1:function(a){            alert(a);        }    }});app.directive("runoobDirective", ['$http',function($http) {    return {        controller:'hello',        link:function(scope, $element, attrs, controllers) {            console.log(controllers);        }    };}]);app.directive("fooDirective", ['$http',function($http) {    return {        controller:'hello2',        require:['fooDirective','^runoobDirective'],        link:function(scope, $element, attrs, controllers) {            console.log(controllers);        },        template : "<h1>自定义指令2!</h1>"    };}]);

新建了一个Directive,然后很开心的看到控制台打出来一个数组,两个实例化的function,分别来自hello和hello2.而且如果这时候你试试查看scope的话,你会很神奇的发现他们的$id是一样的,也就是说这么写能让两个Directive共享一次compile,感觉这在需要大量数据绑定的地方是一个可以很节约性能的功能。

另外,在Directive中注入多个controller还能让他们彼此继承对方的方法属性(就行call和apply),配合watch用在多个controller通信中感觉很有前途。

0 0
原创粉丝点击