资讯专栏INFORMATION COLUMN

Controller As in Angularjs

mo0n1andin / 796人阅读

摘要:因为在里面是基于原型进行继承的。事实上注入后,即提供了一个,可以在这个上面绑定属性和方法。当使用语法的时候,事实上是绑定到了的对象上面。注意这个地方执行顺序是从子元素开始再到父元素的。即在执行阶段前确保执行完毕。

Controller As
    angular
        .module("app", [])
        .controller("DemoController", DemoController);
        
    function DemoController() {
        this.name = "XL";
        this.sayName = function() {
            return this.name;
        }
    }
    
    
{{vm.name}}

在js部分书写控制器的时候,更像是在写构造函数,然后在view层实例化一个ViewModule。那么在view里面可以直接调用挂载在这个vm上的属性和方法。
这样的写法的好处就是避免在嵌套的controller里面使用$parent去获取父controller里面的方法或值。因为在angular里面scope是基于原型进行继承的

    
{{name}}
{{name}}
angular .module("app") .controller("parentController", parentController) .controller("childController", childController); //都使用的推断式注入 function parentController($scope) { $scope.name = "XL"; } function childController($scope) { $scope.name = "xl"; }

最后在视图里面输出"XL", "xl"。如果我要获取的是父控制器里面的属性值,那么我只能$scope.$parent去获取,嵌套的controller多了怎么办- -,使用controller as可以有效的避免这个问题.

如果使用这种controller as的写法的话,尽量避免在controller里面使用$scope,除非遇到$emit,$broadcase,$on, $watch

    
    
    function SomeController($scope, $log) {
        var vm = this;
        vm.title = "Some Title";
        
        $scope.$watch("vm.title", function(current, original) {
            $log("vm.title was %s", original);
            $log("vm.title is now %s", current);
        });
    }
this vs $scope
    angular
        .module("app", [])
        .controller("parentController", parentController);
        
    function parentController($scope) {
        this.sayName = function() {
            console.log("this");
        }
    }
    

结果:

点击BtnA后能输出"this",

点击BtnB后不输出任何东西。

事实上:

当书写Controller的时候,即在写controller constructor,使用controller as语法,当在view里面实例化controller后(vm),this便指向这个实例化vm,然后就可以在view里面调用vm所拥有的属性和方法。在本例中即vm所拥有的sayName()方法

如果

    function parentController($scope) {
        this.sayName = function() {
            console.log("this");
        }
        $scope.sayName = function() {
            console.log("$scope");
        }
    }

controller constructor里面定义$scope.sayName方法,那么点击BtnB的时候可以输出$scope

事实上:

注入$scope后,即提供了一个model,可以在这个model上面绑定属性和方法。那么在view里面声明的controller里面可以访问到。

综上,在this上面挂载的方法其实是在controller constructor上面挂载的方法,必须要通过controller实例去访问。在$scope上面挂载的方法是在模型上面挂载的,因为在directive的pre-link阶段(见下面的compile vs link)是将$scope绑定到DOM上,因此可以直接在view层访问绑定在$scope的方法。

除此之外,this$scope另外一个区别就是指向的问题:

    

log "this" and $scope

- parent scope

log "this" and $scope

- child scope

然后仅仅在parentController上面挂载方法:

    $scope.logThisAndScope = function() {
        console.log(this, $scope);
    }

首先因为controller嵌套同时是在$scope上面挂载方法,因此父元素和子元素点击都会输出this$scope的内容,在这个实例当中this的指向是不同的,一个是parentController另外一个是childController$scope的指向是一样的,同时指向的是绑定在id为parentBox的DOM内的$scope。

The problem with controllerAs in Directives

当使用controllerAs语法的时候, controller scope事实上是绑定到了controllerthis对象上面。但是在平时我们书写directive的时候会创建独立的作用域。

    app.directive("someDirective", function() {
        return {
            scope: {
                oneWay: "@",
                twoWay: "=",
                expr: "&"
            }
        }
    })

接下来我们创建一个拥有独立作用域,有自己的控制器的directive

    app.directive("someDirective", function() {
        return {
            scope: {},
            controller: function() {
                this.name = "Pascal";
            },
            controllerAs: "ctrl",
            template: "
{{ctrl.name}}
" } })

但是,如果这个name属性是一个可以和父作用域共享的呢?当然我们立马想到的是

    app.directive("someDirective", function() {
        return {
            scope: {
                name: "="
            },
            ....
        }
    })

如果外部的name属性发生变化并不会立即反应到内部的controllerthis对象上。在1.2版本里面处理这种情况就是使用$scope服务上挂载的$watch方法去监听name属性的变化。

    app.directive("someDirective", function() {
        return {
            scope: {
                name: "="
            },
            controller: function($scope) {
                this.name = "Pascal";
                
                $scope.$watch("name", function(newValue){
                    this.name = newValue;
                }.bind(this));
                //这个地方要注意this的指向
            }
        }
    })
    
bindToController

1.3版本里面directive出现了一个新的配置对象bindToController,顾名思义绑定到controller上面,当directive使用独立作用域以及controllerAs语法的时候,而且bindToController这个值被设置为true的时候,这个组件的属性都被绑定到controller上了而不是scope上面

这意味着,当controller被实例化后,独立作用域上绑定属性的初始值都可以通过this对象来访问到,未来这个属性的值发生变化后都能被检测的到。

app.directive("someDirective", function () {
     return {
        scope: {
             name: "="
        },
        controller: function () {
            this.name = "Pascal";
        },
        controllerAs: "ctrl",
        bindToController: true,
        template: "
{{ctrl.name}}
" }; });

1.4版本的语法升级:
在1.3版本bindToControllerboolen值,在1.4版本中为一个对象,即如果想将独立作用域上的值绑定到controller上面,可以直接在bindToController这个对象上进行配置。

app.directive("someDirective", function () {
    return {
        scope: {},
        bindToController: {
            someObject: "=",
            someString: "@",
            someExpr: "&"
        },
        controller: function () {
            this.name = "Pascal";
        },
        controllerAs: "ctrl",
        template: "
{{ctrl.name}}{{ctrl.someObject}}
" }; }); //如果父作用域里面的someObject属性发生变化,会随时反应到这个directive的template里面。
complie vs link

Angularjs在处理directive时,取决于自身的compilelink参数定义的规则:
当定义directive的时候同时定义了complie,pre-link,post-link3个参数的时候

    
        
            Hello {{XL}}
            
                Hello {{Sugar}}
            
        
    
    var app = angular.module("app", []);
    
    function createDirective(name) {
        return function() {
            return {
                restrict: "E",
                compile: function(tElem, tAttrs) {
                    console.log(name + ": complie");
                },
                return {
                    pre: function(scope, tElem, iAttrs) {
                        console.log(name + ": pre link");
                    },
                    post: function(scope, tElem, iAttrs) {
                        console.log(name + ": post link");
                    }
                }
            }
        }
    }
    
    app.directive("levelOne", createDirective("levelOne"));
    app.directive("levelTwo", createDirective("levelTwo"));
    app.directive("levelThree", createDirective("levelThree"))

结果:

angularjs一开始compile所有原生指令和自定义指令,complile阶段还没有绑定scope.link阶段分为pre-link和post-link阶段

从结果看来compile阶段pre-link阶段的顺序一样,但是post-link执行顺序正好相反。

修改代码:

    function createDirective(name) {
        return function() {
            return {
                restrict: "E",
                compile: function(tElem, tAttrs) {
                    console.log(name + ": complie =>" + tElem.html());
                },
                return {
                    pre: function(scope, iElem, iAttrs) {
                        console.log(name + ": pre link =>" + iElem.html());
                    },
                    post: function(scope, iElem, iAttrs) {
                        console.log(name + ": post link =>" + iElem.html());
                    }
                }
            }
        }
    }

结果:

现在再看下输出的信息,特别是在pre-link阶段,虽然和compile一样输出元素的顺序是一样的,但是元素中出现了属性class="ng-binding",事实上在compile阶段DOM元素仍然是最初html标记所创建的DOM元素,它是模板元素(template element)的实例元素(instance element).pre-link阶段提供一个scope给这个实体,这个实体可以是全新的scope,继承的scope或者是孤立的scope取决于directive定义的scope属性

post-link阶段:当实例元素初始化完成(compile阶段)绑定scope(pre-link阶段)完成后就可以进行post-link(DOM)操作。注意这个地方执行顺序是从子元素开始再到父元素的。即在level-one执行post-link阶段前确保level-two.level-three执行完毕。

    compile阶段:
    /*
    *   @param tElem - 模板元素
    *   @param tAttr - 模板元素的属性
    */
    compile: function(tElem, tAttrs) {
        
    }
    
    pre-link阶段
    /*
    *   @param scope - 连接于此的实例的scope
    *   @param iElem - 实例元素
    *   @param iAttr - 实例元素的属性
    */
    function (scope, iElem, iAttr) {
    
    }
    
    post-link阶段
    /*
    *   @param scope - 连接于此实例的scope   
    *   @param iElem - 实例元素
    *   @param iAttr - 实例元素的属性
    */
    function (scope, iElem, iAttr) {
    
    }
where to use compile or link?

之前的写法都是直接用link,默认进行了compilepre-link的阶段,在post-link里面就可以直接使用绑定在实例上的scope,ele,attrs
如果在你的程序里面不需要使用scope,不需要$watch其他的值,仅仅提供模板实例的话,可以直接使用compile。这个时候你是不能对DOM有任何操作的。
除此之外,如果你不需要实例元iElem,那么也可以不用link函数。

但当你同时书写compilelink函数(pre-link或者post-link)的时候,一定要在compile函数里面返回link函数,因为如果compile被定义的时候link属性被忽略了。

参考资料

bindToController

thisAndScope

the difference between compile and link

angularjs-style

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/78327.html

相关文章

  • Angularjs 开发指南 自定义指令(custom directives)

    摘要:使用开发,很重要的一步是需要开发自定义的指令。接下来分几个步骤记录如何开发一个自定义的指令。对这个元素及其子元素进行变形之类的操作是安全的。链接函数链接函数负责注册事件和更新。说明和是指令的关键部分,留在下一章中详细讨论。 使用Angularjs开发,很重要的一步是需要开发自定义的指令(custom directives)。接下来分几个步骤记录如何开发一个自定义的指令。 目录: 指令...

    calx 评论0 收藏0
  • scope in Angularjs

    摘要:基于原型的继承性视图存在嵌套的时候,内层的是可以继承到外层的数据的。这者的区别就是在中继承了中的作用域。通过监听某个的是否发生变化来触发相应的。第三个属性值,当为规定的是比较对象的值,而不是引用。这样可以非常好的将一些可服用的代码抽离出来。 scope基于原型的继承性 视图存在嵌套的时候,内层的controller是可以继承到外层的controller数据的。 scope在angula...

    cikenerd 评论0 收藏0
  • [译] 通过 Webpack 实现 AngularJS 的延迟加载

    摘要:虽然这些东西都是非常棒的,但是它们都不是实现延迟加载所必需的东西。我们通过的配置对象中的属性就可以实现延迟加载。单元测试的技巧把改成是全局依赖并不意味着你应该从控制器中删除它。因为在单元测试中,你只会加载这一个控制器而非整个应用模块。 原文链接:http://michalzalecki.com/lazy-load-angularjs-with-webpack/ 随着你的单页应用扩大,...

    Zhuxy 评论0 收藏0
  • angularjs利用ui-route异步加载组件

    摘要:异步加载各个组件就很有必要。在这里我就以为框架来进行异步加载说明。而为了将服务进行异步加载我们不能用普通的或者。而需要调用或者如果采用进行编译打包的话就需要的支持,这样可以对进行拆分打包,达到异步加载的目的。 ui-route相比于angularjs的原生视图路由更好地支持了路由嵌套,状态转移等等。随着视图不断增加,打包的js体积也会越来越大,比如我在应用里面用到了wangeditor...

    lunaticf 评论0 收藏0
  • angular性能优化心得

    摘要:本文针对的读者具备性能优化的相关知识雅虎条性能优化原则高性能网站建设指南等拥有实战经验。这种机制能减少浏览器次数,从而提高性能。仅会检查该和它的子,当你确定当前操作仅影响它们时,用可以稍微提升性能。 搬运自: http://atian25.github.io/2014/05/09/angular-performace/ 不知不觉,在项目中用angular已经半年多了,踩了很多坑...

    guqiu 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<