Controller As in Angularjs

Controller As

    angular
        .module('app', [])
        .controller('DemoController', DemoController);
        
    function DemoController() {
        this.name = 'XL';
        this.sayName = function() {
            return this.name;
        }
    }
    
    <div ng-controller='DemoController as vm'>
        {{vm.name}}
    </div>

在js部份誊写控制器的时刻,更像是在写组织函数,然后在view层实例化一个ViewModule。那末在view内里能够直接挪用挂载在这个vm上的属性和要领。
如许的写法的优点就是防止在嵌套的controller内里运用$parent去猎取父controller内里的要领或值。由于在angular内里scope是基于原型举行继续的

    <div ng-controller='parentController'>
        {{name}}
        <div ng-controller='childController'>
            {{name}}
        </div>
    </div>
    
    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

    <input ng-model='vm.title'>
    
    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');
        }
    }
    <div ng-controller='parentController as vm'>
        <button ng-click='vm.sayName()'>BtnA</button>
        <button ng-click='sayName()'>BtnB</button>
    </div>

效果:

  • 点击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别的一个区分就是指向的题目:

    <div ng-controller='parentController' id='parentBox'>
        <p ng-click='logThisAndScope()'>log 'this' and $scope</p> - parent scope
        <div ng-controller='childController' id='childBox'>
            <p ng-click='logThisAndScope()'>log 'this' and $scope</p> - child scope
        </div>
    </div>

然后仅仅在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: '<div>{{ctrl.name}}</div>'
        }
    })

然则,假如这个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: '<div>{{ctrl.name}}</div>'
    };
});

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: '<div>{{ctrl.name}}{{ctrl.someObject}}</div>'
    };
});
//假如父作用域内里的someObject属性发生变化,会随时反应到这个directive的template内里。

complie vs link

Angularjs在处置惩罚directive时,取决于本身的compilelink参数定义的划定规矩:
当定义directive的时刻同时定义了complie,pre-link,post-link3个参数的时刻

    <level-one>
        <level-two>
            Hello {{XL}}
            <level-three>
                Hello {{Sugar}}
            <level-three>
        <level-two>
    </level-one>
    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'))

效果:
《Controller As in Angularjs》

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());
                    }
                }
            }
        }
    }

效果:

《Controller As in Angularjs》

如今再看下输出的信息,特别是在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属性被忽略了。

参考资料

    原文作者:苹果小萝卜
    原文地址: https://segmentfault.com/a/1190000004164613
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞