我目前正在探索可能的方法来处理AngularJS中的应用程序范围的异常。
我们真正想要避免的事情之一是在嵌套的try / catch块中包装应用程序的多个部分,但干净地处理事情 – 即响应promise而抛出异常。
>有没有人覆盖这个问题,并有任何建议?
>关于如何在服务以及控制器/指令中提取异常的任何建议。 (见下面 – 广播工作确定,但只有当你可以附加监听器到范围)。
进展到目前为止
几个短设计目标:
>允许来自应用程序的一部分的异常在其他地方 – 或者可能多个地方(即“向用户显示错误通知”,“禁用窗口小部件”)处理。
>提供常见错误状况的中央管理 – 即日志到服务器,向用户显示通知,重定向到登录。
>允许从控制器,指令和服务等抛出异常。
>最终允许本地化的消息。
我的团队目前的倾向是编写一个服务来处理异常,这将暴露一系列简单的调用:
- exceptionService.warn('exception_token');
- exceptionService.crit('another_exception_token');
这个服务然后格式化一个“异常”对象,并从rootscope广播。这将允许默认处理程序监视任何广播和应用默认操作,以及允许在其他范围中设置自定义侦听器,这可以处理更具体的条件,即禁用部分UI。
- var exception = {
- token: 'exception_token',severity': 'crit'
- };
- // broadcast exception
- $rootScope.$broadcast(
- 'application_exception',exception
- );
我最近一直在想,并且它发生在我,当谈到一个良好的错误处理在JavaScript,它是不相关的哪个框架,你使用,Angular在别的东西。我最近为一个AngularJS项目写了一个这样的错误处理程序,但我做了一个方式,它可以在任何框架中使用。
- /*
- Factory errorFact is to simplify error handling and reporting in other objects.
- It supports detailed error output as a text string and into the browser's console.
- Usage example:
- A function that supports return of an error object would have the following declaration
- as its very first line:
- var e = errorFact.create("objectName.funcName",arguments);
- - in this declaration we specify the full object + method name as the first string parameter,- and as the second parameter we pass javascript's reserved variable called arguments,which
- provides reference to all of the function's parameters for logging.
- When an error occurs,the function would return:
- return e.error("Error description text");
- - this line will create and return a complete error context.
- When a function that supports return of an error object makes a call into another
- function that also supports the error context,then it can return the nested error
- result by passing the embedded error to the current error object instead of the error
- text.
- Example:
- var e = errorFact.create("objectName.funcName",arguments);
- var data = callAnotherFunc(...); // calling a function that support an error object;
- if(data.isError){ // If an error was triggered;
- return e.error(data); // return that error from the current context;
- }
- The top-level code that calls an error-returning function would do verification
- and if an error occurred,log all its details into console (typically).
- Example:
- var data = getData(...);
- if(data.isError){
- data.log(); // Output all the error details into the browser's console;
- }
- */
- "use strict";
- app.factory("errorFact",function(){
- return {
- // creates a new error context;
- create: function(method,args){
- var result = {
- // initiates and returns the error context;
- error: function(msg){
- this.info.isError = true;
- if(msg.isError){
- this.info.details.caller = msg;
- }else{
- this.info.details.msg = msg;
- }
- return this.info;
- },info:
- {
- isError: false,details: {},log: function(){
- if(this.isError){
- console.error(this.format());
- }
- },// formats complete error details into a text string;
- format: function(){
- if(this.details.caller){
- var txt = this.details.caller.format();
- txt += "\nCALLER: " + this.details.method + "(" + this.formatArguments() + ")";
- return txt;
- }
- if(this.details.method){
- return "Error calling " + this.details.method + "(" + this.formatArguments() + "): " + this.details.msg;
- }else{
- return this.details.msg;
- }
- return "";
- },// formats function argument details into a text string;
- formatArguments: function(){
- if(!this.details.args){
- return "";
- }
- var params = "";
- for(var i = 0;i < this.details.args.length;i ++){
- if(params.length > 0){
- params += ",";
- }
- var p = this.details.args[i];
- if(p === undefined){
- params += "undefined";
- }else{
- if(p === null){
- params += "null";
- }else{
- if(typeof(p) == "object"){
- params += "Object";
- }else{
- params += p;
- }
- }
- }
- }
- return params;
- }
- }
- };
- if(method){
- result.info.details.method = method;
- }
- if(args){
- result.info.details.args = args;
- }
- return result;
- }
- }
- });
下面是一个显示如何使用它的工厂:
- "use strict";
- app.factory('moduleFact',['errorFact',function(errorFact){
- return {
- // Locates existing module and expands its key Id references
- // into corresponding object references:
- // - If 'hintGroupId' is present,property 'hints' is added from
- // the corresponding hint group.
- // - If 'repModules' is present,properties 'question' and 'refs'
- // are added.
- // On success,return the expanded module object.
- // On failure,returns an error object.
- //
- // NOTE: Currently supports only the first value in repModules.
- expandModule: function(moduleData,moduleId){
- var e = errorFact.create("moduleFact.expandModule",arguments);
- if(!moduleData || !moduleData.modules || !moduleId){
- return e.error("Invalid parameters passed");
- }
- var mod = this.findModule(moduleData,moduleId);
- if(mod.isError){
- return e.error(mod);
- }
- var src = mod;
- if(mod.repModules){
- var repId = mod.repModules[0];
- if(!repId){
- return e.error("Invalid repModules encountered");
- }
- ///////////////////////////////////////
- // temporary check to throw a warning:
- if(mod.repModules.length > 1){
- console.warn("Multiple values in property repModules: " + JSON.stringify(mod.repModules) +
- ",which is not supported yet (only the first value is used)");
- }
- ///////////////////////////////////////
- src = this.findModule(moduleData,repId);
- if(src.isError){
- return e.error(src);
- }
- }
- if(src.question){
- mod.question = src.question;
- }else{
- return e.error("Question not specified");
- }
- if(src.refs){
- mod.refs = src.refs;
- }
- if(src.hintGroupId){
- var hg = this.findHintGroup(moduleData,src.hintGroupId);
- if(hg.isError){
- return e.error(hg);
- }
- mod.hints = hg.hints;
- }
- return mod; // needed extra: expand attribute repModules
- },// Expands all the modules and returns the data;
- expandAllModules: function(moduleData){
- var e = errorFact.create("moduleFact.expandAllModules",arguments);
- if(!moduleData || !moduleData.modules){
- return e.error("Invalid parameters passed");
- }
- for(var i = 0;i < moduleData.modules.length;i ++){
- var result = this.expandModule(moduleData,moduleData.modules[i].id);
- if(result.isError){
- return e.error(result);
- }
- }
- return moduleData;
- },// Locates and returns module by its Id;
- findModule: function(moduleData,moduleId){
- var e = errorFact.create("moduleFact.findModule",arguments);
- if(!moduleData || !moduleData.modules || !moduleId){
- return e.error("Invalid parameters passed");
- }
- for(var i = 0;i < moduleData.modules.length;i ++){
- if(moduleData.modules[i].id == moduleId){
- return moduleData.modules[i];
- }
- }
- return e.error("Module with Id = " + moduleId + " not found");
- },// Locates and returns Hint Group by its Id;
- findHintGroup: function(moduleData,hintGroupId){
- var e = errorFact.create("moduleFact.findHintGroup",arguments);
- if(!moduleData || !moduleData.hintGroups || !hintGroupId){
- return e.error("Invalid parameters passed");
- }
- for(var i = 0;i < moduleData.hintGroups.length;i ++){
- if(moduleData.hintGroups[i].id == hintGroupId){
- return moduleData.hintGroups[i];
- }
- }
- return e.error("Hint Group with Id = " + hintGroupId + " not found");
- }
- }
- }]);
所以,当你有这样的工厂,你的高级代码,如在控制器中只会记录任何问题,如下面的例子所示:
- "use strict";
- app.controller('standardsCtrl',['$scope','moduleFact',function($scope,moduleFact){
- var data = ...//getting data;
- var mod = moduleFact.expandAllModules(data);
- if(mod.isError){
- mod.log(); // log all error details into the console;
- }else{
- // use the data
- }
- });
- }]);