• AngularJS 模块加载


    AngularJS模块可以在被加载和执行之前对其自身进行配置。我们可以在应用的加载阶段应用不同的逻辑组。
    在模块的加载阶段, AngularJS会在提供者注册和配置的过程中对模块进行配置。在整个AngularJS的工作流中,这个阶段是唯一能够在应用启动前进行修改的部分。

    config()函数接受一个参数。

    angular.module('myApp', [])
      .config(function($provide) {
    });

    使用config()函数的语法糖,并在配置阶段执行。例如,我们在某个模块之上创建一个服务或指令时:

    angular.module('myApp', [])
           .factory('myFactory', function(){
                var service = {};
                return service;
         }).directive('myDirective', function(){
                return {
                    template: '<button>Click me</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 me</button>'
                    };
                });
    });

    AngularJS会以这些函数书写和注册的顺序来执行它们。

     唯一例外的是constant()方法,这个方法总会在所有配置块之前被执行。

    当对模块进行配置时,只有少数几种类型的对象可以被注入到config()函数中:提供者和常量。

    这种对配置服务进行严格限制的另外一个副作用就是,我们只能注入用provider()语法构建的服务,其他的则不行。
    定义多个配置块,它们会按照顺序执行,这样就可以将应用不同阶段的配置代码集中在不同的代码块中

    angular.module('myApp', [])
           .config(function($routeProvider) {
                $routeProvider.when('/', {
                    controller: 'WelcomeController',
                    template: 'views/welcome.html'
                });
            })
           .config(function(ConnectionProvider) {
                ConnectionProvider.setApiKey('SOME_API_KEY');
            });

     运行块

    和配置块不同,运行块在注入器创建之后被执行,它是所有AngularJS应用中第一个被执行的方法。run
    运行块是AngularJS中与main方法最接近的概念。运行块通常用来注册全局的事件监听器。例如,我们会在.run()块中设置路由事件的监听器以及过滤未经授权的请求。

    假设我们需要在每次路由发生变化时,都执行一个函数来验证用户的权限,放置这个功能唯一合理的地方就是run方法:

    angular.module('myApp', [])
           .run(function($rootScope, AuthService) {
                $rootScope.$on('$routeChangeStart', function(evt, next, current) {
                    // 如果用户未登录
                    if (!AuthService.userLoggedIn()) {
                        if (next.templateUrl === "login.html") {
                        // 已经转向登录路由因此无需重定向
                        } else {
                        $location.path('/login');
                        }
                    }
                });
            });

    多重视图和路由

      从1.2版本开始, AngularJS将ngRoutes从核心代码中剥离出来成为独立的模块。我们需要安装并引用它,才能够在AngularJS应用中正常地使用路由功能

    创建一个独立的路由:用when方法来添加一个特定的路由。有两个参数(when(path,route))。

    angular.module('myApp', []).
           .config(['$routeProvider', function($routeProvider) {
                $routeProvider.when('/', {
                    templateUrl: 'views/home.html',
                    controller: 'HomeController'
                });
            }]);

    稍微复杂一点:

    angular.module('myApp', []).
           .config(['$routeProvider', function($routeProvider) {
                $routeProvider.when('/', {
                    templateUrl: 'views/home.html',
                    controller: 'HomeController'
                })
                .when('/login', {
                    templateUrl: 'views/login.html',
                    controller: 'LoginController'
                })
                .when('/dashboard', {
                    templateUrl: 'views/dashboard.html',
                    controller: 'DashboardController',
                    resolve: {
                        user: function(SessionService) {
                            return SessionService.getCurrentUser();
                        }
                    }
                })
                .otherwise({
                    redirectTo: '/'
                });
            }]);

     1. controller     controller: 'MyController'  或者   controller: function($scope) {}

    如果配置对象中设置了controller属性,制器会与路由所创建的新作用域关联在一起。如果参数值是字符型,会在模块中所有注册过的控制器中查找,如果参数值是函数,函数会作为模板中DOM元素的控制器并与模板进行关联

    2. template
    template: '<div><h2>Route</h2></div>'    渲染到对应的具有ng-view指令的DOM元素中

    5. redirectTo
    redirectTo: '/home'
    // 或者
    redirectTo: function(route,path,search)
    如果redirectTo属性的值是一个字符串,那么路径会被替换成这个值,如果redirectTo属性的值是一个函数,那么路径会被替换成函数的返回值,并根据这个目标路径触发路由变化。

    $routeParams

    如果我们在路由参数的前面加上:, AngularJS就会把它解析出来并传递给$routeParams。

    $routeProvider.when('/inbox/:name', {
        controller: 'InboxController',
        templateUrl: 'views/inbox.html'
    })

    在$routeParams中添加一个名为name的键,它的值会被设置为加载进来的URL中的值。如果浏览器加载/inbox/all这个URL,$routeParams:{  name:'all'  }
    如果想要在控制器中访问这些变量,需要把$routeParams注入进控制器:

    app.controller('InboxController',function($scope,$routeParams) {
        // 在这里访问$routeParams
    });;

    $location 服务

    用以解析地址栏中的URL,可以访问应用当前路径所对应的路由。它同样提供了修改路径和处理各种形式导航的能力。
    $location服务对JavaScript中的window.location对象的API进行了更优雅地封装,并且和AngularJS集成在一起。

    当应用需要在内部进行跳转时是使用$location服务的最佳场景,比如当用户注册后、修改或者登录后进行的跳转.
    $location服务没有刷新整个页面的能力。如果需要刷新整个页面,需要使用$window.location对象(window.location的一个接口)。
    $location.path(); // 返回当前路径

    $location.path('/'); // 把路径修改为'/'路由

    如果你希望跳转后用户不能点击后退按钮.AngularJS提供了replace()方法来实现

    $location.path('/home');
    $location.replace();
    // 或者
    $location.path('/home').replace();

    路由模式

    路由模式决定你的站点的URL长成什么样子。

    标签模式

     标签模式是HTML5模式的降级方案, URL路径会以#符号开头。  使用标签模式的URL看起来是这样的:http://yoursite.com/#!/inbox/all
    要显式指定配置并使用标签模式,需要在应用模块的config函数中进行配置:

    angular.module('myApp', ['ngRoute'])
           .config(['$locationProvider', function($locationProvider) {
                $locationProvider.html5Mode(false);
           $locationProvider.hashPrefix('!'); //这一句要不要都行 }]);

    HTML5 模式

     $location服务通过HTML5历史API让应用能够使用普通的URL路径来路由。当浏览器不支持HTML5历史API时, $location服务会自动使用标签模式的URL作为替代方案。

    在HTML5模式中, AngularJS会负责重写<a href=""></a>中的链接。也就是说AngularJS会根据浏览器的能力在编译时决定是否要重写href=""中的链接

     例如<a href="/person/42?all=true">Person</a>这个标签,在老式浏览器中会被重写成标签模式的URL: /index.html#!/person/42?all=true。但在现代浏览器中会URL会保持本来的样子

    当在HTML5模式的AngularJS中写链接时,永远都不要使用相对路径。如果你的应用是在根路径中加载的,这不会有什么问题,但如果是在其他路径中, AngularJS应用就无法正确处理路由了。

    12.5.2 路由事件

    $route服务在路由过程中的每个阶段都会触发不同的事件,可以为这些不同的路由事件设置监听器并做出响应。
    我们需要给路由设置事件监听器,用$rootScope来监听这些事件。

    1. $routeChangeStart

    在路由变化之前会广播  $routeChangeStart事件,路由服务会开始加载路由变化所需要的所有依赖,并且模板和resolve键中的promise也会被resolve。

    angular.module('myApp', [])
           .run(['$rootScope', '$location', function($rootScope, $location) {
                $rootScope.$on('$routeChangeStart', function(evt, next, current) {
                    
                });
            }]);

    $routeChangeStart事件带有两个参数:
       将要导航到的下一个URL;     路由变化前的URL。

    2. $routeChangeSuccess
    AngularJS会在路由的依赖被加载后广播$routeChangeSuccess事件。

    angular.module('myApp', [])
           .run(['$rootScope', '$location', function($rootScope, $location) {
                $rootScope.$on('$routeChangeSuccess', function(evt, next, previous) {
                    
                });
            }]);

    $routeChangeStart事件带有三个参数:  

       原始的AngularJS evt对象;     用户当前所处的路由;     上一个路由(如果当前是第一个路由,则为undefined)。

    3. $routeChangeError
    AngularJS会在任何一个promise被拒绝或者失败时广播$routeChangeError事件。

    angular.module('myApp', [])
           .run(function($rootScope, $location) {
                $rootScope.$on('$routeChangeError', function(current, previous, rejection) {
                    
                });
            });

    $routeChangeError事件有三个参数:
       当前路由的信息;     上一个路由的信息;     被拒绝的promise的错误信息。

    依赖注入

    一个对象通常有三种方式可以获得对其依赖的控制权:
    (1) 在内部创建依赖;
    (2) 通过全局变量进行引用;
    (3) 在需要的地方通过参数进行传递。

    依赖注入是通过第三种方式实现的。其余两种方式会带来各种问题,例如污染全局作用域,使隔离变得异常困难等。

    依赖注入会事先自动查找依赖关系,并将注入目标告知被依赖的资源,这样就可以在目标需要时立即将资源注入进去。

     在运行期,注入器会创建依赖的实例,并负责将它传递给依赖的消费者。

    // 出自Angular文档的优秀示例
    function SomeClass(greeter) {
        this.greeter = greeter;
    }
    SomeClass.prototype.greetName = function(name) {
        this.greeter.greet(name);
    };
    
    //示例代码在全局作用域上创建了一个控制器,这并不是一个好主意,这里只是为了方便演示。

    SomeClass能够在运行时访问到内部的greeter,不关心如何获得对greeter的引用。为了获得对greeter实例的引用, SomeClass的创建者会负责构造其依赖关系并传递进去。


    AngularJS使用$injetor(注入器服务)来管理依赖关系的查询和实例化。$injetor负责实例化AngularJS中所有的组件,包括应用的模块、指令和控制器等

     在运行时, 任何模块启动时$injetor都会负责实例化,并将其需要的所有依赖传递进去。

     一个简单的应用,声明了一个模块和一个控制器:

    angular.module('myApp', [])
           .factory('greeter', function() {
                return {
                    greet: function(msg) {alert(msg);}
                }
            })
            .controller('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']);    // 使用注入器加载应用
    var $controller = injector.get('$controller');        // 通过注入器加载$controller服务: 
    var scope = injector.get('$rootScope').$new();
    // 加载控制器并传入一个作用域,同AngularJS在运行时做的一样
    var MyController = $controller('MyController', {$scope: scope})

    代码中没有说明是如何找到greeter,但是它的确能正常工作,因为$injector会负责为我们查找并加载它。
    通过annotate函数,在实例化时从传入的函数中把参数列表提取出来。


    任何一个AngularJS的应用中,都有$injector在进行工作,当编写控制器时,如果没有使用[]标记或进行显式的声明, $injector就会尝试通过参数名推断依赖关系

    推断式注入声明

     如果没有明确的声明, AngularJS会假定参数名称就是依赖的名称。因此,它会在内部调用函数对象的toString()方法,分析并提取出函数参数列表,然后通过$injector将这些参数注入进对象实例。

    例如: 注入过程如下:  injector.invoke(function($http, greeter) {});    只适用于未经过压缩和混淆的代码,因为AngularJS需要原始未经压缩的参数列表来进行解析。(此时,参数的顺序 没有什么 意义了)

    显式注入声明

    显式的方法来明确定义一个函数在被调用时需要用到的依赖关系。通过这种方法声明依赖,即使在源代码被压缩、参数名称发生改变的情况下依然能够正常工作。

    通过$inject属性来实现显式注入声明的功能。函数对象的$inject属性是一个数组,数组元素的类型是字符串,它们的值就是需要被注入的服务的名称。

    var aControllerFactory =function aController($scope, greeter) {
            console.log("LOADED controller", greeter);
    // ……控制器
    };
    aControllerFactory.$inject = ['$scope', 'greeter']; // 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();
        
    // 调用控制器
    controller('MyController', {$scope: newScope});

    这种声明方式来讲,参数顺序是非常重要的,$inject数组元素的顺序必须和注入参数的顺序一一对应。这种声明方式可以在压缩后的代码中运行,

    行内注入声明

    行内声明的方式允许我们直接传入一个参数数组而不是一个函数。数组的元素是字符串,它们代表的是可以被注入到对象中的依赖的名字,最后一个参数就是依赖注入的目标函数对象本身。

    angular.module('myApp')
           .controller('MyController', ['$scope', 'greeter', function($scope, greeter) {
    
            }]);

    处理的是一个字符串组成的列表,行内注入声明也可以在压缩后的代码中正常运行。  也是有顺序的

    服 务:

    出于内存占用和性能的考虑,控制器只会在需要时被实例化,并且不再需要就会被销毁。这意味着每次切换路由或重新加载视图时,当前的控制器会被AngularJS清除掉。
    服务提供了一种能在应用的整个生命周期内保持数据的方法,它能够在控制器之间进行通信,并且能保证数据的一致性。(能够在控制器中 复用)
    服务是一个单例对象,在每个应用中只会被实例化一次(被$injector实例化),并且是延迟加载的(需要时才会被创建)。

    以AngularJS的$http服务为例,提供了对浏览器的XMLHttpRequest对象的底层访问功能,通过$http的API同XMLHttpRequest进行交互,而不需要因为调用这些底层代码而污染应用。

    // 示例服务,在应用的整个生命周期内保存current_user
    angular.module('myApp', [])
           .factory('UserService', function($http) {
                var current_user;
                return {
                    getCurrentUser: function() {
                        return current_user;
                    },
                    setCurrentUser: function(user) {
                        current_user = user;
                    }
                };
            });

    在AngularJS中创建自己的服务是非常容易的:只需要注册这个服务即可。服务被注册后,AngularJS编译器就可以引用它,并且在运行时把它当作依赖加载进来。

    注册一个服务

    使用angular.module的factory API创建服务,是最常见也是最灵活的方式:

    angular.module('myApp.services', [])
           .factory('githubService', function() {
                var serviceInstance = {};
                // 我们的第一个服务
                return serviceInstance;
            });

    用githubService作为名字注册成为这个AngularJS应用的一个服务了。

    服务的工厂函数用来生成一个单例的对象或函数,这个对象或函数就是服务,它会存在于应用的整个生命周期内。当我们的AngularJS应用加载服务时,这个函数会被执行并返回一个单例的服务对象

    同创建控制器的方法一样,服务的工厂函数既可以是一个函数也可以是一个数组:

    // 用方括号声明工厂,声明了一个 服务: githubService
    angular.module('myApp.services', [])
           .factory('githubService', [function($http) { }]);

    使用服务

    可以在控制器、指令、过滤器或另外一个服务中通过依赖声明的方式来使用服务。

    将服务的名字当作参数传递给控制器函数,可以将服务注入到控制器中。当服务成为了某个控制器的依赖,就可以在控制器中调用任何定义在这个服务对象上的方法。

    angular.module('myApp', ['myApp.services'])
           .controller('ServiceController', function($scope, githubService) {
                // 我们可以调用对象的事件函数
                $scope.events = githubService.events('auser');
            });

      githubService服务已经被注入到ServiceController中,可以像使用任何其他服务一样使用它.

    //自己修改后的,原来的代码,用到$http 所以就没用。了解原理就行
    angular.module('services', [])
           .factory('githubService', function($http) {
                // 返回带有一个events函数的服务对象
                return {
                    events: function(username) {
                        return [{actor:"一号",name:"Linda"},{actor:"二号",name:"Joke"},
                        {actor:"三号",name:"elm"},{actor:"四号",name:"LIm"}];
                    }
                }
            });
    
    var app = angular.module("app",['services']);
    app.controller('ServiceController', function($scope,githubService) { 
            //     注意username属性的变化,    如果有变化就运行该函数    
            $scope.$watch('username',function(newname){
                console.log($scope.count);
                // 从使用JSONP调用Github API的$http服务中返回promise
                $scope.events = githubService.events(newname);
                /*.success(function(data, status, headers) {
                    // success函数在数据中封装响应
                    // 因此我们需要调用data.data来获取原始数据
                    $scope.events = data.data;
                })*/
            });
        });

    <div ng-controller="ServiceController">
      <label for="username">Type in a GitHub username</label>
      <input type="text" ng-model="username" placeholder="Enter a GitHub username" /><br>
      <ul>
        <li ng-repeat="event in events">
        {{ event.actor }} {{ event.name }}
        </li>
      </ul>
    </div>

    不推荐在控制器中使用$watch,这里只是为了方便演示。在实际生产中会将这个功能封装进一个指令,并在指令中设置$watch

    内置服务$timeout来介绍一下这个延时。同注入githubService一样,需要将$timeout服务注入到控制器中:
      app.controller('ServiceController', function($scope, $timeout, githubService) {
    });  

    我觉得最好写成这样: 行内形式声明

    app.controller('ServiceController',['$scope','$timeout',"githubService",function($scope, $timeout, githubService) {
        }]);

    在自定义服务之前注入所有的AngularJS内置服务,这是约定俗成的规则。

    $timeout服务会取消所有网络请求,并在输入字段的两次变化之间延时350 ms。换句话说,如果用户两次输入之间有350 ms的间隔,就推断用户已经完成了输入,然后开始向GitHub发送请求:

    app.controller('ServiceController', function($scope, $timeout, githubService) {
            // 和上面的示例一样, 添加了$timeout服务
            var timeout;
            $scope.$watch('username', function(newUserName) {
                if (newUserName) {
                    // 如果在进度中有一个超时(timeout)
                    if (timeout) $timeout.cancel(timeout);
                        timeout = 
                        $timeout(function() {
                            githubService.events(newUserName)
                                    .success(function(data, status) {
                                        $scope.events = data.data;
                                    });
                                }, 350);
                        }
                });
            });

     在控制器之间共享数据,需要在服务中添加一个用来储存用户名的方法。服务在应用的生命周期内是单例模式的,因此可以将用户名安全地储存在其中。

    angular.module('app',[])
           .factory('githubService', function($http) {
                var githubUsername;
                var runUserRequest = function(path) {
                        // 从使用JSONP的Github API的$http服务中返回promise
                        return $http({    });
                    };
            
                return {
                    events: function() {
                        return runUserRequest('events');
                    },
                    setUsername: function(username) {
                        githubUsername = username;
                    }
                };
            });
    //服务中有一个setUsername方法,用来保存当前的GitHub用户名了。
    //可以来获取用户名
    angular.module('myApp', ['myApp.services'])
           .controller('ServiceController',
                function($scope, githubService) {
                    $scope.setUsername = githubService.setUsername;
    });

    创建服务时的设置项

    有5种方法用来创建服务:
     factory()
     service()
     constant()
     value()
     provider()

    factory()    是创建和配置服务的最快捷方式。 接受两个参数, name(字符串)  服务名,   getFn(函数)或者   一个包含可被注入对象的数组  【一个特殊的数组,前面是字符串,最后一个是函数】,  创建服务实例时被调用。

    angular.module('myApp')
           .factory('myService', function() {
                return {
                    'username': 'auser'
                };
            })
    angular.module('myApp')
           .factory('githubService', ['$http', function($http) {
                return {
                    getUserEvents: function(username) {
                        
                    }
                };
            }]);

    服务是单例对象, getFn在应用的生命周期内只会被调用一次。

    service()

    一个支持构造函数的服务,它允许我们为服务对象注册一个构造函数
    两个参数。   name(字符串)   constructor(函数)  构造函数,我们调用它来实例化服务对象。
    service()函数会在创建实例时通过new关键字来实例化服务对象。

    var Person = function($http) {
        this.getName = function() {
            return $http({ method: 'GET', url: '/api/user'});
        };
    };
    angular.service('personService', Person);

    provider()
    所有服务工厂都是由$provide服务创建的, $provide服务负责在运行时初始化这些提供者。提供者是一个具有$get()方法的对象, $injector通过调用$get方法创建服务实例。
    $provider提供了数个不同的API用于创建服务,每个方法都有各自的特殊用途

    所有创建服务的方法都构建在provider方法之上。 provider()方法负责在$providerCache中注册服务。

    我们假定传入的函数就是$get()时, factory()函数就是用provider()方法注册服务的简略形式。

    //两种方法的作用完全一样,并且会创建同一个服务
    
    angular.module('myApp')
           .factory('myService', function() {
                return {
                    'username': 'auser'
                };
            })
            // 这与上面工厂的用法等价
            .provider('myService', {
                $get: function() {
                    return {
                        'username': 'auser'
                    };
                }
            });

    是否可以一直使用.factory()方法来代替.provider()呢?

    取决于是否需要用AngularJS的.config()函数来对.provider()方法返回的服务进行额外的扩展配置。config()方法可以被注入特殊的参数。

    // 使用`.provider`注册该服务
    angular.module('myApp', [])
           .provider('githubService', function($http) {
                // 默认的,私有状态
                var githubUrl = 'https://github.com',
                setGithubUrl: function(url) {
                    // 通过.config改变默认属性
                    if (url) { githubUrl = url }
                },
                method: JSONP, // 如果需要,可以重写
                $get: function($http) {
                    self = this;
                    return $http({ method: self.method, url: githubUrl + '/events'});
                }
            });

    通过使用.provider()方法,可以在多个应用使用同一个服务时获得更强的扩展性,特别是在不同应用或开源社区之间共享服务时。
    如果希望在config()函数中可以对服务进行配置,必须用provider()来定义服务

    provider()  两个参数。   name(字符串)  name参数在providerCache中是注册的名字。name+Provider会成为服务的提供者。同时name也是服务实例的名字
                aProvider(对象/函数/数组)    

                    如果aProvider是函数,那么它会通过依赖注入被调用,并且负责通过$get方法返回一个对象
                    如果aProvider是数组,会被当做一个带有行内依赖注入声明的函数来处理。数组的最后一个元素应该是函数,可以返回一个带有$get方法的对象。
                    如果aProvider是对象,它应该带有$get方法。

       provider()函数返回一个已经注册的提供者实例。

    直接使用provider() API是最原始的创建服务的方法:
    // 在模块对象上直接创建provider的例子
    angular.module('myApp', [])
           .provider('UserService', {
                favoriteColor: null,
                setFavoriteColor: function(newColor) {
                    this.favoriteColor = newColor;
                },
                // $get函数可以接受injectables
                $get: function($http) {
                    return {
                        'name': 'Ari',
                        getFavoriteColor: function() {
                            return this.favoriteColor || 'unknown';
                        }
                    };
                }
            });

    用这个方法创建服务,必须返回一个定义有$get()函数的对象,否则会导致错误。

    可以通过注入器来实例化服务

    var injector = angular.injector(['myApp']); // Invoke our service
    injector.invoke(['UserService', function(UserService) {
                // UserService returns
                // {
                //         'name': 'Ari',
                //         getFavoriteColor: function() {}
                // }
            }]);

    constant()       

    将一个已经存在的变量值注册为服务,并将其注入到应用的其他部分当中。constant()方法返回一个注册后的服务实例。

    angular.module('myApp') .constant('apiKey','123123123');

    这个常量服务可以像其他服务一样被注入到配置函数中:

    angular.module('myApp')
           .controller('MyController', function($scope, apiKey) {
                // 可以像上面一样用apiKey作为常量
                // 用123123123作为字符串的值
                $scope.apiKey = apiKey;
    });

    value()

    如果服务的$get方法返回的是一个常量,通过value()函数方便地注册服务。

    value()方法返回以name参数的值为名称的注册后的服务实例。

    angular.module('myApp').value('apiKey','123123123');

    何时使用value()constant()
    value()方法和constant()方法之间最主要的区别是, 常量可以注入到配置函数中,而值不行。

    可以通过value()来注册服务对象或函数,用constant()来配置数据。

    angular.module('myApp', [])
           .constant('apiKey', '123123123')
           .config(function(apiKey) {
            // 在这里apiKey将被赋值为123123123
            // 就像上面设置的那样
           })
           .value('FBid','231231231')
           .config(function(FBid) {
            // 这将抛出一个错误,未知的provider: FBid
            // 因为在config函数内部无法访问这个值
           });

    decorator()

    $provide服务提供了在服务实例创建时对其进行拦截的功能,可以对服务进行扩展,或者用另外的内容完全代替它。

    装饰器是非常强大的,它不仅可以应用在我们自己的服务上,也可以对AngularJS的核心服务进行拦截、中断甚至替换功能的操作。事实上 AngularJS中很多功能的测试就是借助$provide.decorator()建立的。


    例如,我们想给之前定义的githubService服务加入日志功能,可以借助decorator()函数方便地实现这个功能,而不需要对原始的服务进行修改

    decorator()  接受两个参数。  name(字符串) 将要拦截的服务名称。       decoratorFn(函数)  在服务实例化时调用该函数,这个函数由injector.invoke调用,可以将服务注入这个函数中
    $delegate是可以进行装饰的最原始的服务,为了装饰其他服务,需要将其注入进装饰器。

    一个例子:

    var githubDecorator = function($delegate,$log) {
                var events = function(path) {
                    var startedAt = new Date();
                    var events = $delegate.events(path);
                    // 事件是一个promise 
                    events.finally(function() {
                        $log.info("Fetching events" +" took " +(new Date() - startedAt) + "ms");});
                    return events;
                };
                return {
                        events: events
                };
    };
    
    angular.module('myApp')
           .config(function($provide) {
                $provide.decorator('githubService',githubDecorator);
           });

    XHR和服务器通信

    使用$http

    使用内置的$http服务直接同外部进行通信,$http服务简单的封装了原生的XMLHttpRequest对象。

    $http只能接受一个参数,参数是一个对象,包含了用来生成HTTP请求的配置内容。    返回一个promise对象,具有success和error两个方法。

    $http({method: 'GET',url: '/api/users.json'})
        .success(function(data,status,headers,config) {
            // 当相应准备就绪时调用
        }).error(function(data,status,headers,config) {
            // 当响应以错误状态返回时调用
        });

    $http方法返回一个promise对象,可以在响应返回时用then方法来处理回调。如果使用then方法,会得到一个特殊的参数,它代表了相应对象的成功或失败信息,还可以接受两个可选的函数作为参数。或者可以使用success和error回调代替

    var promise = $http({method: 'GET',url: '/api/users.json'});
    promise.then(function(resp){
                    // resp是一个响应对象
                }, function(resp) {
                    // 带有错误信息的resp
            });
    // 或者使用success/error方法
    promise.success(function(data, status, headers, config){
            // 处理成功的响应
    });
    // 错误处理
    promise.error(function(data, status, headers, config){
    // 处理非成功的响应
    });
    //如果响应状态码在200和299之间,会认为响应是成功的, success回调会被调用,否则error回调会被调用。

    then()方法与其他两种方法的主要区别是,它会接收到完整的响应对象,而success()和error()则会对响应对象进行析构。

    //一个小例子:
    var app = angular.module("app",[]);
    app.controller("appcontrol",["$scope","$http",function($scope,$http){
            $http({
                method:"GET",
                url:"name.json"
            }).success(function(data,status,headers,config){
                console.log("OK");
                $scope.books = data;
                console.log($scope.books);
            }).error(function(data,status,headers,config){
                console.log("error");
            });
        }])

    <div ng-controller="appcontrol">
      <ul ng-repeat="book in books">
        <li>{{book.name}}=={{book.price}}</li>
      </ul>
    </div>

    快捷方法:

    // 快捷的GET请求
    $http.get('/api/users.json');  可以接受两个参数。   url(字符串)    config(可选,对象)  这是一个可选的设置对象。 

    4. jsonp()
    这是用来发送JSONP请求的快捷方式。  参数 同上

    $http.jsonp("/api/users.json?callback=JSON_CALLBACK");

    5. post()
    这是用来发送POST请求的快捷方式。

    post()函数可以接受三个参数。
     url(字符串) 代表请求的目的地。    data(对象或字符串) 这个对象包含请求的数据。   config(可选,对象)  这是一个可选的设置对象

    设置对象

    当我们将$http当作函数来调用时,需要传入一个设置对象,用来说明如何构造XHR对象。

    $http({
        method: 'GET',
        url: '/api/users.json',
        params: {
            'username': 'auser'
        }
    });

    设置对象可以包含以下键。

    1. method(字符串)    ‘GET’、‘DELETE’、‘HEAD’、 ‘JSONP’、 ‘POST’、 ‘PUT’。

    2. url(字符串)

    3.params(字符串map或对象)这个键的值是一个字符串map或对象,会被转换成查询字符串追加在URL后面。如果值不是字符串,会被JSON序列化。

    // 参数会转化为?name=ari的形式

    $http({  params: {'name': 'ari'}  })

    4. data(字符串或对象)
    这个对象中包含了将会被当作消息体发送给服务器的数据。通常在发送POST请求时使用。

    var blob = new Blob(['Hello World'], {type: 'text/plain'});
    $http({
        method: 'POST',
        url: '/',
        data: blob
    });

    响应对象


    AngularJS传递给then()方法的响应对象包含四个属性。

     data(字符串或对象)
    这个数据代表转换过后的响应体(如果定义了转换的话)。
     status(数值型)
    响应的HTTP状态码。
     headers(函数)
    这个函数是头信息的getter函数,可以接受一个参数,用来获取对应名字的值。例如,用如
    下代码获取X-Auth-ID的值:
    $http({
      method: 'GET',
      url: '/api/users.json'
      }).then (resp) {
      // 读取X-Auth-ID
      resp.headers('X-Auth-ID');
      });
     config(对象)
    这个对象是用来生成原始请求的完整设置对象。
     statusText(字符串)
    这个字符串是响应的HTTP状态文本。

    缓存 HTTP 请求

    默认情况下, $http服务不会对请求进行本地缓存。在发送单独的请求时,我们可以通过向$http请求传入一个布尔值或者一个缓存实例来启用缓存。

    $http.get('/api/users.json',{ cache: true })
    .success(function(data) {})
    .error(function(data) {});

    第一次发送请求时, $http服务会向/api/users.json发送一个GET请求。第二次发送同一个GET请求时, $http服务会从缓存中取回请求的结果,而不会真的发送一个HTTP GET请求。

    如果想要对AngularJS使用的缓存进行更多的自定义控制,可以向请求传入一个自定义的缓存实例代替true。

    每次发送请求时都传入一个自定义缓存是很麻烦的事情(即使是在服务中)。可以通过应用的.config()函数给所有$http请求设置一个默认的缓存:

    angular.module('myApp', [])
           .config(function($httpProvider, $cacheFactory) {
                $httpProvider.defaults.cache = $cacheFactory('lru', {
                        capacity: 20
                    });
            });
    //$cacheFactory('lru', {capacity: 20});  就是自定义的缓存实例

    拦截器

    果我们想要为请求添加全局功能,例如身份验证、错误处理等,在请求发送给服务器之前或者从服务器返回时对其进行拦截,是比较好的实现手段。
    一共有四种拦截器,两种成功拦截器,两种失败拦截器。

    拦截器的核心是服务工厂,通过向$httpProvider.interceptors数组中添加服务工厂,在$httpProvider中进行注册。

     request    $http设置对象来对请求拦截器进行调用。    返回一个更新过的设置对象,或者一个可以返回新的设置对象的promise。

     response      $http设置对象来对响应拦截器进行调用      返回一个更新过的响应,或者一个可以返回新响应的promise。

     requestError

     responseError

    调用模块的.factory()方法来创建拦截器,可以在服务中添加一种或多种拦截器:

    angular.module('myApp', [])
           .factory('myInterceptor', function($q) {
                var interceptor = {
                    'request': function(config) {
                        // 成功的请求方法
                        return config; // 或者 $q.when(config);
                    },
                    'response': function(response) {
                        // 响应成功
                        return response; // 或者 $q.when(config);
                    },
                    'requestError': function(rejection) {
                        // 请求发生了错误,如果能从错误中恢复,可以返回一个新的请求或promise
                        return response; // 或新的promise
                        // 或者,可以通过返回一个rejection来阻止下一步
                        // return $q.reject(rejection);
                    },
                    'responseError': function(rejection) {
                        // 请求发生了错误,如果能从错误中恢复,可以返回一个新的响应或promise
                        return rejection; // 或新的promise
                        // 或者,可以通过返回一个rejection来阻止下一步
                        // return $q.reject(rejection);
                    }
                };
                return interceptor;
            });
    
    //我们需要使用$httpProvider在.config()函数中注册拦截器:
    angular.module('myApp', [])
           .config(function($httpProvider) {
                $httpProvider.interceptors.push('myInterceptor');
            });

    设置$httpProvider

    使用.config()可以向所有请求中添加特定的HTTP头

    默认的请求头保存在$httpProvider.defaults.headers.common对象中。默认的头如下所示:
    Accept: application/json, text/plain, */*
    通过.config()函数可以对这些头进行修改或扩充,如下所示:

    angular.module('myApp', [])
           .config(function($httpProvider) {
                 $httpProvider.defaults.headers
                    .common['X-Requested-By'] = 'MyAngularApp';
            });

     

    Restangular是一个专门用来从外部读取数据的AngularJS服务。

    尽管$http和$resource是AngularJS的内置服务,但这两个服务在某些方面的功能是有限的。 Restangular通过完全不同的途径实现了XHR通信,并提供了良好的使用体验

    1. promise
    Restangular支持promise模式的异步调用,使用起来更符合AngularJS的习惯。可以像使用原
    始的$http方法一样对响应进行链式操作。
    2. promise展开
    也可以像使用$resource服务一样使用Restangular,通过很简单的方式同时操作promise和对象。
    3. 清晰明了
    Restangular库几乎没有复杂或神奇的东西,无需通过猜测或研究文档就可以知道它是如何工
    作的。
    4. 全HTTP方法支持
    Restangular支持所有的HTTP方法。
    5. 忘记URL
    $resource要求明确的指定想要拉取数据的URL, Restangular并不需要事先知道URL或提前
    指定它们(除基础URL外)。
    6. 资源嵌套
    Restangular可以直接处理嵌套的资源,无需创建新的Restangular实例。
    7. 一个实例
    同$resource不同,使用过程中仅需要创建一个Restangular资源对象的实例。

    Restangular依赖Lo-Dash或Underscore,因此为了确保Restangular可以正常运行,需要引入这两个库中的一个。

    <script type="text/javascript" src="/js/vendor/lodash/dist/lodash.min.js"></script>
    <script type="test/javascript" src="js/vendor/restangular.min.js"></script>

    同其他的AngularJS库一样,我们需要将restangular资源当作依赖加载进应用模块对象。
        angular.module('myApp', ['restangular']);
    完成后,就可以将Restangular服务注入到AngularJS对象中:
    angular.module('myApp', [])
        .factory('UserService', ['Restangular', function(Restangular) {
          // 现在我们已经在UserService中访问了Restangular
    }])

    Restangular有两种方式创建拉取数据的对象。可以为拉取数据的对象设置基础路由:

    var User = Restangular.all('users');  会让所有的HTTP请求将/users路径作为根路径来拉取数据。

        调用上述对象的getList()方法会从/users拉取数据:  var allUsers = User.getList(); // GET /users

    也可通过单个对象来发送嵌套的请求,用唯一的ID来代替路由发送请求:  var oneUser = Restangular.one('users', 'abc123');

    调用oneUser上的get()时向/users/abc123发送请求。
    oneUser.get().then(function(user) {

        // GET /users/abc123/inboxes
        user.getList('inboxes');
    });

    Restangular非常聪明,知道如何根据在Restangular源对象上调用的方法来构造URL。但设置拉取数据的URL是很方便的,特别是当后端不支持纯粹的RESTful API时。

    var messages = Restangular.all('messages');
    通过这个对象,可以使用getList()来获取所有信息。 getList()方法返回了一个集合,其中包含了可以用来操作特定集合的方法

    Restangular返回的是增强过的promise对象,因此除了可以调用then方法,还可以调用一些特殊的方法,比如$object。 $object会立即返回一个空数组(或对象),在服务器返回信息后,数组会被用新的数据填充。

    // POST到/messages
    var newMessage = {
        body: 'Hello world'
    };
    
    var messages = Restangular.all('messages');
    
    // 然后在promise中调用
    messages.post(newMessage).then(function(newMsg){
            // 首先将消息设置成空数组
            // 然后一旦getList是完整的就填充它
            $scope.messages = messages.getList().$object;
        }, 
        function(errorReason)
            // 出现了一个错误
        });

    使用remove()方法发送一个DELETE HTTP请求,调用集合中一个对象(或元素)的remove()方法来发送删除请求。

    var message = messages.get(123);
    message.remove(); // 发送DELETE HTTP请求

    更新和储存对象 由HTTP PUT方法完成。 Restangular 通过put()方法来支持这个功能。通过put()方法来支持这个功能。
    更新一个对象,首先查询这个对象,然后在实例中设置新的属性值,再调用对象的put()方法将更新保存到后端。  在更新对象时使用Restangular.copy()是一个比较好的实践。

    嵌套资源是指包含在其他组件内部的组件。

    //一个 作者所写的所有的书籍, 作家: abc123
    var author = Restangular.one('authors', 'abc123');
    // 构建一个GET到/authors/abc123/books的请求
    var books = author.getList('books');

    也可以在服务器返回的对象上调用:

    Restangular.one('authors', 'abc123').then(function(author) {
        $scope.author = author;
    });
    
    // 构建一个GET到/authors/abc123/authors的请求
    // 使用$scope.author,它是从服务器返回的真实对象
    $scope.author.getList('books');

    Restangular支持所有的HTTP方法。它支持GET、 PUT、 POST、 DELETE、 HEAD、 TRACE、OPTIONS和PATCH。

    author.get();         // GET/authors/abc123
    author.getList('books');    // GET/authors/abc123/books
    author.put();         // PUT/authors/abc123
    author.post();         // POST/authors/abc123
    author.remove();     // DELETE/authors/abc123
    author.head();         // HEAD/authors/abc123
    author.trace();     // TRACE/authors/abc123
    author.options();     // OPTIONS/authors/abc123
    author.patch();     // PATCH/author/abc123

    下面是一些例子。

        • GET /zoos:                        列出所有动物园
        • POST /zoos:                    新建一个动物园
        • GET /zoos/ID:                 获取某个指定动物园的信息
        • PUT /zoos/ID:                  更新某个指定动物园的信息(提供该动物园的全部信息)
        • PATCH /zoos/ID:              更新某个指定动物园的信息(提供该动物园的部分信息)
        • DELETE /zoos/ID:            删除某个动物园
        • GET /zoos/ID/animals:    列出某个指定动物园的所有动物
        • DELETE /zoos/ID/animals/ID:    删除某个指定动物园的指定动物

    设置 Restangular

    Restangular具有高度的可定制性,可以根据应用的需要进行相应的设置。每个属性都有默认值,所以我们也无需在不必要的情况下对其进行设置。

    将RestangularProvider注入到config()函数中,或者将Restangular注入到一个run()函数中,
    如果设置Restangular时需要用到其他服务,那么就在run()方法中设置,否则就在config()中进行设置。

    通过setBaseUrl()方法给所有后端 API 请求设置 baseUrl

    angular.module('myApp', ['restangular'])
           .config(function(RestangularProvider) {
                RestangularProvider.setBaseUrl('/api/v1');
    });

     

    2. 添加元素转换

    使用elementTransformers可以在Restangular对象被加载后为其添加自定义方法。

    例如,如果我们只想更新authors资源,可以用如下方法:

    angular.module('myApp', ['restangular'])
           .config(function(RestangularProvider) {
                    // 3个参数:
                                // route
                RestangularProvider.extendModel('authors', function(element) {
                        element.getFullName = function() {
                            return element.name + ' ' + element.lastName;
                        };
                    return element;
                });
            });

    设置responseInterceptors

    Restangular可以设置响应拦截器。responseInterceptors在需要对服务器返回的响应进行转换时非常有用。

    responseInterceptors在每个响应从服务器返回时被调用。调用时会传入以下参数。
     data:从服务器取回的数据。
     operation:使用的HTTP方法。
     what:所请求的数据模型。
     url:请求的相对URL。
     response:完整的服务器响应,包括响应头。
     deferred:请求的promise对象。

    会使getList()返回一个带有元信息的数组,,向/customers发送GET请求会返回一个像{customers: []}这样的数组。

    angular.module('myApp', ['restangular'])
           .config(function(RestangularProvider) {
                RestangularProvider.setResponseInterceptor(function(data, operation, what) {
                    if (operation == 'getList') {
                        var list = data[what];
                        list.metadata = data.metadata;
                        return list;
                    }
                    return data;
                });
            });

    使用requestInterceptors

    在将数据实际发送给服务器之前对其进行操作。

    自定义Restangular服务

    将Restangular服务注入到工厂函数中,就可以方便地对Restangular进行封装。在工厂函数内部,使用withConfig()函数来创建自定义设置

    angular.module('myApp', ['restangular'])
           .factory('MessageService', ['Restangular', function(Restangular) {
                var restAngular = Restangular.withConfig(function(Configurer) {
                    Configurer.setBaseUrl('/api/v2/messages');
                });
                var _messageService = restAngular.all('messages');
                return {
                    getMessages: function() {
                        return _messageService.getList();
                    }
                };
            }]);

    XHR实践

    跨域和同源策略:同源策略允许页面从同一个站点加载和执行特定的脚本。站外其他来源的脚本同页面的交互则被严格限制。

    跨域资源共享(Cross Origin Resource Sharing, CORS)是一个解决跨域问题的好方法,从而可以使用XHR从不同的源加载数据和资源。

    JSONP是一种可以绕过浏览器的安全限制,从不同的域请求数据的方法。使用JSONP需要服务器端提供必要的支持。

    JSONP的原理是通过<script>标签发起一个GET请求来取代XHR请求。 JSONP生成一个<script>标签并插到DOM中,然后浏览器会接管并向src属性所指向的地址发送请求。

    当服务器返回请求时,响应结果会被包装成一个JavaScript函数,并由该请求所对应的回调函数调用。

    $http服务中提供了一个JSONP辅助函数。

    $http
    .jsonp("https://api.github.com?callback=JSON_CALLBACK") .success(function(data) {
    // 数据
    });

    当请求被发送时, AngularJS会在DOM中生成一个如下所示的<script>标签:

    <script src="https://api.github.com?callback=angular.callbacks._0"  type="text/javascript"></script> 
    //JSON_CALLBACK被替换成了一个特地为此请求生成的自定义函数。

    当支持 JSOPN的服务器返回数据时,数据会被包装在由 AngularJS生成的具名函数angular.callbacks._0中。

    使用JSONP需要意识到潜在的安全风险。首先,服务器会完全开放,允许后端服务调用应用中的任何JavaScript。

    不受我们控制的外部站点(或者蓄意攻击者)可以随时更改脚本,使我们的整个站点变得脆弱。服务器或中间人有可能会将额外的JavaScript逻辑返回给页面,从而将用户的隐私数据暴露出来。

    请求是由<script>标签发送的,所以只能通过JSONP发送GET请求。并且脚本的异常也很难处理使用JSONP一定要谨慎,同时只跟信任并可以控制的服务器进行通信

    使用 CORS

    CORS规范简单地扩展了标准的XHR对象,以允许JavaScript发送跨域的XHR请求。它会通过预检查(preflight)来确认是否有权限向目标服务器发送请求。

    告诉AngularJS我们正在使用CORS。使用config()方法在应用模块上设置两个参数以达到此目的,

    首先,告诉AngularJS使用XDomain,并从所有的请求中把X-Request-With头移除掉。X-Request-With头默认就是移除掉的,但是再次确认没有坏处。

    angular.module('myApp', [])
           .config(function($httpProvider) {
                $httpProvider.defaults.useXDomain = true;
                delete $httpProvider.defaults.headers
                .common['X-Requested-With'];
            });

    服务器端CORS支持,确保服务器支持CORS是很重要的

    CORS请求分为简单和非简单两种类型。

    CORS并不是一个安全机制,只是现代浏览器实现的一个标准。在应用中设置安全策略依然是我们的责任。

    使用 XML

     假如服务器返回的是XML而非JSON格式的数据,需要将其转换成JavaScript对象。

     以X2JS库为例,这是一个非常好用的开源库,将XML格式转换成JavaScript对象
    首先引入:

    <script type="text/javascript" src="https://x2js.googlecode.com/hg/xml2json.js"></script>

     创建一个工厂服务,功能就是在DOM中解析XML

    angular.factory('xmlParser', function() {
                var x2js = new X2JS();
                return {
                    xml2json: x2js.xml2json,
                    json2xml: x2js.json2xml_str
                };
            });
    //借助这个轻量的解析服务,可以将$http请求返回的XML解析成JSON格式,如下所示:
    angular.factory('Data', [$http, 'xmlParser', function($http, xmlParser) {
                $http.get('/api/msgs.xml', {
                    transformResponse: function(data) {
                        return xmlParser.xml2json(data);
                    }
                });
            });

    使用 AngularJS 进行身份验证

    纯客户端身份验证

    通过令牌授权来实现客户端身份验证,服务器需要做的是给客户端应用提供授权令牌。
    令牌本身是一个由服务器端生成的随机字符串,由数字和字母组成,它与特定的用户会话相关联。  uuid库是用来生成令牌的好选择。

    当用户登录到我们的站点后,服务器会生成一个随机的令牌,并将用户会话同令牌之间建立关联,用户无需将ID或其他身份验证信息发送给服务器

    客户端发送的每个请求都应该包含此令牌,这样服务器才能根据令牌来对请求的发送者进行身份验证。
    服务器端则无论请求是否合法,都会将对应事件的状态码返回给客户端,这样客户端才能做出响应。

    下面的表格中是一些常用的状态码:
    状 态 码 含 义

    200  一切正常
    401 未授权的请求
    403 禁止的请求
    404 页面找不到
    500 服务器错误


    当客户端收到这些状态码时会做出相应的响应。

    数据流程如下:
    (1) 一个未经过身份验证的用户浏览了我们的站点;
    (2) 用户试图访问一个受保护的资源,被重定向到登录页面,或者用户手动访问了登录页面;
    (3) 用户输入了他的登录ID(用户名或电子邮箱)以及密码,接着AngularJS应用通过POST请求将用户的信息发送给服务端;
    (4) 服务端对ID和密码进行校验,检查它们是否匹配;
    (5) 如果ID和密码匹配,服务端生成一个唯一的令牌,并将其同一个状态码为200的响应一起返回。如果ID和密码不匹配,服务器返回一个状态码为401的响应

    有下面几种方法可以将路由定义为公共或非公共。

    1. 保护API访问的资源

    对一个会发送受保护的API请求的路由进行保护,但又希望可以正常加载页面,可以简单地通过$http拦截器来实现。

    创建一个$http拦截器并能够处理未通过身份验证的API请求,首先要创建一个拦截器

    我们在应用的.config()代码块内设置$http响应拦截器,并将$httpProvider注入其中。

    angular.module("app",[])
           .config(function($httpProvider){
                   //这里构造拦截器,会处理所有请求的响应 以及 响应错误。
                   var interceptor = function($q,$rootScope,Auth){
                       return {
                           "response":function(resp){
                               if(resp.config.url=="/api/login")
                               {
                                   //假设后台返回的数据格式 如下:{token:"AUTH_TOKEN"}
                                   Auth.setToken(resp.data.token);
                               }
                               return resp;
                           },
                           "responseError":function(rejection){
                               //错误处理,
                               switch(rejection.status){
                                   case 401:     if(rejection.config.url !="api/login"){
                                                   //如果当前不是登陆页面
                                                   $rootScope.$broadcast('auth:loginRequired');
                                               }
                                               break;
                                   case 403:     $rootScope.$broadcast('auth:forbidden');
                                            break;
    
                                   case 404:     $rootScope.$broadcast('page:notFound');
                                            break;
    
                                   case 500:     $rootScope.$broadcast('server:error');
                                            break;
                               }
    
                               return $q.reject(rejection);
                           }
                       }
                   }
             // 将拦截器和$http的request/response链整合在一起
                   $httpProvider.interceptors.push(interceptor);
           });

    如果我们希望始终对某些路径进行保护,或者请求的API不会对路由进行保护,那就需要监视路由的变化,以确保访问受保护路由的用户是处于登录状态的。

    为了监视路由变化,需要为$routeChangeStart事件设置一个事件监听器。这个事件会在路由属性开始resolve时触发,

    通过监听器对事件进行监听,并检查路由,看它是否定义为可被当前用户访问。

    先要定义应用的访问规则。可以通过在应用中设置常量,然后在每个路由中通过对比这些常量来判断用户是否具有访问权限。

    angular.module('myApp', ['ngRoute'])
         .constant('ACCESS_LEVELS', {
          pub: 1,
          user: 2
       });

    把ACCESS_LEVELS设置为常量,可以将它注入到.confgi()和.run()代码块中,并在整个应用范围内使用

    //使用这些常量来为每个路由都定义访问级别:
    angular.module('myApp', ['ngRoute'])
           .config(function($routeProvider, ACCESS_LEVELS) {
                    $routeProvider
                        .when('/', {
                            controller: 'MainController',
                            templateUrl: 'views/main.html',
                            access_level: ACCESS_LEVELS.pub
                        })
                        .when('/account', {
                            controller: 'AccountController',
                            templateUrl: 'views/account.html',
                            access_level: ACCESS_LEVELS.user
                        })
                        .otherwise({
                            redirectTo: '/'
                        });
            });

    为了验证用户的身份,创建一个服务来对已经存在的用户进行监视。同时让服务能够访问浏览器的cookie,这样当用户重新登录时,只要会话有效就无需再次进行身份验证。

    angular.module('myApp.services', [])
          .factory('Auth', function($cookieStore,ACCESS_LEVELS) {
                var _user = $cookieStore.get('user');
                var setUser = function(user) {
                    if (!user.role || user.role < 0) {
                        user.role = ACCESS_LEVELS.pub;
                    }
                    _user = user;
                    $cookieStore.put('user', _user);
                };
                return {
                    isAuthorized: function(lvl) {
                        return _user.role >= lvl;
                    },
                    setUser: setUser,
                    isLoggedIn: function() {
                        return _user ? true : false;
                    },
                    getUser: function() {
                        return _user;
                    },
                    getId: function() {
                        return _user ? _user._id : null;
                    },
                    getToken: function() {
                        return _user ? _user.token : '';
                    },
                    logout: function() {
                        $cookieStore.remove('user');
                        _user = null; }
                }
            };
        });

    当用户已经通过身份验证并登录后,可以在$routeChangeStart事件中对其有效性进行检查。

    angular.module('myApp', [])
           .run(function($rootScope, $location, Auth) {
                // 给$routeChangeStart设置监听
                $rootScope.$on('$routeChangeStart', function(evt, next, curr) {
                    if (!Auth.isAuthorized(next.$$route.access_level)) {
                        if (Auth.isLoggedIn()) {
                            // 用户登录了,但没有访问当前视图的权限
                            $location.path('/');
                        } else {
                            $location.path('/login');
                        }
                    }
                });
            });

    如果提供的令牌是合法的,且与一个合法用户是关联的状态,那服务器就会认为用户的身份是合法且安全的。

  • 相关阅读:
    当期所得税费用总额
    所得税净利润算法
    [AGC028B]Removing Blocks 概率与期望
    bzoj 4319: cerc2008 Suffix reconstruction 贪心
    bzoj 2430: [Poi2003]Chocolate 贪心
    BZOJ 2839: 集合计数 广义容斥
    luogu 5505 [JSOI2011]分特产 广义容斥
    CF504E Misha and LCP on Tree 后缀自动机+树链剖分+倍增
    CF798D Mike and distribution 贪心
    CF707D Persistent Bookcase 可持久化线段树
  • 原文地址:https://www.cnblogs.com/a-lonely-wolf/p/5743103.html
Copyright © 2020-2023  润新知