公司动态
如何实现异步表单校验:CustomerManagerStandard之wcUnique自定义指令(邮箱唯一性校验)深度拆解
如何实现异步表单校验CustomerManagerStandard之wcUnique自定义指令邮箱唯一性校验深度拆解【免费下载链接】CustomerManagerStandardCustomer Manager AngularJS/BreezeJS Application.项目地址: https://gitcode.com/gh_mirrors/cu/CustomerManagerStandardCustomerManagerStandard是一个基于 AngularJS BreezeJS 的客户管理应用示例内置了一套完整的异步表单校验方案通过wcUnique自定义指令实现邮箱唯一性校验——用户在编辑客户邮箱时应用会实时向服务端发请求判断该邮箱是否已被其他客户占用从而在提交前就拦截重复数据。本文带你拆解这套异步校验的完整机制指令原理、表单接入方式与服务端查重逻辑帮你掌握 AngularJS 异步表单校验的通用思路。项目背景一个典型的 AngularJS 客户管理应用先认识一下项目本身。CustomerManagerStandard 采用「前端 AngularJS 后端 Web API.NET 与 Node.js 双后端」架构核心能力包括客户管理增删改查、卡片/列表双视图、筛选与分页订单管理客户订单的展示与维护BreezeJS 数据层本地缓存与服务器同步提供实体框架般的开发体验邮箱唯一性校验就发生在「新增/编辑客户」表单中——填入一个已被占用的邮箱地址时表单会立刻提示Email already in use并禁用提交按钮。为什么邮箱唯一性校验必须异步必填、最小长度、格式等常规校验都可以靠正则或内置验证器同步完成但「唯一性」不同答案在数据库里只有服务端知道某个邮箱是否已被使用。这带来两个硬性约束必须发请求校验函数内部要调用 HTTP API耗时不可控所以必须返回 Promise不能同步返回布尔值必须挂对钩子AngularJS 的ngModel控制器专门提供了异步验证器入口$asyncValidators校验结果以 Promise 形式返回框架会自动将其纳入$valid/$error.unique状态体系。wcUnique指令正是围绕这两点设计的源码位于CustomerManager/app/customersApp/directives/wcUnique.js。wcUnique 指令工作原理核心机制$asyncValidators 异步验证器指令的核心在 link 函数中——为ngModel注册一个名为unique的异步验证器ngModel.$asyncValidators.unique function (modelValue, viewValue) { var deferred $q.defer(), currentValue modelValue || viewValue, key attrs.wcUniqueKey, property attrs.wcUniqueProperty; if (key property) { dataService.checkUniqueValue(key, property, currentValue) .then(function (unique) { if (unique) { deferred.resolve(); // 唯一校验通过 } else { deferred.reject(); // 重复$error.unique 置为 true } }); return deferred.promise; } else { return $q.when(true); // 参数未就绪直接放行 } };拆解三个要点要点说明返回 Promise校验器返回deferred.promiseAngularJS 在 resolve 时判定有效、reject 时判定无效并自动更新$error.uniqueresolve/reject 语义服务端返回唯一则resolve()重复则reject()让$error.unique变为true依赖注入通过require: ngModel拿到控制器注入$q与dataService指令不硬编码任何业务细节key 与 property 双属性设计让指令可复用指令本身不知道自己在校验邮箱还是用户名它只读取两个属性data-wc-unique-key{{vm.customer.id}}—— 当前记录 id用于排除与自己比较data-wc-unique-propertyemail—— 要检查唯一性的字段名换一个字段只需改属性值指令代码零改动。这就是把「业务参数」放到模板属性、把「校验骨架」放到指令中的经典分离手法。兜底策略key 未就绪时直接放行注意if (key property)这段防御式判断。首次加载时模板插值{{vm.customer.id}}可能尚未求值此时若带着空 key 请求服务端会出错。返回已 resolve 的$q.when(true)表示先放行等 key 就绪、下次 blur 触发校验时再真正检查。这种「参数不全不报错」的兜底是编写可复用指令时非常值得学习的一点。在表单中接入 wcUnique三个关键配置在CustomerManager/app/customersApp/views/customers/customerEdit.html中邮箱输入框的完整用法如下input typetext nameemail classform-control />单元测试覆盖了哪些场景CustomerManager/test/unit/wcUniqueSpec.js用 Jasmine angular-mocks对指令做了完整的边界覆盖测试思路本身也值得借鉴最小长度优先初始状态因ng-minlength未满足而无效确保同步验证器先于异步验证器生效触发时机验证输入值并触发 blur 后断言checkUniqueValue确实被调用用spyOnmock 服务层重复值拒绝填入已存在的abcdef.com断言form.email.$error.unique true唯一值通过填入未占用的someonegmail.com断言校验通过服务端故障容错模拟请求 reject 的场景验证表单不会陷入错误死循环。 关键启示异步校验的单元测试必须 mock 网络层否则测试既慢又不可靠——用spyOn(...).and.callFake(...)直接返回受控的 Promise是 AngularJS 测试异步指令的标准姿势。总结一份可复用的异步表单校验清单把 wcUnique 的方案抽象出来任何需要「服务端判重」的场景用户名、手机号、订单号都能套用✅异步验证器挂$asyncValidators返回 Promiseresolve 通过 / reject 失败✅触发时机选 blur避免逐字符请求风暴✅allowInvalid: true保留用户输入只标记错误不擦值✅用属性传业务参数key/property指令保持通用✅参数未就绪时兜底放行避免初始化时序问题✅服务端查重排除自身 id编辑场景不误报✅单元测试 mock 网络层覆盖通过/拒绝/超时三类分支。关键文件速查文件路径职责CustomerManager/app/customersApp/directives/wcUnique.js异步唯一性校验指令CustomerManager/app/customersApp/views/customers/customerEdit.html客户编辑表单接入示例CustomerManager/app/customersApp/services/customersService.jsHTTP 查重请求封装CustomerManager/app/customersApp/services/dataService.js数据服务门面Breeze/HTTP 切换CustomerManager/Controllers/BreezeDataServiceController.cs.NET 端查重实现CustomerManager/server/routes/api.jsNode.js 端查重路由CustomerManager/test/unit/wcUniqueSpec.js指令单元测试想动手体验克隆仓库即可本地运行git clone https://gitcode.com/gh_mirrors/cu/CustomerManagerStandard【免费下载链接】CustomerManagerStandardCustomer Manager AngularJS/BreezeJS Application.项目地址: https://gitcode.com/gh_mirrors/cu/CustomerManagerStandard创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考