angularjs基础知识1

#### 作用域($scope) ####
    作用域是和应用的数据模型关联的,同时作用域也是表达式执行的上下文。作用域是视图和控制器之间的胶水。在应
    用将视图渲染并呈现给用户之前,视图中的模块会和作用域进行连接,然后应用会对DOM进行设置以便将属性变化通
    知给AngularJS。作用域是应用状态的基础。理解:当为子DOM元素创建新的作用域时,实际上是为子DOM元素创建
    了一个新的执行上下文。
    作用:
        1.监视数据模型的变化
        2.可将数据模型的变化通知给整个应用,甚至是系统外的组件
        3.可以进行嵌套,隔离业务功能和数据
        4.给表达式提供运算时所需的执行环境
    作用域包含了渲染视图时所需的功能和数据,是所有数据的唯一源头。可以将作用域理解为视图模型。
        ng-controller指令为这个DOM元素创建了一个新的$scope对象,并将其嵌套在$rootScope中。
        可以使用$scope上叫做$destory()的方法来清理这个作用域。
#### 控制器 ####
    在指令内部创建的作用域被称作孤立作用域。
    控制器在AngularJS中的作用是增强视图,控制器是一个函数,用来向视图的作用域中添加额外的功能。
    命名:[Name]Controller
    举个栗子:
        <div ng-controller='FirstController'>
            <h4>adding machine ever</h4>
            <button ng-click='add(1)' class='button'>Add</button>
            <a ng-click="substrct(1)" class = "button alert">Substract</a>
            <h4>Current count: {{counter}}</h4>
        </div>
        <script>
            angular.module("app01",[]).controller("FirstController",function($scope){
                $scope.counter = 0;
                $scope.add = function (amount) {$scope.counter += amount;};
                $scope.subtract = function (amount) {$scope.counter -= amount;}
            })
        </script>
    控制器并不适合用来执行DOM操作、格式化或数据操作,以及除存储数据模型之外的状态维护操作。只是视图与
    $scope之间的桥梁。
    举个栗子:
        创建一个ParentController,其中包含一个user对象,再创建一个ChildController来引用这个对象。
        app.controller("ParentController",function($scope){
            $scope.person = {greeted : false};
        });
        app.controller("ChildController",function($scope){
            $scope.sayHello = fuction () {
                $scope.person.name = "Ari Learer"
            }
        });
    将ChildController置于ParentController内部,在完成继承关系。
        <div ng-controller="ParentController">
            <div ng-controller = "ChildController">
                <a ng-click = "sayHello()">Say Hello</a>
            </div>
            {{person}}
        </div>
    控制器应该保持短小精悍,在其内进行DOM操作和数据操作是不好的实践。
    简洁的控制器的例子:
        angular.moudle("myApp",[]).controller("MyController",function($scope.UserSrv){
            //内容可以被指令控制
            $scope.onLogin = function (user) {
                UserSrv.runlogin(user);
            };
        });
#### 表达式 ####
    特性:
        1.所有的表达式都在其所属的作用域内部执行,并有访问本地$scope的权限
        2.如果表达式发生了TypeError和RefrenceError并不会抛出异常
        3.不允许使用任何流程控制功能(条件控制,例如if/else)
        4.可以接受过滤器和过滤器链
    1.解析AngularJS表达式
    将$parse服务注入到控制器中,然后调用它就可以实现手动解析表达式。
    举个栗子:
        <div ng-controller = "MyController">
            <input ng-model = "expr" type = "text" placeholder = "enter an expression" />
            <h2>{{parseValue}}</h2>
        <div>
        可在Mycontroller中给expr这个表达式设置一个$watch并解析它:
        angular.moudle("myApp",[]).controller("MyControll",function($scope,$parse){
            $scope.$watch("expr",function(newVal,oldVal,scope){
                if(newVal !== oldVal){
                    //用该表达式设置parseFun
                    var parseFun = $parse(newVal);
                    //获取经过解析后的表达式的值
                    $scope.parseValue = parseFun(scope);
                }
            });
        });
    2.插值字符串
    插值允许基于作用域上的某个条件实时更新文本字符串。要在字符串模板上做插值操作,需要在对象中注入
    $interpolate服务。
    举个栗子:
        angular.moudle('myApp',[]).controller("MyController",function($scope,$interpolate){
            //同时拥有访问$scope和$interpolate服务的权限
        });
    $interpolate服务是一个可以接受三个参数的函数:
        text(字符串):一个包含字符插值标记的字符串(必需)
        mustHaveExpression(布尔型):如果设为true,当传入的字符串中不含有表达式时会返回null
        trustedContext(字符串):AngularJS会对已经进行过字符插值操作的字符串通过$sec.getTrust()方法
        进行严格的上下文转义。
        $interpolate服务返回一个函数,用来在特定的上下文中运算表达式。
    举个栗子:
        <div ng-controller="MyController">
            <input ng-model="to" type="email" placeholder="Recipient" />
            <textarea ng-model="emailBody"></textarea>
            </pre>{{previewText}}</pre>
        </div>
    $watch函数会监视$scope上的某个属性。只要属性发生变化就会调用对应的函数。可以使用$watch函数在$scope
    上的某个属性发生变化时直接运行一个自定义的函数。
        angular.moudle("myApp",[]).controller("MyController",function($scope,$interpolate){
            //设置监听
            $scope.$watch("emailBody",function(body){
                if(body){
                    var template = $interpolate(body);
                    $scope.previewText = template({to:$scope.to});
                }    
            };
        });
    用startSymbol()方法可以修改标识开始的符号。接受一个参数。value(字符型):开始符号的值
    用endSymbol()方法可以修改标识结束的符号。这个方法接受一个参数。value(字符型):结束符号的值
    需要修改这两个符号的设置,需要在创建新模块时将$interpolateProvider注入进去。
    举个栗子:
        angular.moudle("emailParser",[]).config(['$interpolateProvider',function
        ($interpolateProvider){
            $interpolateProvider.startSymbol("__");
            $interpolateProvider.endSymbol("__");
        }])
        .factory("EmailParser",["interpolate",function($interpolate){
            //处理解析的服务
            return {
                parse : function(text,context){
                    var template = $interpolate(text);
                    return tempolate(context);
                }
            };
        }]);
    现在模块已经创建了一个模块,可以将它注入的应用中:
        angular.module("myApp",["emailParser"]).controller("MtController",
        ["$scope","EmailParser",function($scope,EmailParser){
            //设置监听
            $scope.$watch("emailBody",function(body){
                if(body){
                    $scope.previewText = EmailParser.parse(body,{
                        to:$scope.to
                    });
                }
            });
        }]);
    现在用自定义的__符号取代默认语法中的{{}}符号来请求插值文本。
#### 过滤器 ####
    在HTML中的模板绑定符号{{}}内通过|符号来调用过滤器:
        {{name|uppercase}}
    在js代码中可以通过$filter来调用过滤器。
        app.controller("DemoController",["$scope","$filter",function($scope,$filter){
            function($scope,$filter){
                $scope.name = $filter("uppercase")("Ari");
            }]);
    {{123.456789|number:2}};//<!--显示:123.46-->
    1.currency过滤器可以将一个数值格式化为货币格式。
    2.date(默认:mediumDate)
    本地化日期格式:
        {{today|date:"medium"}}      //Aug 09,2013 12:09:02 PM
        {{today|date:"short"}}        //8/9/1312:09PM
        {{today|date:"fullDate"}}    //Thursday,August 09,2013
        {{today|date:"longDate"}}    //August 09,2013
        {{today|date:"mediumDate"}}    //Aug 09,2013
        {{today|date:"shortDate"}}    //8/9/13
        {{today|date:"mediumTime"}}    //12:09:02 PM
        {{today|date:"shortTime"}}    //12:09 PM
    年份格式化:
        四位年份:{{today|date:"yyyy"}}    //2013
        两位年份:{{today|date:"yy"}}        //13
        一位年份:{{today|date:"y"}}        //2013
    月份格式化:
        英文月份:{{today|date:"MMMM"}}        //August
        英文月份简写:{{today|date:"MMM"}}    //Aug
        数字月份:{{today:date|"MM"}}            //08
        一年中的第几个月份:{{today|date:"M"}}    //8
    日期格式化:
        数字日期:{{today|date:"dd"}}            //09
        一年中的第几天:{{today|date:"d"}}    //9
        英文星期:{{today|date:"EEEE"}}        //Thursday
        英文星期简写:{{today|date:"EEE"}}        //Thu
    小时格式化:
        24小时制的数字小时:{{today|date:"HH"}}        //00
        一天中的第几个小时:{{today|date:"H"}}            /0
        12小时制的数字小时:{{today|date:"hh"}}        //12
        上午或下午中的第几个小时:{{today|date:"h"}}    //12
    分钟格式化:
        数字分钟数:{{today|date:"mm"}}            //09
        一个小时中的第几个分钟:{{today|date:"m"}}    //9
    秒数格式化:
        数字秒数:{{today|date:"ss"}}            //02
        一分钟内的第几秒:{{today|date:"s"}}    //2
        毫秒数:{{today|date:".sss"}}            //.995
    字符格式化:
        上下午标识:{{today|date:"a"}}        //AM
        四位时区标识:{{today|date:"Z"}}        //0700
    3.filter
    filter过滤器可以从给定数组中选择一个子集,并将其生成一个新数组返回。这个过滤器的第一个参数可以是字符
    串、数组、对象或一个用来从数组中选择元素的函数。
    栗子:
        {{['Ari','lerner','likes','to','eat',pizza]|filter:"e"}};//['lerner','likes','eat']
    如果要过滤对象,可以使用对象过滤器:
        {{[{
            'name':'ari',
            'city':'san francisco',
            'favorite food':'pizza'
        },{
            'name':'nate',
            'city':'san francisco',
            'favorite food':'indian food'
        }] | filter:{'favority food':'pizza'}}};//[{'name':'ari','city':'san francisco',
                                                    'favorite food':'pizza'}]
    也可以用自定义函数进行过滤:
        {{['Ari','likes','to','travel'] | filter:isCapitalized}};//['Ari']
        $scope.isCapitalized = function(str){
            return str[0] == str[0].toUpperCase();
        }
    也可以给filter过滤器传入第二个参数,用来指定预期值同实际值进行比较的方式。
        .true
        用angular.equals(expected,actual)对两个值进行严格比较。
        .false
        进行区分大小写的子字符串比较。
        .函数
        运行这个函数,如果返回真值就接受这个元素。
    4.json
        json过滤器可以将一个json或js对象转换为字符串。
        {{{'name':'ari','city':'san'} | json}};
            //{
                "name":"ari",
                "city":"san"
            }
    5.limitTo
        limitTo过滤器会根据传入的参数生成一个新的数组或字符串,新的数组或字符串的长度取决于传入的参数。如
        果传入的长度大于被操作数组或字符串的长度,那么整个数组或字符串都会被返回。
    例子:
        {{san frencisco is every cludy | limitTo:3}};//san
        {{san frencisco is every cludy | limitTo:-5}};//cludy
        {{['a','b','c','d'] | limitTo:1}};//["a"]
    6.lowercase
    7.number
        number过滤器将格式化成文本,第二个参数是可选的,用来控制小数点后截取的位数。如果传入了一个非数字字
        符,会返回空字符串。
        {{123456789 | number}};//1,234,567,890
        {{1.234567 | number:2}};//1.23
    8.orderBy
        orderBy过滤器可以用表达式对指定的数组进行排序。2个参数,1来确定数组排序方向的谓词,2表示是否反向
        .函数
        当第一个参数是函数时,该函数被当作待排序对象的getter方法
        .字符串
        决定数组元素的排序方向。可以传入+或-来强制进行升序或降序排列。
        .数组
        {{[{
            'name':'ari',
            'status':'awake'
        },{
            'name':'Q',
            'status':'sleeping'
        },{
            'name':'Nate',
            'status':'awake'
        }] | orderBy:'name'}};
        <!--
            [
            {"name":"ari","status":"awake"},
            {"name":"Nate","status":"awake"},
            {"name":"Q","status":"sleeping"}
            ]
        -->
    通过将第二个参数设置为true可以将排序结果进行反转。
    9.uppercase
#### 创建自定义过滤器 ####
    angular.moudle("myApp.filters",[]).filter("capitalize",function(){
        return function(input){};
    });
    可以做一些错误检查:
    angular.module("myApp.filter",[]).filter("capitalize",function(){
        return function(input){
            //input是传入的字符串
            if(input){
                return input[0].toUpperCase() + input.slice(1);
            }
        }
    });
    将一个句子的首字母转换成大写的形式:
        {{'ginger loves dog treats' | lowercase | capitalize}};
#### 表达验证 ####
    要使用表单验证,首先要确保表单有一个name属性。
    1.必填项
    <input type="text" required />
    2.最小长度,ng-minthlength="{number}"
    <input type="text" ng-minlength="5" />
    3.最大长度,ng-maxlength="{number}"
    <input type="text" ng-maxlength="20">
    4.模式匹配,ng-pattern="PATTERN"
    <input type="text" ng-pattern="[a-zA-Z]" />
    5.电子邮件,type="eamil"
    <input type="email" name="email" ng-model="user.model">
    6.数字,type="number"
    <input type="number" name="homrpage" ng-mdel="user.page" />
    7.url,type="url"
    <input type="url" name="homepage" ng-model="user.facebook_url" />
    8.自定义验证
    如何通过向后端服务器发送请求,并通过响应的结果来将字段设置为合法或不合法,以确保输入字段中的内容的唯一的
    9.在表单中控制变量
    表单的属性可以在其所属的$scope对象中访问到,我们可以访问$scope对象,因此js可以间接地访问DOM中的表单属
    性。
    formName.inputFieldName.property;(可以使用此格式访问这些属性)
    .未修改的表单
    formName.inputFieldName.$pristine;验证用户是否修改了表单,未修改,返回true
    .修改过的表单
    formName.inputFieldName.$dirty;//只要是用户修改了表单,无论是否通过验证,该值都返回true
    .合法的表单
    .formName.inputFieldName.$valid;//如果当前表单内容是合法的,返回true
    .不合法的表单
    .formNamw=e.inputFieldName.$invalid;//如果当前表单的内容是不合法的,范回true
    .错误(.$error对象,包含当前表单的所有验证,以及是否合法的信息)
    .formName.inputFieldName.$error(如果验证失败,这个属性的值为true;如果值为false,该字段通过验证)
    10.一些有用的css样式
    AngularJS处理表单时,会根据表单当前的状态添加一些css类(ex:当前是合法的,未发生变化的等):
        .ng-pristine()
        .ng-dirty()
        .ng-valid()
        .ng-invalid()
    当某个字段中的输入非法时,ng-invalid类会被添加到这个字段中。
        input.ng-invalid {
            border:1px solid red;
        }
        input.ng-valid {
            border:1px solid blue;
        }
    .parsers
    当用户同控制器进行交互,并且ngModelController中的$setViewValue()方法被调用时,$parsers数组中的函数
    会以流水线的形式被逐个调用。第一个$parse被调用后,执行结果会传递给第二个$parse,以此类推。
    使用$parsers数组是实现自定义验证的途径之一。每一个$parser返回的值都会被传入下一个$parser中。当不希望
    数据模型发生更新时返回undefined
    angular.module("myApp").directive("oneToTen",function(){
        return {
            require : '?ngModel',
            link : function(scope,ele,attrs,ngModel){
                if(!ngModel) return;
                ngModel.$parsers.unshift(
                    function(viewValue){
                        var i = parseInt(viewValue);
                        if(i>=0 && i<10){
                            ngModel.$setValidity("oneToTen",true);
                            return viewValue;
                        }else{
                            ngModel.$setValidity("oneToTen",false);
                            return undefied;
                        }
                    }
                )
            }
        }
    });
    .formatters
    当绑定的ngModel值发生了变化,并经过$parsers数组中解析器的处理后,这个值会被传递给$formatters流水线。
    通过formatters数组可以在这个值上执行过滤器:
    angular.module("myApp").directive("oneToTen",function(){
        return {
            require : "?ngModel",
            link : function(scope,ele,attrs,ngModel){
                if(!ngModel) return;
                ngModel.$formatters.unshift(function(v){
                    return $filter("number")(v);
                });
            }
        }
    });
    组合实例:
    <form name="singup_form" novalidateng-submit="singupForm()">
        <fieldset>
            <legend>Singup</legend>
            <button type="submit" class="button radius">Submit</button>
        </fieldset>
    </form>
#### 指令简介 ####
    http://www.cnblogs.com/rohelm/p/4051437.html
    声明指令本质上是在HTML中通过元素、属性、类或注释来添加功能。
    1.指令:自定义HTML元素和属性(元素是不用自定义的一般情况下)
    自定义元素时需要设置replace:true,可以将自定义标签从生成的DOM中完全移除。
    2.创建指令
    声明创建指令的合格方法:
        <my-directive></my-directive>
        <div my-directive></div>
        <div class="my-directive"></div>
        <!--directive:my-directive-->
    坚持使用属性方法,有比较好的跨浏览器兼容性。<div my-dirctive></div>,restrict:A(代表:attribute)
    3.关于IE浏览器
    好的经验法就是坚持始终用属性来声明指令,这样会避免兼容性的问题。
    4.表达式
    <h1 ng-init="greeting='Hello World'">
        The greeting is : {{greeting}}
    </h1>
    //将表达式greeting='Hello World'赋值给内置指令ng-init.在表达式中,将greeting属性的值设置为Hello 
    World,然后计算花括号内的{{greeting}}这个表达式的值。
    .用表达式来声明指令
    <my-directive="someExpression"></my-directive>
    <div my-directive="someExpression"></div>
    <div class="my-directive:someExpression"></div>
    <!--directive:my-directive someExpression-->
    #### 向指令中传递数据 ####
    最简单的设置指令内部作用域中属性的值的方法就是使用由所属控制器提供的已经存在的作用域。AngularJS允许通过
    创建新的子作用域或者隔离作用域来解决控制器一旦被移除的问题。
    当用如下代码将指令的作用域设置成一个只包含它自己的属性的干净对象时:
        scope : {
            someProperty : "needs to be set"
        }
        实际上创造的是隔离作用域,本质上,意味着指令有了一个属于自己的$scope对象,这个对象只能在指令的方法
        中或指令的模板字符串中使用:
            <div my-directive 
                my-url = "http://www.baidu.com" 
                my-link-text = "click">
            </div>
            <script>
                angular.module("myApp",[]).directive("myDirective",function () {
                    return {
                        restrict : 'A',
                        replace : true,
                        scope : {
                            myUrl : '@',//绑值策略
                            myLinkText : '@'//绑值策略
                        },
                        template : '<a href = "{{myUrl}}">' + '{{myLinkText}}</a>'
                    }
                });
            </script>
    在作用域对象内部把someProperty值设置为@这个绑定策略,这个绑值策略告诉AngularJS将DOM中的
    some-property属性的值赋值给新作用域对象中的someProperty属性。
    注意:默认情况下someProperty在DOM中的映射是some-property属性。如果想显式的指定绑定的属性名,可以用如
    下的方式:
        scope : {
            soemProperty : '@someAttr'
        }
    在这个例子中,被绑定的属性名是some-attr而不是some-property。
        <div my-directive some-attr="someProperty with @ binding">
    默认情况下约定DOM属性和JavaScript中对象属性的名字是一样的(除非对象的属性名侧泳的是驼峰式写法)。
        scope : {
            myUrl : '@someAttr',
            myLinkText : '@'
        }
    上面的隔离作用域中的内容是:将指令的私有属性$scope.myUrl同DOM中some-attr属性的值绑定起来。这个值既可
    以是硬编码的也可以是当前作用域(例如some-attr="{{expression}}")中某个表达式的运算结果。
    在DOM中要用some-attr代替my-url:
        <div my-directive some-attr = "http://www.baidu.com" my-link-text = "click"></div>
    更进一步,还可以在DOM对应的作用域上运算表达式,并将结果传递给指令,在指令内部最终被绑定在属性上。
        <div my-directive some-attr="{{'http://' + 'google.com'}}"></div>;//{{}}可以理解为eval
    注意:在输入标签上使用了内置指令ng-model,这个指令可以将输入文本同$scope上的myUrl属性进行绑定
        <input type="text" ng-model="myUrl">
        <div my-directive my-url="{{myUrl}}" my-link-text="click"></div>
    内置指令ng-model在它自身内部的隔离作用域和DOM的作用域之间创建了一个双向数据绑定。
    在隔离作用域和ng-model内部的隔离作用域之间创建一个双向数据绑定。将内部的$scope.myUrl属性同当前控制器
    作用域中的theirUrl属性及进行绑定,在DOM中通过作用域查询来实现这个绑定。
        <label>their url feild:</label>
        <input type="text" ng-model="theirUrl"/>
        <div my-directive
            some-attr="theirUrl"
            my-link-text="click"></div>
        <script>
            angular.module("myApp",[]).directive("myDirective",function () {
                return {
                    restrict : 'A',
                    replace : true,
                    scope : {
                        myUrl : "=someAttr",//经过了修改
                        myLinkText : "click"
                    },
                    template : '<div><label>my url field:</label><input type="text" ng-
                    model="myUrl" /><a href="{{myUrl}}">{{myLinkText}}</a>>'
                };
            });
        </script>
    除了将原来的文本输入字段添加回主HTML外,唯一的修改是用=绑定策略代替了@.
#### 内置指令 ####
    1.基础ng属性指令
        ng-href;
        ng-src;
        ng-disabled;
        ng-checked;
        ng-readonly;
        ng-selected;
        ng-class
        ng-style;
        1.1:布尔属性
                根据HTML标准的定义,布尔属性代表一个true或flase值。当这个属性出现时,这个属性的值就是
            true。如果未出现,就是false。
                当在AngularJS中使用动态数据绑定时,不能简单地将这个属性的值设为true或false,因为根据标准
            定义的只有当这个属性不出现时,它的值才是flase。因此AngularJS提供了一组带有ng-前缀版本的布尔
            属性,通过运算表达式的值来决定在目标元素上是插入还是移除对应的属性。
        .ng-disabled;//主要在于判断ng-disabled什么时候为true,什么时候为false
            <input>;//(text,checkbox,radio,number,url,email,submit)
            <textarea>;
            <select>;
            <button>;
        AngularJS中通过ng-disabled可以对是否出现属性进行绑定。
            <input type="text" ng-model="someProperty" placeholder="TypeEnable" />
            <button ng-model="button" ng-disable="!someProperty">AbUTTON</button>
            //按钮会一直禁用,直到用户在文本字段输入内容
            <textarea ng-disabled="isDisabled">Wait5seconds</textarea>
            angular.module("myApp",[]).run(function($rootScope,$timeout){
                $rootScope.isDisabled = true;
                $timeout(function(){
                    $rootScope.disabled = false;
                },5000);
            });//文本字段被禁用5秒
        1.2.ng-readonly
        <input type="text" ng-model="someProperty" /><br/>
        <input type="text" ng-readonly="someProperty" value="Some text here">
        1.3.ng-checked
        在下面的例子中,通过ng-init指令将someProperty的值设置为true。将some Property同ng-checked绑定
        到一起,AngularJS会输出标准的Html checked属性,默认会把复选框勾选。
            <label>someProperty={{someProperty}}</label>
            <input type="checkbox" ng-ckecked="someChecked" ng-init="someProperty=true"
            ng-model="someProperty">
        1.4.ng-selected
        ng-selected可以对是否出现option标签的selected属性进行绑定。
            <label>select two fish:</label>
            <input type="checkbox" model="isTwoFish">
            <select>
                <option>one fish</option>
                <option ng-selected="isTwoFish">Two Fish</option>
            </select>
    1.5.类布尔属性
    ng-href、ng-src等属性都能有效帮助重构和避免错误,因此在改进代码时强烈建议用它们代替原来的href和src属
    性
        1.7.ng-href
        这里的潜在问题是当用户点击一个由插值动态生成的链接时,如果插值尚未生效,就会404.这时,如果使用ng-
        href,AngularJS会等到插值生效后再执行点击链接的行为。
        <!--当href包含一个{{expression}}时总是使用ng-href-->
        <a ng-href="{{myHref}}">click</a>
        <!--用户点击之前,href不会加载-->
        <a href="{{myHref}}">404</a>
        angular.module("myApp",[]).run(function($rootScope,$timeoput){
            $timeout(function(){
                $rootScope.myHref = "http://google.com";
            },2000);
        });
        1.6.ng-src
        AngularJS会告诉浏览器在ng-src对应的表达式生效之前不要加载图像:
        <h1>WrongWay</h1>
        <img src="{{imgSrc}}" />
        <h1>RightWay</h2>
        <img ng-src="{{imgSrc}}">
        angular.module("myApp",[]).run(function($rootScope,$timeout){
            $timeout(function(){
                $rootScope.imgSrc="https://www.google.com/images/srpr/logo11w.png"
            },2000);
        });
    2.在指令中使用子作用域
        下面介绍的指令会以父级作用域为原型生成子作用域。ng-app和ng-controller是特殊的指令,因为它们会修
        改嵌套在它们内部的指令的作用域。
        2.1 ng-app;//任何具有ng-app属性的DOM元素将被标记为$rootScope的起始点,任何嵌套在ng-app内的指
        令都会继承他它。    
        在js代码中通过run方法来访问$rootScope:
            <html ng-app="myApp">
            <body>
                {{someProperty}}
                <button ng-click="someAction()"></button>
            </body>
            </html>
            angular.module("myApp",[]).run(function($rootScope){
                $rootScope.someProperty = "hello computer";
                $rootScope.someAction = function () {
                    $rootScope.someProperty = "hello human";    
                };
            });
        2.2 ng-controller
        内置指令ng-controller的作用是为嵌套在其中的指令创建一个子作用域,避免将所有操作和原型都定义在
        $rootScope上。用这个指令可以在一个DOM元素上放置控制器。
        $scope对象的职责是承载DOM中指令所共享的操作和模型。
        操作:$scope上的标准的js方法
        模型:$scope上保存的包含瞬时状态数据的js对象。持久化状态的数据应该放在服务上,服务的作用是处理模型
        的持久化。
        处于技术和架构方面的原因,绝对不要直接将控制器中的$scope赋值为值类型对象(字符串、布尔值或数字)。
        DOM中应该始终通过.操作符来访问数据。
        尽量将业务逻辑移到服务和指令中。
        例子:
            <div ng-controller="someController">
                {{someModel.someProperty}}
                <buton ng-click="someAction()">    click</button>
            </div> 
            angular.module("myApp",[]).controller("someController",function($scope){
                //创建模型
                $scope.soemModel = {
                    someProperty : "hello computer"
                };
                //设置$scope自身的操作
                $scope.someAction = function(){
                    $scope.someModel.someProperty = "hallo";
                };
            });
        首先,使用了$rootScope的子作用域,提供了一个干净的对象$scope.使用子作用域意味着其上的操作和模型
        在应用的其他部分是无法访问到的,只能被这个作用域的指令及其子作用域访问。
        其次,显示声明了数据类型。
        例子:在已有的控制器中嵌套了第二个控制器,并且没有设置对象模型的属性。
        <div ng-controller="soemController">
            {{someBareValue}}
            <button ng-click="soemAction()">Communicate to child</button>
            <div ng-controller="childController">
                {{soemBareValue}}
                <button ng-click="childAction()">Communicate to parent</buton>
            </div>
        </div>
        angular.module("myApp",[]).controller("parentController",function($scope){
            //反模式,裸值
            $scope.someBareValue = "hello world";
            //设置$scope本身的操作,这样没有问题
            $scope.soemAction = function(){
                //在someController和childController中设置{{someBareValue}}
                $scope.someBareValue = "hello human,from parent";
            };
        })
        .controller("childController",function($scope){
            $scope.childAction = function(){
                //在childController中设置{{someBareValue}}
                $scope.someBareValue = "hello human,from child"
            };
        });
        由于原型链继承的关系,修改父级对象中的someBareValue会同时修改子对象的值,但反之则不行。
        这个例子的实际效果,首先点击child button,然后点击parent button。这个例子充分说明了控制器是复制
        而非引用someBareValue。
        js对象要么是值复制要么是引用复制。字符串、数字和布尔值变量是值复制。数组,对象和函数是引用复制。
        如果将模型对象的某个属性设置为字符串,它会通过引用进行共享,因此在子$scope中修改属性也会修改
        父$scope中的这个属性。正确做法:
        <div ng-controller="someController">
            {{someModel.someValue}}
            <button ng-click="someAction()">Communicate to child</button>
            <div ng-controller="childController">
                {{someModel.someValue}}
                <button ng-click="childAction()">Communicate to parent</button>
        </div>
        <script>
            angular.module("myApp",[]).controller("someController",function($scope){
                //最佳实践,永远使用一个模式
                $scope.someModel = function(){
                    someValue : "hello computer"
                };
                $scope.someAction = function(){
                    $scope.someModel.someValue = "hello human,from parent";
                };
            }).controller("childController",function($scope){
                $scope.childAction = function(){
                    $scope.someModel.someValue = "hello human,from child";
                };
            });
        </script>
        无论点击那个按钮,值都会同步修改
        注意:虽然这个特性是使用ng-controller时最重要的特性之一,但在任何会创建子作用域的指令时,如果将
        指令定义中的scope设置为true,这个特性也会带来负面影响。下面的内置指令都有同样的特性:
        ng-include;
        ng-switch;
        ng-repeat;
        ng-view;
        ng-controller;
        ng-if;
    3.ng-include
    使用ng-include可以加载、编译并包含外部HTML外部片段到当前的应用中。
    在同一个属性上添加onload属性可以在模板加载完成后执行一个表达式。
    要记住:使用ng-include时AngularJS会自动创建一个子作用域,如果想使用某个特定的作用域,例如controller
    的作用域,必须在同一个DOM元素上添加ng-controller=“controller”指令,这样当模板加载完成后,不会像往常
    一样从外部作用域继承并创建一个新的子作用域。
        <div ng-include="/myTemplateName.html"
            ng-controller="controller"
            ng-init="name='world'">
            Hello{{name}}
        </div>
    4.ng-switch
    这个指令和ng-swith-when及on="propertyName"一起使用。可以在propertyName发生变化时渲染不同指令到
    视图上。
    //当person.name是Ari时,文本区域下面的div会显示出来,并且这个人会获得胜利:
        <input type="text" ng-model="person.name" />
        <div ng-switch on="person.name">
            <p ng-switch-default>And the winner is</p>
            <h1 ng-switch-when="Ari">{{person.name}}</h1>
        </div>
        注意:在switch被调用之前用ng-switch-default来输出默认值
    5.ng-view
    ng-view指令用来设置将被路由管理和放置在HTML中的视图的位置
    6.ng-if
    使用ng-if指令可以完全根据表达式的值在DOM中生成或移除一个元素。如果赋值给表达式的值是false,那对应的
    元素将会从DOM中移除,否则对应的元素的一个克隆将被重新插入DOM中。
    ng-if同ng-show和ng-hide指令最本质的区别是,它不是通过CSS显示或隐藏DOM节点,而是真正生成或移除节点。
    当一个元素通过ng-if被移除,同它关联的作用域也会被销毁。而且当它重新加入DOM中时,会通过原型继承从它的
    父作用域生成一个新的作用域。
    内部的代码加载之后被jQuery修改过(例如用.addClass),那么当ng-if的表达式值为false时,这个DOM元素
    会被移除,表达式再次成为true时这个元素及其内部的子元素会被重新插入DOM,此时这些元素的状态会是它们的
    原始状态,而不是它们上次被移除时的状态。也就是说无论用jQuery的.addClass添加了什么类都不会存在了。
        <div ng-if="2+2===5">
            Won't see this DOM node, not even in the source code
        </div>
        <div ng-if="2+2=4">
            Hi,i do exist
        </div>
    7.ng-repeat
    ng-repeat用来遍历一个集合或为集合中的每个元素生成一个模板实例。集合中的每个元素都会被赋予自己的模板
    和作用域。同时每个模板实例的作用域中都会暴露一些特殊的属性。
        $index:遍历的进度(0...length-1)
        $filter:当元素是遍历的第一个时值为true
        $middle:当元素处于第一个和最后元素之间时值为true
        $last:当元素是遍历的最后一个时值true
        $even:当$index值是偶数时值为true
        $odd:当$index值是奇数时值为true
    下面的例子展示了如何用$odd和$even来制作一个红蓝相间的列表。记住:js中数组的索引从0开始,因此用!$even
    和$odd来将$even和$odd的布尔值反转。
        <ul ng-controller="PeopleController">
            <li ng-repeat="person in people" ng-class="{even:!$even,odd:!$odd}">
            {{person.name}} lives in {{person.city}}
            </li>
        </ul>
        .odd{
            background-color:blue;
        }
        .even{
            background-color:red;
        }
        angular.module('myApp',[]).controller('peopleController',function($scope){
            $scope.people = {
                {name:"Ari",city:"san"},
                {name:"Erik",city:"Seattle"}
            };
        });
    8.ng-init
    ng-init指令用来在指令被调用时设置内部作用域的初始值。
    ng-init最常见的使用场景:需要创建小的示例代码的时候,对于任何需要健壮结构的场景,请在控制器中用数据
    模型对象来设置状态。
        <div ng-init="greeting='hello';person='world'">
            {{greeting}}{{person}}
        </div>
    9.{{}}
    {{}}实际上也是指令,ng-bind的简略形式。是AngularJS内置的模板语法,它会在内部$scope和视图之间创建
    绑定,基于这个绑定,只要$scope发生变化,视图就会随之自动更新。
    注意:在屏幕可视的区域内使用{{}}会导致页面加载时未渲染的元素发生闪烁,用ng-bind可以避免这个问题。
    10.ng-bind
    <body ng-init="greeting='hello'">
        <p ng-bind='greeting'></p>
    </body>
    HTML加载含有{{}}语法的元素并不会立即渲染它们,导致未渲染内容闪烁,可以用ng-bind将内容同元素绑定在一
    起避免闪烁。内容会被当做子节点渲染到含有ng-bind指令的元素内。
    11.ng-cloak
    除使用ng-bind指令来避免未渲染元素闪烁,还可以在含有{{}}的元素上使用ng-bloak指令:
    <body ng-init="greeting='hello'">
        <p ng-bloak>{{greeting}}</p>
    </body>
    12.ng-bind-template
    同ng-bind指令类似,ng-bind-template用来在视图中绑定多个表达式。
    <div ng-bind-template="{{message}}{{name}}">
    </div>
    13.ng-model
    ng-model指令用来将input、select,textarea或之定义表单控件同包含它们的作用域中的属性进行绑定。
    应该始终用ngModel来绑定$scope上的一个数据模型的属性,而不是$scope上的属性,可避免在作用域或后代
    作用域中发生属性覆盖。
    <input type="text" ng-model="modelName.soemProperty" />
    14.ng-show/ng-hide
    ng-show和ng-hide根据所给表达式的值来显示或隐藏html元素。当赋值给ng-show指令的值为flase时元素会被
    隐藏。赋值给ng-hide指令的值为true时元素也会被隐藏。
        <div ng-show="2 + 2 == 5">
            2 + 2 isn't 5, don't show
        </div>
        <div ng-show="2 + 2 == 4">
            2 + 2 is 4, do show
        </div>
    15.ng-change
    ng-change指令会在表单输入发生变化时计算给定表达式的值。因为要处理表单输入,这个指令要和ngModel联合用
    <div ng-controller="EquationController">
        <input type="text" ng-model="equation.x" ng-change="change()" />
        <code>{{equation.output}}</code>
    </div>
    angular.module("myApp",[]).controller("EquationController",function($scope){
        $scope.equation = {};
        $scope.change = function () {
            $scope.equation.output = parseInt($scope.equation.x) + 2;
        };
    });
    只要文本输入字段中的内容发生了变化就会改变equation.x的值,进而运行change()函数
    16.ng-form
    ng-form用来在一个表单内部嵌套另一个表单。普通的HTML<form>标签不允许嵌套,但ng-forn可以。
    这意味着内部所有的子表单都合法时,外部的表单才会合法。这对于ng-repeat动态创建表单是很有用的。
    由于不能通过字符插值来给输入元素动态的生成name属性,所以需要将ng-form指令内每组重复的输入字段都包含
    在一个外部表单元素内。
    下面的css类会根据表单的验证状态自动设置:
        .表单合法时设置ng-valid
        .表单不合法是设置ng-invalid
        .表单未进行修改时设置ng-pristion
        .表单进行过修改时设置ng-dirty
    Angular不会将表单提交到服务器,除非它指定了action属性。要指定表单提交时调用哪个js方法,使用下面两个
    指令中的一个。
        .ng-submit:在表单元素中使用
        .ng-click:在一个按钮或submit类型(input[type=submit])的输入字段上使用。
    为了避免处理程序被多次调用,只使用下面两个指令中的一个。    
    下面的例子展示了如何通过服务器返回的JSON数据动态生成一个表单。用ng-loop来遍历从服务器取回的所有数据。
    由于不能动态生成name属性,又需要这个属性来做验证,所以在循环的过程中会为每一个字段都生成一个新表单。
    由于AngularJS中用来取代<form>的ng-form指令可以嵌套,并且外部表单在所有子表单都合法之前一直处于不
    合法状态,因此可以在动态生成子表单的同时使用表单验证功能。
    硬编码JSON数据,假设其是从服务器返回的:
        angular.module("myApp",[]).controller("FormController",function($scope){
            $scope.feild = [
                {placeholder:'username',isRequired:true},
                {palceholder:'password',isRequired:true},
                {placeholder:'email(optional)',isRequired:false}
            ];
            $scope.submitForm = function(){
                alert("it works!");
            };
        });
    下面用这些数据生成一个有验证功能的动态表单:
        <form name="singnup_form"
            ng-controller="FormController"
            ng-submit="submitForm()" novalidate>
            <div ng-reoeat="field in fields" ng-form="singup_form_input">
                <input type="text" name="dynamic_input" ng-required="field.isRequired"
                    ng-model="field.name" placeholder="{{field.paceholder}}" />
                <div
                    ng-show="signup_form_input.dynamic_input.$dirty && 
                        signup_form_input.dynamic_input.$invalid">
                        <span class="error"
                            ng-show="signup_form_input.dynamic_input.$error.required">
                                the feild is required.
                        </span>
                </div>
            </div>
            <button type="submit" ng-disable="signup_form.$invalid">
                submit all.
            </button>
        </form>
        input.ng-invalid{
            border:1px solid red;
        }
        input.ng-valid{
            border:1px solid green;
        }
    17.ng-click
    ng-click用来指定一个元素被点击时调用的方法或表达式。
        <div ng-controller="CounterController">
            <buton ng-click="count=count+1" ng-init="count=0">
                increment
            </button>
            count:{{count}}
            <button ng-click="decrement()">
                decrement
            </buton>
        </div>
        angular.module("myApp",[]).controller("CounterController",function($scope){
            $scope.decrement = function(){
                $scope.count = $scope.count - 1;
            }
        });
    18.ng-select
        ng-select用来将数据同HTML的<select>元素进行绑定。这个指令可以和ng-model以及ng-options指令
        一同使用,构建精细且表现优良的动态表单。
        ng-option的值可以是一个数组或对象。
        数组作为数据源:
            。用数组中的值做标签
            。用数组中的值作为选中的标签
            。用数组中的值作为标签组
            。用数组中的值作为选中的标签组
        对象作为数据源:
            。用对象的键-值做标签
            。用对象的键-值作为选中的标签
            。用对象的键-值作为标签组
            。用对象的键-值作为被选中的标签组
    举个栗子:
        <div ng-controller="CityController">
            <selection ng-model="city" ng-option="city.name for city in cities">
                <option value="">Choose City</option>
            </select>
            Base City:{{city.name}}
        </div>
        angular.module("myApp").controller("CityController",function($scope){
            $scope.cities = [
                {name : "Seattle"},
                {name : "san Francison"},
                {name : "New York"},
                {name : "Boston"}
            ];
        });
    19.ng-submit
        ng-submit用来将表达式同onsubmit事件进行绑定。这个指令同时会阻止默认行为(发送请求并重新加载页
        面),除非表单不含有action属性。
        <form ng-submit="submit()" ng-controller="FormController">
            Enter text and hit enter:
            <input type="text" ng-model="person.name" name="person.name" />
            <input type="submit" name="person.name" value="Submit" />
            <code>people={{people}}</code>
            <ul ng-repeat="(index,object) in people">
                <li>{{object.name}}</li>
            </ul>
        </form>
        angular.module("myApp",[]).controller("FormController",function($scope){
            $scope.person = {
                name : null
            };
            $scope.people = [];
            $scope.result = function () {
                if($scope.person.name){
                    $scope.people.push(name : $scope.person.name);
                    $scope.person.name = "";
                }
            };
        });
    20.ng-class
        用ng-class在一个随机数大于5时将.red类添加到一个div上
        <div ng-controller="LotteryController">
            <div ng-class="{red:x>5}" ng-if="x>5">
                you won!
            </div>
            <button ng-click="x=generateNumber()" ng-init="x=0">
                Draw number
            </button>
            <p>number is : {{x}}</p>
        </div>
        .red{
            background-color:red;
        }
        angular.module("myApp",[]).controller("LotteryController",function($scope){
            $scope.generateNumber = function(){
                return Math.floor((Math.random()*10) + 1);
            };
        });
    21.ng-attr-(suffix)
        当AngularJS编译DOM时会查找花括号({some expression})内的表达式。这些表达式会被自动注册到
        $watch服务中并更新到$digest循环中,成为它的一部分。
        <-- update when 'someExpression' on the$scope is updated -->
        <h1>hello{{someExpression}}</h1>
        有时浏览器会对属性进行很苛刻的限制。SVG就是一个例子:
            <svg>
                <circle cx="{{cx}}"></circle>
            </svg>    
        运行这个代码会抛出一个错误,指出有一个非法属性。可以用ng-attr-cx来解决这个问题。注意,cx位于
        这个名称的尾部。
            <svg>
                <circle ng-attr-cx="{{cx}}"></circle>
            </svg>
#### 指令详解 ####
    指令:在特定DOM元素上运行的函数,指令可以扩展这个元素的功能。
    指令的工厂函数只会在编译器第一次匹配到这个指令时调用一次。和controller函数类似,通过
    $injector.invoke来调用指令的工厂函数。
    当AngularJS在DOM中遇到具名的指令时,会去匹配已经注册过的指令,并通过名字在注册过的对象中查找。此时,
    就开始了一个指令的生命周期,指令的生命周期开始于$complie方法并结束语link方法。
    angular.module('myApp', []).directive('myDirective', function() {
        return {
            restrict: String,
            priority: Number,
            terminal: Boolean,
            template: String or Template Function:function(tElement, tAttrs) (...},
            templateUrl: String,
            replace: Boolean or String,
            scope: Boolean or Object,
            transclude: Boolean,
            controller: String or function(scope, element, attrs, transclude, otherInjectables) { ... },
            controllerAs: String,
            require: String,
            link: function(scope, iElement, iAttrs) { ... },
            compile: // 返回一个对象或连接函数,如下所示:
                function(tElement, tAttrs, transclude) {
                    return {
                        pre: function(scope, iElement, iAttrs, controller) { ... },
                        post: function(scope, iElement, iAttrs, controller) { ... }
                    }
                    // 或者
                    return function postLink(...) { ... }
            }
        };
    });
    1.restrict(字符串)//'A'(属性),'E'(元素),'C'(类名),'M'(注释),可以单独使用,也可以混用
    2.priority(优先级),数值型,默认为0
        如果一个元素上具有两个优先级相同的指令,声明在前面的那个会被优先调用。如果其中一个的优先级更高,
        则不管声明的顺序如何都会被优先调用:具有更高优先级的指令总是优先运行。
    3.terminal(布尔型)
        告诉AngularJS停止运行当前元素上比本指令优先级低的指令。
        使用了terminal参数的例子是ngView和ngIf。 ngIf的优先级略高于ngView,如果ngIf的表达式值为true,
         ngView就可以被正常执行,但如果ngIf表达式的值为false,由于ngView的优先级较低就不会被执行。
    4.template(字符串或函数)
    template参数是可选的,必须被设置为一下两种形式之一:
        .'一段HTML文本';
        .一个可以接受两个参数的函数,参数为tElement和tAttrs,并返回一个代表模板的字符串。tElement和
        tAttrs中的t代表template,是相对于instance的。
    5.templateUrl(字符串或函数)
    template是可选的参数,可以是一下的类型:
        .一个代表外部HTML文件路径的字符串;
        .一个可以接受两个参数的函数,参数为tElement和tAttrs,并返回一个外部Html文件路径的字符串。
    6.replace(布尔型)
        replace是一个可选参数,如果设置了这个参数,值必须为true,因为默认值为false。默认值意味着模板会
        被当作子元素插入到调用此指令的元素内部。
    **指令的作用域**
        $rootScope这个特殊的对象会在DOM中声明ng-app时被创建。
    1.scope参数
    scope参数是可选的,可以被设置为true或一个对象。默认值是false。
    当scope设置为true时,会从父作用域继承并创建一个新的作用域对象。
    如果一个元素上有多个指令使用了隔离作用域,其中只有一个可以生效。只有指令模板中的根元素可以获得一个
    新的作用域。因此,对于这些对象来说scope默认被设置为true。
    内置指令ng-controller的作用,就是从父级作用域继承并创建一个新的子作用域。它会创建一个新的从父作用
    域继承而来的子作用域。
    2.隔离作用域
    具有隔离作用域的指令最主要的使用场景是创建可复用的组件,组件可以在未知上下文中使用,并且可以避免污染
    所处的外部作用域或不经意的污染内部作用域。
    创建具有隔离作用域的指令需要将scope属性设置为一个空对象{}.如果这样做了,指令的模板就无法访问外部
    作用域了:
        <div ng-controller="MainController">
            Outside myDirective : {{myProperty}}
            <div my-directive ng-init="myProperty='wow,this is cool'">
                Inside myDirective : {{myProperty}}
            </div>
        </div>    
        angular.module('myApp',[]).controller('MainController',function($scope){})
            .directive('myDirective',function(){
                return {
                    restrict : 'A',
                    scope : {},
                    priority : 100,
                    template : '<div>inside myDirective {{myProperty}}</div>'
                };
            });
        注意,这里为myDirective设置了一个高优先级。由于ngInit指令会以非零的优先级运行,这个例子将会
        优先运行ngInit指令,然后才是定义的指令,并且这个myProperty在$scope对象中是有效的。
    3.绑定策略
    本地作用域属性:@
    双向绑定:=(or =attr)
    父级作用域绑定:通过&符号可以对父级作用域进行绑定,以便在其中运行函数。意味着对这个值进行设置时会生成
    一个指向父级作用域的包装函数。&(or &attr)
    例如,在开发一个电子邮件客户端,并且要创建一个电子邮件的文本输入框:
        <input type="text" ng-model="to" />
        <!--调用指令-->
        <div scope-example ng-model="to" on-send="sendMail(email)" 
            from-name="ari@fullstack.io"/>
        </div>
    这里有一个数据模型(ng-model),一个函数(sendMail())和一个字符串(from-name),在指令中做以下设置
    以访问以下内容。
    scope:{
        ngModel:"=",   //将ng-model同指定对象绑定
        onSend:'&',   //将引用传递给这个方法
        fromName : '@'//存储与fromName相关联的字符串
    }
    4.transclude
    transclude是一个可选的参数。如果设置了,其值必须为true,它的默认值是flase。
    嵌入链接函数会与对应的嵌入作用域进行预绑定。
    transclude链接函数是实际被执行用来克隆元素和操作DOM的函数。
    在控制器内部操作DOM是和AngularJS风格相悖的做法,但通过链接函数就可以实现这个需求。仅在compile参数
    中使用transclude是推荐的做法。
    例子:通过指令来添加一个超链接标签。可以在控制器内的$transclude函数中实现:
        angular.module("myApp").directive('link',function(){
            return {
                restrict : 'A',
                transclude : true,
                controller :
                    function($scope,$element,$transclude,$log){
                        $transclude(function (clone){
                            var a = angular.element('<a>');
                            a.attr('href',clone.text());
                            a.text(clone.text());
                            $log.info("Create new a tag in link directive");
                            $element.append(a);
                        });
                    }
                };
            });
    5.controllerAs(字符串)
        controllerAs参数用来设置控制器的别名,可以一次为名来发布控制器,并且作用域可以访问
        controllerAs。这样就可以在视图中引用控制器,甚至无需注入$scope.
        创建一个controller,不要注入$scope:
            angular.module("myApp",[]).controller("MainController",function(){
                this.name = "Ari";
            });
        现在,在html中无需引用作用域就可以使用MAinController:
            <div ng-appng-controller="MainControllerasmain">
                <input type="text" ng-model="main.name" />
                <span>{{main.name}}</span>
            </div>
        controllerAs的强大能力在于可以在路由和指令中创建匿名控制器,可以将动态的对象创建为控制器,并且
        这个对象是隔离的、易于测试的。
        例如,在指令中创建匿名控制器:
            angular.module('myApp').directive('myDirective',function(){
                return {
                    restrict : 'A',
                    template : '<h4>{{myController.msg}}</h4>',
                    controllerAs : 'myController',
                    controller : function () {
                        this.msg = 'hello';
                    }
                };
            });
    6.require(字符串或数组)
        require参数可以被设置为字符串或数组,字符串代表另外一个指令的名字。 require会将控制器注入到其
    值所指定的指令中,并作为当前指令的链接函数的第四个参数。
        字符串或数组元素的值是会在当前指令的作用域中使用指令的名称。
        scope会影响指令作用域的指向,是一个隔离作用域,一个有依赖的作用域或者完全没有作用域。在任何情况
    下, AngularJS编译器在查找子控制器时都会参考当前指令的模板。
        require参数的值可以用下面的前缀进行修饰,这会改变查找控制器时的行为:
            ?
            如果在当前指令中没有找到所需要的控制器,会将null作为传给link函数的第四个参数。
            ^
            如果添加了^前缀,指令会在上游的指令链中查找require参数所指定的控制器。
            ?^
            将前面两个选项的行为组合起来,我们可选择地加载需要的指令并在父指令链中进行查找。
            没有前缀
            如果没有前缀,指令将会在自身所提供的控制器中进行查找,如果没有找到任何控制器(或具有指定名字
        的指令)就抛出一个错误。
    **AngularJS的生命周期**
    1.编译阶段
        在编译阶段,AngularJS会遍历整个html文档并根据js中的指令定义来处理页面上生命的指令。
    2.compile(对象或函数)
        compile选项本身并不会被频繁使用,但是link函数会被经常使用。本质上,当设置了link选项,实际上
    是创建了一个postLink()链接函数,以便compile()函数可以定义链接函数。
        compile和link选项是互斥的。如果同时设置了这两个选项,那么会把compile所返回的函数当做链接函数,
    而link选项本身则会被忽略。
        compile : function (tEle,tAttrs,transcludeFn){
            var tplE1 = angular.element('<div>' + '<h2></h2>' + '</div>');
            var h2 = tplE1.find('h2');
            h2.attr('type',tAttrs.type);
            h2.attr('ng-model',tAttrs.ngModel);
            h2.val('hello');
            tEle.replaceWith(tplE1);
            return function(scope,ele,attrs){
                //连接函数
            };
        }
    3.链接
        用link函数创建可以操作DOM的指令。
    4.ngModel
        ngModel是一个语法特殊的指令,它提供更底层的API来处理控制器内的数据,当在指令中使用ngModel时能够
    访问一个特殊的API,这个API用来处理数据绑定、验证,css更新的等不实际操作DOM的事情。
        ngModel控制器会随ngModel被一直注入到指令中,其中包含了一些方法,为了访问ngModelController
    必须使用required设置:
        angular.module('myApp').directive('myDirective',function(){
            return {
                required : '?ngModel',
                link : function (scope,ele,attrs,ngModel){
                    if(!ngModel) return;
                    //现在指令中已经有ngModelController的一个实例
                }
            }; 
        });
        注意:如果不设置required选项,ngMOdelController就不会被注入到指令中
    4.1.自定义渲染
        在控制器中定义$reader方法可以定义视图的具体渲染方式。这个方法会在$parser流水线完成后被调用。
        这个方法会破坏AngularJS的标准工作方式,请谨慎使用:
            angular.module('myApp').directive("myDirective",function(){
                retrn {
                    required : '?ngModel',
                    link : function (scope,ele,attrs,ngModel){
                        if(!ngModel) return;
                        ngModel.$reader = function () {
                            element.html(ngModel.$viewValue() || 'None');
                        };
                    }
                };
            });
    4.2.属性
        ngModelController中有几个属性可以用来检查甚至修改视图。
    1.$viewValue:保存着更新视图所需的实际字符串
    2.$modelValue
        $modelValue是由数据模型持有。$modelValue和$viewValue可能是不同的,取决于$parser流水线是否
    对其进行了操作。
    3.$parsers
        $parsers的值是一个由函数组成的数组,其中函数会以流水线的形式被逐一调用。ngModel从DOM中读取的值
    会被传入$parsers中的函数,并以此被其中的解析器处理。
    4.formatters
        $formatters的值是一个由函数组成的数组,其中的函数会以流水线的形式在数据模型的值发生变化时被逐一
    调用。它和$parser流水线互不影响,用来对值进行格式化和转换,以便在绑定了这个值的控件中显示。
    5.$viewChangeListeners
        $viewChangeListeners的值是一个由函数组成的数组,其中的函数会以流水线的形式在视图中的值发生变化
    时被逐一调用。通过$viewChangeListeners,可以在无需使用$watch的情况下实现类似的行为。由于返回值会被
    忽略,因此这些函数不需要返回值。
    6.$error
    $error对象中保存着没有通过验证的验证器名称以及对应的错误信息。
    7.$pristine
    $pristine的值是布尔型的,可以告诉我们用户是否对控件进行了修改。
    8.$dirty
    $dirty的值和$pristine相反,可以告诉我们用户是否和控件进行过交互。
    9.$valid
    $valid值可以告诉我们当前的控件中是否有错误。当有错误时值为false, 没有错误时值为true。
    10. $invalid
    $invalid值可以告诉我们当前控件中是否存在至少一个错误,它的值和$valid相反。
#### AngularJS模块加载 ####
    1.配置
    在某个模块上创建一个服务挥着指令时:
    angular.module('myApp',[]).factory('myFactory',function(){
        var service = {};
        return service;
    })
    .directive('myDirective',function(){
        return {
            template : '<button>Click</button>'
        }
    })
    AngularJS会在编译时执行这些辅助函数。它们在功能上等同于下面的写法:
    angular.module('myApp',[]).config(function($provide,$compileProvider){
        $provide.factory('myFactory',function(){
            var service = {};
            return service;
        });
        $compileProvider.directive('myDirective',function(){
            return {
                template : '<button>Click</button>'
            };
        });
    });
    注意:AngularJS会以这些函数书写和注册的顺序来执行。无法注入一个尚未注册的提供者。唯一例外是
    constant()方法,这个方法总会在所有配置块之前被执行。
    2.运行块
    和配置块不同,运行块在注入器创建之后被执行,它是所有AngularJS应用中第一个被执行的方法
    运行块是AngularJS中与main方法最接近的概念。运行块中的代码块通常很难进行单元测试,它是和应用本身
    高度耦合的。
    运行块通常用来注册全局的事件监听器。
    假设我们需要在每次路由发生变化时,都执行一个函数来验证用户的权限,放置这个功能唯一合理的地方就是run
    方法:
    angular.module('myApp',[]).run(function($rootScope,AuthSerivce){
        $rootScope.$on('$routeChangeStart',function(evt,next,current){
            //如果用户未登陆
            if(!AuthService.userLoggedIn()){
                if(next.templateUrl === "login.html"){
                    //已经转向登陆路由因此无需重定向
                }else{
                    $location.path('/login');
                }
            }
        });
    });
#### 多重视图和路由 ####
    要把ngRoute模块在应用中当做依赖加载进来:
    angular.module('myApp',['ngRoute']);
    1.布局模板
    要创建一个布局模板,需要修改HTML以告诉AngularJS把模板渲染在哪里。通过ng-view指令和路由结合在一起,
    可精确指定当前路由所对应的模板在DOM中的渲染位置。
    <header>
        <h1>Header</h1>
    </header>
    <div class="content">//将要渲染的内容都放在了<div class="content">中,
        <div ng-view><div>//而<header>和<footer>中的内容在路由改变时不会有任何变化
    </div>//ng-view是由ngRoute提供的一个特殊指令,在HTML中给$route对应的视图内容占位
    <footer>
        <h5>Footer</h5>
    </footer>
    ng-view是一个优先级为1000的终极指令。AngularJS不会运行同一个元素上的低优先级指令。
    3.路由
    创建一个独立的路由:
    angular.module('myApp',[]).config(['$routeProvider',function($routeProvider){
        $routeProvider
            .when('/',{
                templateUrl : 'view/home.html',//路由路径,会与$location.path进行匹配
                controller : 'HomeController'//配置对象,决定当第一个参数中的路由匹配了可以做什么
            });
    }]);
    一个复杂的路由方案会包含多个路由,以及一个可以将所有意外路径进行重定向的捕获器。
    angular.module("myApp",[]).
        config(['$routeProvider',function($routeProvider){
            $routeProvider
                .when('/',{
                    templateUrl : 'view/home.html',
                    controller : 'HomeController'
                })
                .when('/login',{
                    templateUrl : 'views/login.html',
                    controller : 'LoginController'
                })
                .when('/dashboard',{
                    templateUrl : 'view/dashboard.html',
                    controller : 'DashboardController',
                    resolve : {
                        user : function (SessionService){
                            return SessionService.getCurrentUser();
                        }
                    }
                })
                .otherwise({
                    redirectTo : '/'
                });
        }]);
    .controller
        controller : 'MyController' / function($scope){}
        如果配置对象中设置了controller属性,那么这个指定的控制器会与路由所创建的新作用域关联在一起。
        如果参数是字符型,会在模块中所有注册过的控制器中查找对应的内容,然后与路由器关联在一起。
        如果参数是函数型,这个函数会作为模板中DOM元素的控制器并与模板进行关联。
    .template
        template: '<div><h2>Route</h2></div>'
        AngularJS会将配置对象中的HTML模板渲染到对应的具有ng-view指令的DOM元素中。
    .templateUrl
        templateUrl : 'views/template_name.html'
        应用会根据templateUrl属性所指定的路径通过XHR读取视图(或从$templateCache中读取)。如果能够找到
    并读取这个模块,AngularJS会将模板的内容渲染到具有ng-view指令的DOM元素中。
    .resolve
        resolve : {
            'data' : ['$http',function($http){
                return $http.get('/api').then(
                    function success(resp){return response.data;},
                    function error(reason){return error;}
                );
            }];
        }
        如果设置了resolve属性,AngularJS会将列表中的元素都注入到控制器中。
    .redirectTo
        redirectTo : '/home' / function(route,path,search){}
        如果redirectTo属性的值是一个字符串,那么路径会被替换成这个值,并根据这个目标路径触发路由变化
        函数:function(从当前路径中提取出的路由参数,当前路径,当前Url中的查询串)
    .reloadOnSearch
        如果reloadOnSearch选项被设置为true(默认),当$location.search()发生变化时会重新加载路由。
    false的话,url中的查询串部分的变化就不会重新加载路由。
    用when函数来设置路由:
        angular.module('myApp',[])
            .config(['$routeProvider',function($routeProvider){
                $routeProvider
                    .when('/',{
                        controller : 'HomeController',
                        templateUrl : 'views/home.html'
                    })
                    .when('/index/:home',{
                        controller : 'InboxController',
                        templateUrl : 'views/index.html'
                    })
                    .otherwise({//在没有任何路由匹配时被调用,默认跳转到'/'路径
                        redirectTo : '/'
                    });
            }]);
    $routeParams
        如果在路由参数前面加上 : ,AngularJS就会把它解析出来并传递给$routeParams.
        $routeProvider
            .when('/index/:name',{
                controller : 'InboxController',
                template : 'view/index.html'
            });
        AngularJS会在$routeParams中添加一个名为home的键,它的值会被设置为加载进来的URL中的值。
        如果浏览器加载/inbox/all这个url,那么$routeParams对象看起来:
        {name : all}
        注意:如果想要在控制器中访问这些变量,需要把$routeParams注入进控制器;
        app.controller('InboxController',function($scope,$routeParams){
            //在这里访问$routeParams
        });
    4.location服务
        $location服务没有刷新整个页面的能力。如果需要,需要使用$window.location对象。
        .path():用来获取页面当前的路径
            $location.path();//返回当前的路径
            $location.path('/');//把路径修改为'/'路由
        .rplace():如果希望跳转后用户不能点击后退按钮,使用replace()方法来实现
        .absUrl():获取编码后的完整URL
        .hash():获取URL中的hash片段
        .host():用来获取URL中的主机
        .port():获取URL中的端口号
        .protocol():获取URL中的协议
        .search():获取URL中的查询串
            可以向这个方法中传入新的查询参数,来修改url中的查询串部分:
            $location.search({name:'Ari',username:'auser'});
            $location.search('name=Ari&username=auser');
        search方法可以接受两个参数:
            search(可选):新的查询参数
            paramValue(可选):如果search参数的类型是字符串,那么paramValue会做为该参数的值覆盖URL当
        中的对应值。如果paramValue的值是null,对应的参数会被移除掉。
        .url():获取或设置当前url
    5.路由模式
        不同的路由模式在浏览器的地址栏中会以不同的url格式呈现。$location服务默认会使用标签模式来进行
        路由。
    .标签模式
        标签(hashbang)是AngularJS用来同应用内部进行连接的技巧。URL路径会以#符号开头。
        使用标签的例子:
        http://yoursite.com/#!/inbox/all
        如果要显式的指定配置并使用标签模式,需要在应用模块的config函数中进行配置:
        angular.module('myApp',['ngRoute'])
            .config(['$locationProvider',function($locatnProvider){
                $locationProvider.html5Mode(false);
            }]);
        还可以配置hasPrefix,也就是标签模式下标签默认的前缀!符号。
        angular.module('myApp',['ngRoute'])
            .config(['$locationProvider',function($locationProvider){
                $locationProvider.html5Model(false);
                $locationProvider.hasPrefix('!');
            }]);
    .html5模式
        同样的路由在html5模式中:
        http://yoursite.com/inbox/all
        在AngularJS内部,$location服务通过html5历史API让应用能够使用普通的URL路径来路由。当浏览
        器不支持HTML5历史API时,$location服务会自动使用标签模式的url作为替代方案。
    .路由事件
        1.$routeChangeStart
        AngularJS在路由变化之前会广播$routeChangeStart事件。在这一步中,路由服务会开始加载路由变化
        所需要的所有依赖,并且模板和resolve键中的promise也会被resolve
            angular.module('myApp',[])
                .run(['$rootScope','$location',function($rootScope,$location){
                    $rootScope.$on('$routeChangeStart',function(evt,next,current){
                    });
                }]);
        $routeChangeStart事件带有两个参数:
            .将要导航到的下一个URL
            .路由变化前的URL
    .$routeChangeSuccess
        AngularJS会在路由的依赖被加载后广播$routeChangeSuccess事件。
        angurlar.module('myApp',[])
            .run(['rootScope','$location',function($rootScope,$location){
                $rootScope.$on('$routeChangeSuccess',function(evt,next,previous){
                });
            }]);
        $routeChangeSuccess事件有三个参数:
            .原始的AngularJS evt 对象
            .用户当前所处的路由
            .上一个路由(如果当前是第一个路由,则为undefined)
    .$routeChangeError
        AngularJS会在任何一个promise被拒绝或者失败时广播$routeChangeError事件。
        angular.module('myApp',[])
            .run(function($rootScope,$location){
                $rootScope.$on('routeChangeError',function(current,previous,rejection){
                });
            });
        三个参数:
            .当前路由的信息
            .上一个路由的信息
            .被拒绝的promise的错误信息
        4.routeUpdate
            AngularJS在reloadSearch属性被设置为flase的情况下,重新使用某个控制器的实例时,会广播
        $routeUpdate事件。
#### 依赖注入 ####
    一个对象通常有三种方式可以获得对其依赖的控制权:
        .在内部创建依赖
        .通过全局变量进行引用
        .在需要的地方通过参数进行传递
        在编写依赖于其他对象或库的组件时,需要描述组件之间的依赖关系。在运行期,注入器会创建依赖的实
    例,并负责将它传递给依赖的消费者。
        function SomeClass(greeter){
            this.greeter = greeter;
        }
        SomeClass.prototype.greetName = function(name){
            this.greeter.greet(name);
        }
        AngularJS使用$injetor(注入器服务)来管理依赖关系的查询和实例化。$injetor负责实例化AngularJS
    中的所有组件,包括应用的模块、指令和控制器等。
        在运行时,任何模块启动时$injetor都会负责实例化,并将其需要的所有依赖传递进去。
        angular.module('myApp',[])
            .factory('greeter',function(){
                return {
                    greet : function(msg){alert(msg);}
                }
            })
            .contrller('MyController',function($scope,greeter){
                $scope.sayHello = function () {
                    greeter.greet('hello');
                };
            });
        在AngularJS实例化这个模式时,会查找greeter并自然而然的把对它的引用传递进去:
        <div ng-app="myApp">
            <div ng-controller='MyController'>
                <button ng-click="sayHello()">hello</button>
            </div>
        </div>
        而在内部,AngularJS的处理过程如下:
        //使用注入器加载应用
        var injector = angular.injector(['ng','myApp']);
        //通过注入器加载$controller服务:var $controller = injector.get('$controller');
        var scope = injector.get('$rootScope').$new();
        //加载控制器并传入一个作用域,同AngularJS在运行时做的一样
        var MyController = $controller('MyController',{$scope:scope})
        上面的代码并没有说明是如何找到greeter的,但它的确能正常工作,因为$injector会负责查找并加载。
            AngularJS通过annotate函数,在实例化时从传入的函数中把参数列表提取出来。在Chrome的开发者
        工具中输入下面的代码可以查看到:
            > injector.annotate(function($q,greeter){})
            ['$q','greeter']
        当编写控制器时,如果没有使用[]标记或进行显式的声明,$injector就会尝试通过参数名推断依赖关系。
    .推断式注入声明
        如果没有明确的声明,AngularJS会假定参数名称就是依赖的名称。因此会在内部调用函数对象的
        toString()方法,分析并提取出函数参数列表,然后通过$injector将这些参数注入进对象实例。
            injector.invoke(function($http,greeter){});
    ·显式注入声明
        var aControllerFactory = function aController($scope,greeter){
            console.log('LOADED controller',greeter);
            //...控制器
        } ;
        aControllerFactory.$inject = ['$scope','geeter'];//Greeter服务
        angular.module('myApp',[])
            .controller('MyController',aControllerFactory)
            .factory('greeter',greeterService);
        var injector = angular.injector(['ng','myApp']),
            controller = injector.get('$controller'),
            rootScope = injector.get('$rootScope'),
            newScope = rootScope.$new();
    .injectorAPI
        1.annotate():返回一个由服务组成的数组,这些服务会在实例化时被注入到目标函数中。
        

posted @ 2017-03-16 16:40  Ruuudy  阅读(296)  评论(0)    收藏  举报