Angular 1 学习笔记一


一条最重要的最佳实践是每个类型的组件只有唯一的特定责任。
Controllers的职责:
负责管理用户和应用程序的交互和模型model。
它提供事件处理器来负责处理整个应用程序运行过程中的用户交互。
Controller还负责程序流程,它通过调用服务的方法来更新数据并将浏览导向下一个处理环节。

它不处理DOM,跟外部服务的交互,存储数据,也不进行复杂的运行时间超长的业务运算。
它应该很瘦,通常限制controller为只包含跟model和用户交互相关的方法。

我们通常会发现在controller中经常重复一些同一时间的处理代码,可能会试着想把它们移到一个服务中。
但大多时候,最好把它们放到base controller中。
我们可以通过使用$controller 服务来添加一个base controller的属性和函数在controller的scope内。

var app = angular.module('angularjs-starter',[]);

app.controller('ParentCtrl',function($scope){
    //被其它子controller继承的方法定义在这里
});

app.controller('ChildCtrl',function($scope,$controller){
    //这里调用$controller来将ParentCtrl的方法和属性添加到ChildCtrl的scope对象
    $controller('ParentCtrl',{$scope:$scope});
    
    //这里定义ChildCtrl自己的方法和属性
});

该模式还可以在代码重构时处理一些更加棘手的问题。
如果你有一些多余的事件处理器需要在视图销毁时注销,你可以确保注销事件处理器的代码在controller
销毁时被执行,我们可以通过经多余代码到baseController并在$destroy信息被广播时使用$on方法执行它。

然而,如果我们通过潜入一个ng-controller指令到另一个ng-controller指令中的功能继承模式,注销事件处理器的代码
在父ng-controller中,该代码不会执行直到父 controller被销毁时才执行,这样就达不到效果了。


Directives的职责:
操作DOM并提供一个扩展HTML语法的新功能。
如果你发现需要在Controller或者Service中使用$('element'),最好的实践是将这部分代码移到一个directive里。
你也应该将重复出现的HTML代码移到一个directive里来简化HTML代码。
<div class="row-fluid" ng-repeat="adjunct in adjuncts">
    <div class="span1">{{adjunct.Name}}</div>
    <div class="span1">{{adjunct.Type}}</div>
    <div class="span1">{{adjunct.Use}}</div>
    <div class="span1">{{adjunct.Time}}</div>
    <div class="span1">{{adjunct.Amount}}</div>
    <div class="span1">{{adjunct.UseFor}}</div>
    <div class="span1">{{adjunct.Notes}}</div>
    <div><a href="#/editadjunct/{{adjunct._id.$oid}}">
    <i class="icon-pencil"></i></a></div>
</div>

<div class="row-fluid" ng-repeat="adjunct in adjuncts">
    <display-adjunct item="adjunct"></display-adjunct>
</div>

Directive还表单输入校验工作。
Validation 指令帮助你将校验代码从controller里移植出来到一个可以重用的组件里。
使用指令校验表单数据是AngularJS的最佳实践做法之一,它能够减少我们controller组件的复杂程度。


Service 的职责
它负责提供可重用代码库给其它组件,可以提供跨切面功能,比如日志,身份验证,消息等。
可以包含请求和存储数据到外部服务的代码,还包含操作,排序,过滤甚至转换数据的功能。
还可以用于将应用程序使用的外部服务集成到应用程序中来。

在整个AngularJS生命周期中,只会创建一个的服务实例,所以我们还可以将Service用作各组件之间交互的最好媒介。
一个Controller可以将数据保存到一个Service中,导向另外一个新Controller,新Controller可以从服务中获取这些数据。

服务还可用于为我们的应用程序提供业务逻辑实现。
我们可以创建一个规则引擎来处理复杂形势的校验或者创建一个状态机来处理需要长时间运行业务处理的用户工作流
还可以创建一个复杂的计算引擎来处理计算税率和运费。

设计服务:Chapter 2
在开始写服务时首先定义服务接口。
方法名使用名词组合,不使用缩写和简写
保持服务责任单一,便于测试,便于封装,重用。
保持命名规则一致
保持顶级使用情景,不要包含一些用不到的功能。

每个方法只做一件事,避免使用SWITCH
善于使用像underscore或者lodash之类的类库避免重复代码。


Law of Demeter 迪米特法则
依赖注入可以使我们编写服务和其依赖的松耦合代码。
迪米特法则说:通常的形式下,每个代码单元应只有关于其它代码单元有限的知识。
换句话说,不要再你的方法中使用链式对象引用,而是直接将要使用的对象传入。
$scope.getBrewerName = function(authenticate){
    if(authenticate && authenticate.currentBrewer){
        return authenticate.currentBrewer.firstName + ' ' + authenticate.currentBrewer.lastName;
    }
    return '';
},
我们注意到该方法使用了authenticate对象,实际上我们真正要使用的是其属性对象currentBrewer
如果我们传入Authenticate对象,则首先要判断该对象的存在与否,以及currentBrewer对象是否合法,
这些都增加了该方法的复杂程度,给测试增加的困难。
所以我们可以直接传入Brewer对象,来简化方法逻辑,便于测试:
$scope.getBrewerName = function(brewer){
    if(brewer){
        return brewer.firstName + ' ' + brewer.lastName;
    }
    return '';
},

通过遵守迪米特法则,我们编写依赖于接口而不是具体对象代码。


传入所需的依赖:可测试性的另外一个原则就是使用依赖注入,将所有依赖通过构造函数传入。
而不使用$injector服务获取服务,除非没有别的方法的情况下。
angular.module('brew-everywhere').factory('authenticate',
    function ($injector) {
        var authenticate = {
            rootScope: $injector.get('$rootScope'),
            window: $injector.get('$window'),
            shaService: $injector.get('sha1'),
            userResource: $injector.get('userResource'),
            currentBrewer: null,
            // methods
            login: function (username, password) {
                authenticate.userResource.query({UserName: username})
                    .then(function (brewers) {
                        if (brewers.length === 0) {
                            authenticate.window.alert("Invalid user name");
                            return;
                        }
                        var brewer = brewers[0];
                        var passwordHash = authenticate.shaService.hash(password +
                            Date.parse(brewer.DateJoined).valueOf().toString());
                        if (passwordHash !== brewer.Password) {
                            authenticate.window.alert("Invalid password.")
                            return;
                        }
                        authenticate.currentBrewer = brewer;
                        authenticate.rootScope.$broadcast('USER_UPDATED');
                    });
            },
            isBrewerLoggedIn: function () {
                if ((authenticate.currentBrewer !== null) &&
                    (authenticate.currentBrewer !== undefined)) {
                    return true;
                }
                return false;
            },
            logout: function () {
                authenticate.currentBrewer = null;
                authenticate.rootScope.$broadcast('USER_UPDATED');
            }
        };
        return authenticate;
});

直接将依赖项作为参数传入:

angular.module('brew-everywhere').factory('authenticate',
    function ($rootScope, $window, sha, userResource) {
        var authenticate = {
            // data members
            currentBrewer: null,
            // methods
            login: function (username, password) {
                userResource.query({UserName: username})
                    .then(function (brewers) {
                    if (brewers.length === 0) {
                        $window.alert("Invalid user name");
                        return;
                    }
                    var brewer = brewers[0];
                    var passwordHash = sha.hash(password +
                        Date.parse(brewer.DateJoined).valueOf().toString());
                    if (passwordHash !== brewer.Password) {
                        $window.alert("Invalid password.")
                        return;
                    }
                    authenticate.currentBrewer = brewer;
                    $rootScope.$broadcast('USER_UPDATED');
                });
            },
            isBrewerLoggedIn: function () {
                if ((authenticate.currentBrewer !== null)
                    && (authenticate.currentBrewer !== undefined)) {
                    return true;
                }
                return false;
            },
            logout: function () {
                authenticate.currentBrewer = null;
                $rootScope.$broadcast('USER_UPDATED');
            }
        };
        
        return authenticate;
});

只有在我们无法传入依赖时才会考虑使用$injector服务,比如我们需要基于环境或者配置约束动态的创建服务实例时。
我们不能保证$injector服务返回的就是合法的服务对象,所以需要添加对其返回值的校验。
但是,如果我们将依赖对象作为参数传入时,我们就首先保证了依赖对象的合法性。从而减少了检测逻辑代码。

当我们需要在某个模块的config方法中访问某个服务时,我们必须使用$injector服务来获取需要的服务实例。

限制构造函数赋值:
当我们在服务构造函数中包含外部服务调用时,这会造成难于测试和调试。
如果在我们服务初始化时调用外部服务出现异常,造成我们的服务无法被正常创建,这样会造成整个应用程序无法启动。
所以,一般我们会为我们需要从外部服务获取数据的服务创建一个init方法,在模块的run方法中调用,初始化服务。
因为run方法是在应用程序启动后才运行,所以就避免了问题出现。


Sparingly 使用promises:
我们推荐在跟长时间运行的请求和计算交互时避免让服务方法返回promise而是选择一个事件或者发布/订阅机制 。

Promise只擅长于单一调用,我们不能重置它然后第二次解析它。
如果我们有一个复杂的应用有很多用户需要知道一个服务数据的更新事件,此时Promise就无法满足了。


Service,factory 和 provider
一个Service是一个我们程序中可以跨多个组件使用的一个对象,一个函数或者一组数值。
当我们将一个服务注入到一个应用程序时,$injector服务首先检查该服务的实例是否已经存在,如果不存在它会创建一个新的并返回,如果存在将直接返回旧的。

我们有五种不同的方法可以为模块定义服务:
前两种适用于静态数值,配置值和模块使用。
后三种适合于基于如何创建和使用它们定义服务。

第一个方法是 constant,用于定义原始值和对象,它们将永远不会改变,以及模块配置需要的。
angular.module('logging').constant('logging_config',{
    traceLevel:{
        _LOG_TRACE_: '_LOG_TRACE_',
        _LOG_DEBUG_: '_LOG_DEBUG_',
        _LOG_INFO_: '_LOG_INFO_',
        _LOG_WARN_: '_LOG_WARN_',
        _LOG_ERROR_: '_LOG_ERROR_',
        _LOG_FATAL_: '_LOG_FATAL_',
    }
});

第二个方法是 value,跟constant类似,也是用于定义原始值和对象,但是这些数值和对象可以被修改。
并且不能用于模块的config方法。
angular.module('logging').value('logging_config', {
    traceLevel: {
        _LOG_TRACE_: '_LOG_TRACE_',
        _LOG_DEBUG_: '_LOG_DEBUG_',
        _LOG_INFO_: '_LOG_INFO_',
        _LOG_WARN_: '_LOG_WARN_',
        _LOG_ERROR_: '_LOG_ERROR_',
        _LOG_FATAL_: '_LOG_FATAL_',
    }
});

在考虑使用constant和value时主要考虑 我们是否需要定义的值可以被使用我们服务的人修改或者重写。
如果允许重写,那么就是用value方法;调用者可以使用AngularJS装饰者重写该值。
如果不允许修改,那就是用constant方法定义,如果你需要在模块的config方法里使用这些值,那么请使用Constant方法定义。

Value方法的另外一个用途是定义model对象,该模式提供了一个很好的方式来保存model定义和对其属性值的操作方法。
可以帮助你将重复的代码独立到一个单独的组件文件里。在controller需要操作model时,保持简洁。

下面我们使用value方法定义 brewer model类并在其prototype上定义两个方法用于获取全名和检查brewer是否有东西在仓库中。

(function(){
    'use strict';
    
    var Brewer = function(){
        var self = this;
        self.userName = '';
        self.firstName = '';
        self.lastName = '';
        self.email = '';
        self.location = '';
        self.bio = '';
        self.website = '';
        self.avatar = '';
        self.photo = '';
        self.dateJoined = '';
        self.inventory = [];
    };
    
    Brewer.prototype = {
        fullName: function(){
            return this.firstName + ' ' + this.lastName;
        },
        hasItemInInventory: function(value){
            var result = false;
            if (this.inventory && this.inventory.length > 0){
                angular.forEach(this.inventory, function(item){
                    if(item.name === value){
                        result = true;
                    }
                });
            }
            return result;
        }
    }
    
    angular.module('brew-everywhere').value('brewer',Brewer);
    
})();

现在,当你逐日这个服务到一个controller或者directive时,$injector服务将提供该model的构造函数,我们来创建model实例。

当我们需要为服务提供配置数据,比如API,服务URL,以及其它参数时,上面的方法都不适合了。
这时我们需要用provider方法,它定义的服务,可以让我们的应用程序在调用它时提供配置数据。

Service方法:该方法提供了一个简洁的注册服务的构造函数的方法。
下面我们定义一个logging服务,它打包log4javascript类库,首先我们定义一个构造方法调用logging
它定义了服务的成员,然后我们通过给logging对象原型添加不同方法来定义服务功能。
最后,我们通过将其构造方法注册给Angular的Service方法来完成定义。

(function(){
    'use strict';
    
    var logging = function(){
        this.log = null;
    };
    
    logging.prototype = {
        init: function(){
            //获取logger对象
            this.log = log4javascript.getLogger("main");
            //设置日志层级
            this.log.setLevel(log4javascript.Level.ALL);
            //创建和添加一个追加器到logger
            var appender = new log4javascript.PopUpAppender();
            this.log.addAppender(appender);
        },
        trace: function (message) {
            this.log.trace(message);
        },
        debug: function (message) {
            this.log.debug(message);
        },
        info: function (message) {
            this.log.info(message);
        },
        warn: function (message) {
            this.log.warn(message);
        },
        error: function (message) {
            this.log.error(message);
        },
        fatal: function (message) {
            this.log.fatal(message);
        }
    };
    
    angular.module('brew-everywhere').service('logging',logging);
    
})();
        
    
factory方法:如果我们不是通过类型/类定义的方法来定义我们的服务并且我们不需要在module的config方法里
配置我们的服务时,我们选择使用 factory方法定义服务。

factory方法包裹一个构造函数和一个默认的提供者$get, 它是在$get方法被调用时返回由构造函数提供的服务实例。

(function(){
    'use strict';
    
    angular.module('brew-everywhere').factory('logging',function(){
        
        var logging = {
            log: null,
            init: function () {
                // get the main logger
                logging.log = log4javascript.getLogger("main");
                // set the logging level
                logging.log.setLevel(log4javascript.Level.ALL);
                // create and add an appender to the logger
                var appender = new log4javascript.PopUpAppender();
                logging.log.addAppender(appender);
            },
            trace: function (message) {
                logging.log.trace(message);
            },
            debug: function (message) {
                logging.log.debug(message);
            },
            info: function (message) {
                logging.log.info(message);
            },
            warn: function (message) {
                logging.log.warn(message);
            },
            error: function (message) {
                logging.log.error(message);
            },
            fatal: function (message) {
                logging.log.fatal(message);
            }
        };
        
        return logging;
        
    });
})();


Provider方法:
该方法注册一个服务提供者,该提供者有一个$get方法负责实例化一个服务对象。
该provider还可以有其它的方法定义,在使用时用provider实例本身调用,而非对象实例。
如果想在服务对象实例化之前对其进行配置,则可以调用这些定义给provider的方法实施。

(function () {
    'use strict';
    angular.module('brew-everywhere').provider('logging', function ()
    {
        var logging = {
            log: null,
            init: function () {
                logging.log = log4javascript.getLogger("main");
            },
            setLogLevel: function (logLevel) {
                logging.log.setLevel(logLevel);
            },
            setAppender: function (appender) {
                logging.log.addAppender(appender);
            },
            
            $get: function () {
                function trace(message) {
                    logging.log.trace(message);
                }
                function debug(message) {
                    logging.log.debug(message);
                }
                function info(message) {
                    logging.log.info(message);
                }
                function warn(message) {
                    logging.log.warn(message);
                }
                function error(message) {
                    logging.log.error(message);
                }
                function fatal(message) {
                    logging.log.fatal(message);
                }
                return {
                    trace: trace,
                    debug: debug,
                    info: info,
                    warn: warn,
                    error: error,
                    fatal: fatal
                };
            }
        };
        
        return logging;
    });
})();

总结一下,如果你需要定义一些在整个应用程序中始终不变的值,使用constant方法。
如果需要定义一些值或者model可以被修改,则使用value方法。
如果想将服务定义成一个类,并需要调用其构造函数,则选择service方法。
如果只需要对象实例而不需要其构造函数时,我们采用factory方法。
如果需要在module的config方法里为服务提供配置,则使用provider方法。


Immediately-Invoked Function Expression (IIFE)

(function () {
    'use strict';
    // module definition goes here
    // service definition goes here
})();

该方法可以应用在任何组件编写上,但是当你准备用编译工具将你的代码连接成一个单独文件并包裹它们到一个闭包里时,
最好不要用这种写法。

使用Christian Heilmann's Revealing Module Pattern,就是只暴露那些被客户使用到的方法和属性。

下面的代码就使用了Revealing Module Pattern定义了, 发布/预订 消息服务:
messaging_service 函数定义了一个闭包,在这里我们定义服务的各种方法和内部变量来保存服务状态,
在函数最后,我们返回一个对象,它只包含我们想展示给用户的方法和属性。

(function(){
    'use strict';
    
    angular.module('brew-everywhere').factory('messaging_service',[messaging_service]);
    
    function messaging_service() {
        //#region Internal Properties
        var cache = {};
        //#endregion
        //#region Internal Methods
        function publish(topic, args) {
            cache[topic] && angular.forEach(cache[topic],function (callback) {
                callback.apply(null, args || []);
            });
        }
        
        function subscribe(topic, callback) {
            if (!cache[topic]) {
                cache[topic] = [];
            }
            cache[topic].push(callback);
            return [topic, callback];
        }
        
        function unsubscribe(handle) {
            var t = handle[0];
            cache[t] && angular.forEach(cache[t], function (idx) {
                if (this == handle[1]) {
                    cache[t].splice(idx, 1);
                }
            });
        }
        //#endregion
        
        // Define the functions and properties to reveal.
        var service = {
            publish: publish,
            subscribe: subscribe,
            unsubscribe: unsubscribe
        };
        return service;
    }
})();

配置服务:
我们将服务定义为Provider时,在$get返回服务对象外还定义了一些属于provider本身的方法,用于操作
服务对象的配置,我们可以在module的config方法里调用provider的这些方法,对我们的服务对象进行配置。

angular.module('MyApp',['logging']).config(function(loggingProvider){
    //初始化logging 服务
    loggingProvider.init();
    //设置日志层级
    loggingProvider.setLogLevel(log4javascript.Level.ALL);
    //创建并添加一个日志追加器
    var appender = new log4javascript.PopupAppender();
    loggingProvider.setAppender(appender);
});

我们还可以通过定义service和factory类型服务,定义配置方法然后再module的run方法中调用这些方法来实现对服务的配置。

使用constant和value定义静态变量和model。

service方法定义服务,实质上就是注册一个服务对象的构造函数。
即service方法的第一个参数为服务名称,第二个参数是一个没有返回值的函数,实质上就是一个对象构造方法。
此种定义方式,我们可以使用服务名作为类名,实例化我们需要的服务对象。
定义一个服务构造函数,然后注册给module


factory方法定义服务,实质上是注册一个服务对象的工厂方法函数,它可以根据条件返回具体的服务对象实例,
它不能用服务名称来直接实例化服务对象实例。
它的第一个参数为服务对象工厂方法名称,第二个参数为制造函数,该函数返回值是具体服务对象。
编写factory方法时,主要定义一个服务对象,返回。
定义一个具体的服务类注册到module

provider方法定义服务,主要是在对象类的外层包裹了一个provider静态类,可以在内部服务对象类被返回前通过
静态方法为该服务类提供一些配置信息,主要是用于在服务对象生成之前,也就是在应用程序启动之前,进行一些配置。
它需要在第二个函数内部实现一个$get方法,定义需要的服务对象让后返回。
provider里其它的静态方法则是应用于服务生成前配置参数的操作。
定义一个可配置的工厂方法注册给module


===============================
跨切面内容处理:messaging, logging,data validation,caching,internationalization,security
消息,日志,数据校验,缓存,国际化,安全

在我们有一个需要长时间运行的方法或者前端异步Ajax调用一个服务时,跟服务的消费者交互变得非常重要。
当我们服务执行运算或者等待Ajax调用返回时,我们不想阻塞应用程序的运行,最好的处理方式就是采用异步处理。

有许多方式可以处理这类方法:
我们可以请消费者提供一个回调函数给我们的服务来让它在完成时执行。
也可以返回一个promise,在服务结束时解析它。
或者使用一个消息设计模式一旦服务方法完成来通知消费者。

回调函数和promise在通知单个消费者是非常适用。
但是当我们的服务方法执行结束或者数据发生变化时有多个消费者等着通知时它们就无能为力了。

当我们同时有多个视图在服务器的数据发生变化时每一个都需要被通知到,比如电子商务网站的购物车,
当用户添加一个物品到购物车时,需要更新显示的内容,好需要显示运输成本,订单总额等等。

为了处理这样的情况,我们需要使用publish/subscribe设计模式来处理当一个长期执行的方法结束时通知多个消费者,我们的数据发生了变化。

回调函数和Promise并不能提供大型复杂应用程序需要的灵活性和松耦合。
发布/订阅模式是一个消息模式,它允许多个消费者订阅和发布消息给其它组件,而不需要知道彼此的实现细节。
订阅消息在与该消息相关的事件发生时获取通知。长时间运行的处理或者Ajax请求,通常有三种消息:
一个是调用流程或者消息,一个是处理或者请求完成,第三个是失败通知。
消费者通常是发布启动消息来调用处理或者请求,然后订阅其完成和失败消息通知,一旦处理或者请求完成或者失败,它们会受到相关消息。
实现功能的服务或者长时间运行的处理会订阅启动消息并发布成功和失败消息。

也就是说,在该系统中分为两类,一类是发布者,一来是订阅者,它们都发布消息同时又订阅消息。已达到相互松散的交互。

 在AngularJS中使用rootScope对象的$broadcast 和 $on方法实现发布/订阅模式。
但是这里有个性能问题,因为$on 方法在每次执行digest循环被执行时被评估,当我们的应用程序有大量的订阅者等待消息发布时可能会不知不觉的影响到digest循环。
因为在每次执行digest循环是每次调用rootScope的$on方法都需要评估。

使用一个简单的利用回调的发布/订阅消息服务比较好。
它减轻了rootScope的digest循环的负担并保持在digest循环过程中需要被评估的项目数量最小。

当使用回调函数式消息机制时,如果你的回调函数也发布另外的消息作为它处理过程的一部分,那么会给拖慢我们应用程序的性能。
所以,最好使用$timeout服务来包裹发布消息代码,以让callback处理函数可以尽可能快的返回从而不影响性能。

    angular.module('brew-everywhere')
        .factory('messaging',function(){
            var cache = {};
            
            var subscribe = function(topic,callback){
                if(!cache[topic]){
                    cache[topic]=[];
                }
                cache[topic].push(callback);
                return [topic,callback];
            };
            
            var publish = function(topic, args){
                cache[topic] && angular.forEach(cache[topic]),function(callback){
                    callback.apply(null,args || []);
                });
            };
            
            var unsubscribe = function(handle){
                var t = handle[0];
                if(cache[t]){
                    for(var x=0;x<cache[t].length;x++){
                        if(cache[t][x] === handle[1]){
                            cache[t].splice(x,1);
                        }
                    }
                }
            };
            
            var service = {
                publish:publish,
                subscribe:subscribe,
                unsubscribe:unsubscribe
            };
            
            return service;
        });
        
我们实现publish/subscribe模式时定义了一个内部对象变量 cache,
用它来保存针对每个消息的订阅者和它们的回调方法的属性。
订阅方法subscribe需要一个主题topic和一个在该主题事件发布后需要调用的回调函数callback。
然后检查cache看是否存在一个名为topic的主题,如果不存在则在cache里创建一个主题属性,并创建一个空数组赋给该属性。
然后将回调函数插入到该数组里,最后返回一个处理器对象,订阅者可以使用该对象[topic,callback]根据需求退订。

发布方法publish需要一个topic字符串和一个能够传递给订阅了该事件的各个回调函数的参数数组
方法首先检查看cache对象是否包含topic主题,如果包含,会遍历它数组的每一项并调用callback并传入参数数组。
cache是一个对象,topic是一个主题key,callback组成的数组是其值。

cache = function(){
    var topic = [callback1,callback2];
}

unsubscribe 方法需要一个由subscribe方法返回的handle对象,来移除跟topic关联的callback函数。
方法首先检查看cache对象是否包含一个名为topic的属性,如果有就遍历该属性值数组直到发现跟handler里
匹配的callback然后从数组中移除。

最后,定义一个需要返回的对象。

    angular.module('brew-everywhere').controller('LoginCtrl',function($scope,messaging,events){
        $scope.username = '';
        $scope.password = '';
        $scope.currentUser = {};
        $scope.userAuthenticateHandle = null;
        
        $scope.authenticateUserCompleteHandler = function(user){
            $scope.currentUser = user;
        };
        
        $scope.userAuthenticateHandle = messaging.subscribe(
            events.message._AUTHENTICATE_USER_COMPLETE_,
            $scope.authenticateUserCompletedHandler);
            
        $scope.$on('$destory',function(){
            messaging.unsubscribe($scope.userAuthenticatedHandle);
        });
        
        $scope.onLogin = function(){
            messaging.publish(event.message._AUTHENTICATE_USER_,
                [$scope.username,$scope.password]);
        
    });
    
    LoginCtrl视图控制器依赖于messaging服务和events服务。其中events服务是一个我们定义了用于整个应用程序各类消息的常量集合。
    Controller首先定义一个消息处理器authenticateUserCompletedHandler然后以_AUTHENTICATE_USER_COMPLETE_为主题注册到messaging服务。
    然后定义onLogin方法用于on-click指令来处理用户登录行为,其处理就是发布一个主题为_AUTHENTICATE_USER_的事件,并以用户名和密码为参数数组。
    
    当用户出发ng-click事件时,消息服务会发布一个消息然后等待服务处理消息,并等待返回_AUTHENTICATE_USER_COMPLETE_消息,该消息处理器是
    将验证通过对象赋值给指定变量。
    
    处理出错将被该controller以外的专门服务和conctroller处理。
    该controller还使用$scope.$on方法来监控$destroy消息,该消息是在该controller被销毁前发出。
    所以可以处理取消订阅。避免消息服务去尝试调用一个已经不存在的回调函数。

转载于:https://my.oschina.net/u/924064/blog/901369

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值