AngularJS中$exceptionHandler如何捕获触发错误的HTML元素与函数入参
实现方案
步骤1:给业务函数添加上下文捕获逻辑
你需要的两类信息都可以在函数触发时直接获取:
- HTML元素可以从事件参数
$event.target中读取,包含标签、类型、ID、属性等完整DOM信息 - 入参可以直接读取函数的传入参数对象
推荐给需要监控的函数外层包裹try-catch,捕获错误时将上下文信息附加到异常对象后再抛出,示例代码如下:
// 控制器中先注入$location,用于获取当前页面路由 $scope.monitorLength = function($event,item,itemID,mLengths,deny) { try { // 原有函数逻辑保持不变 var permitChars = [8,35,36,46] ; //允许删除、退格键 var permitZip = [32,109,189] ; //允许邮编中出现的字符 var keyCode = $event.keyCode || $event.charCode ; if (!permitChars.includes(keyCode)) { if (deny == 1 && isNaN(String.fromCharCode(keyCode))) { $scope.errMsg = "不允许输入非数字字符,例如+ . , -等" ; $scope.errColor = "red" ; $event.preventDefault() ; return ; } if (deny == 2 && (isNaN(String.fromCharCode(keyCode)) || permitZip.includes(keyCode))) { $scope.errMsg = "仅允许输入数字、空格和横杠字符" ; $scope.errColor = "red" ; $event.preventDefault() ; return ; } var len = $scope[item][itemID] ; if (deny == 1 && (/^0/.test(len) || /^1/.test(len))) { $scope.errMsg = "已自动选择国家/区号,请直接输入主号码" ; $scope.errColor = "red" ; $event.preventDefault() ; return ; } var maxLength = parseInt(mLengths[mLengths.length-1]) ; if (len && (len.toString().length > maxLength)) { $scope.errMsg = "该字段最大长度为" +maxLength+ "个字符" $scope.errColor = "red" ; $event.preventDefault() ; return ; } } else { $scope.errMsg = "" ; } } catch (e) { // 附加自定义上下文信息 e.bugContext = { // 触发元素信息 triggerElement: $event ? { tagName: $event.target.tagName, type: $event.target.type, id: $event.target.id, outerHTML: $event.target.outerHTML } : "函数由JS逻辑直接调用,无关联DOM元素", // 函数入参 inputArgs: { item: item, itemID: itemID, mLengths: mLengths, deny: deny }, // 可选:当前页面路由信息,用于判断所属页面 currentRoute: $location.path() } // 重新抛出异常,让全局错误处理器捕获 throw e; } }
如果多个函数需要添加该逻辑,可以封装通用的包装函数避免重复代码:
// 通用包装工具 const wrapWithErrorContext = (fn, getContext) => { return function() { try { return fn.apply(this, arguments) } catch (e) { e.bugContext = getContext.apply(this, arguments) throw e } } } // 使用示例 $scope.monitorLength = wrapWithErrorContext( // 原函数逻辑 function($event,item,itemID,mLengths,deny) { /* 原有代码 */ }, // 上下文获取逻辑 function($event,item,itemID,mLengths,deny) { return { triggerElement: $event ? $event.target : null, args: Array.from(arguments), route: $location.path() } } )
步骤2:改造全局$exceptionHandler读取上下文信息
在原有错误处理逻辑中新增对自定义上下文的读取,序列化后添加到邮件内容即可:
.config(function ($provide,apiServiceProvider) { $provide.decorator('$exceptionHandler', function($delegate) { return function (exception, cause) { var msg = ` DateTime: ${new Date()}`; msg += ` AppVersion: ${appFullVer}`; msg += ` Exception: ${exception.message}`; msg += `<br>Stack: ${exception.stack}`; // 新增:读取自定义上下文信息 if (exception.bugContext) { msg += `<br>错误上下文:${JSON.stringify(exception.bugContext, null, 2)}` } if (cause) { msg += `<br>Cause: ${cause}`; } else { msg += `<br>Cause: none passed`; } if (httpErrorID == 1) { msg += ` HTTP Error: ${httpErrorMsg}`; httpErrorID = 0; } if (errTesting == 0) { apiServiceProvider.$get().mail(5,"AppError","Bug","App Angular JS Error",msg); } else { console.log(msg); } } }); })
额外说明
你当前遇到的undefined is not an object (evaluating 'mLengths.length')错误,本质是调用monitorLength时传入的mLengths参数为undefined,添加上下文捕获后你可以直接看到是哪个场景下传参错误,快速定位问题。
如果使用了ui-router,还可以将$state.current.name(当前路由名称)、$state.current.templateUrl(当前模板路径)添加到上下文中,更方便判断错误所属页面。
内容的提问来源于stack exchange,提问作者rolinger
相关产品推荐
相关产品推荐

