如何在Angular表单中使用自定义ng-pattern函数验证瑞典SSN
解决AngularJS表单中瑞典社会保障号(SSN)的验证问题
看起来你在实现瑞典SSN表单验证时遇到了两个核心问题:一是自定义的校验逻辑不会随用户输入触发,二是没法正确获取输入框的实时值。下面是针对性的解决方案:
一、为什么当前代码不工作?
先明确你现有JS代码的问题:
- 你把
isValidSwedishSSN写成了立即执行函数,这意味着它只会在页面初始化时运行一次,之后用户输入任何内容都不会重新计算校验结果。 - 直接访问
user.user.ssn的方式无法监听输入框的变化,自然拿不到最新的输入值。
二、正确的解决方案:用自定义验证器
对于Luhn算法这种复杂的校验逻辑,AngularJS的ng-pattern(适合简单正则匹配)不是最佳选择,推荐使用ngModel.$validators来创建自定义验证规则——它会自动在用户输入变化时触发验证,还能实时获取最新的输入值。
方法1:在控制器中添加验证器(快速实现)
如果你只在当前页面使用这个验证,可以直接在控制器里给ssn控件添加验证器:
// 假设你的表单是form1,输入框的name属性是ssn $scope.form1.ssn.$validators.swedishSSN = function(modelValue, viewValue) { // 获取当前输入值:viewValue是用户输入的原始值,modelValue是绑定的模型值 const ssn = viewValue || modelValue; // 如果字段为空,交给required验证处理,这里返回true if (!ssn) return true; // 第一步:验证格式是否符合要求(支持ÅÅÅÅMMDD-XXXX或ÅÅMMDD-XXXX) const formatPattern = /^(\d{8}-\d{4}|\d{6}-\d{4})$/; if (!formatPattern.test(ssn)) return false; // 第二步:提取数字并执行Luhn校验 const ssnDigits = ssn.replace(/\D/g, ""); // 取最后10位(兼容12位和10位的SSN格式) const luhnDigits = ssnDigits.slice(-10).split("").reverse(); if (luhnDigits.length !== 10) return false; const sum = luhnDigits .map(Number) .reduce((prev, curr, index) => { // 每隔一位乘以2 if (index % 2) curr *= 2; // 大于9则减去9(等价于各位数字相加) if (curr > 9) curr -= 9; return prev + curr; }, 0); // 第三步:可选添加日期有效性校验(解决你提到的"000000-0000"返回true的问题) const datePart = ssnDigits.substring(0, ssnDigits.length - 4); let year, month, day; if (datePart.length === 8) { year = parseInt(datePart.slice(0,4), 10); month = parseInt(datePart.slice(4,6), 10) - 1; // 月份从0开始 day = parseInt(datePart.slice(6,8), 10); } else { // 处理两位数年份,这里默认按1900-1999处理,可根据需求调整为2000+ const twoDigitYear = parseInt(datePart.slice(0,2), 10); year = twoDigitYear >= 0 && twoDigitYear <= 99 ? twoDigitYear + 1900 : null; month = parseInt(datePart.slice(2,4), 10) - 1; day = parseInt(datePart.slice(4,6), 10); } let isValidDate = false; if (year && month !== undefined && day) { const date = new Date(year, month, day); // 验证日期是否合法(避免无效日期如2023-02-30) isValidDate = date.getFullYear() === year && date.getMonth() === month && date.getDate() === day; } // 返回最终校验结果:Luhn通过 + 日期合法(如果开启日期验证) return sum % 10 === 0 && isValidDate; };
然后修改你的HTML:
- 移除
ng-pattern="isValidSwedishSSN",因为我们用了自定义验证器 - 在错误提示中添加自定义验证失败的信息:
<div ng-if="uf.userClicked" ng-messages="form1.ssn.$error" role="alert"> <div md-colors="{ 'color': 'primary' }" ng-message="required">Måste fyllas i.</div> <div md-colors="{ 'color': 'primary' }" ng-message="swedishSSN"> Du måste skriva ett giltigt personnummer i formatet 'ÅÅÅÅMMDD-XXXX' eller 'ÅÅMMDD-XXXX'. </div> </div>
方法2:创建自定义指令(复用性更强)
如果需要在多个页面使用瑞典SSN验证,推荐封装成自定义指令:
angular.module('你的应用模块名').directive('swedishSsn', function() { return { require: 'ngModel', link: function(scope, element, attrs, ngModel) { ngModel.$validators.swedishSsn = function(modelValue, viewValue) { const ssn = viewValue || modelValue; if (!ssn) return true; // 格式验证 const formatPattern = /^(\d{8}-\d{4}|\d{6}-\d{4})$/; if (!formatPattern.test(ssn)) return false; // Luhn校验 const ssnDigits = ssn.replace(/\D/g, ""); const luhnDigits = ssnDigits.slice(-10).split("").reverse(); if (luhnDigits.length !== 10) return false; const sum = luhnDigits .map(Number) .reduce((prev, curr, index) => { if (index % 2) curr *= 2; if (curr > 9) curr -= 9; return prev + curr; }, 0); // 日期验证(可选) const datePart = ssnDigits.substring(0, ssnDigits.length - 4); let year, month, day; if (datePart.length === 8) { year = parseInt(datePart.slice(0,4), 10); month = parseInt(datePart.slice(4,6), 10) - 1; day = parseInt(datePart.slice(6,8), 10); } else { const twoDigitYear = parseInt(datePart.slice(0,2), 10); year = twoDigitYear >= 0 && twoDigitYear <= 99 ? twoDigitYear + 1900 : null; month = parseInt(datePart.slice(2,4), 10) - 1; day = parseInt(datePart.slice(4,6), 10); } let isValidDate = false; if (year && month !== undefined && day) { const date = new Date(year, month, day); isValidDate = date.getFullYear() === year && date.getMonth() === month && date.getDate() === day; } return sum % 10 === 0 && isValidDate; }; } }; });
然后在HTML的input上添加这个指令:
<input type="text" flex-xs="100" md-min-length="13" placeholder="ÅÅÅÅMMDD-XXXX" ng-change="uf.searchUser()" required ng-model="uf.user.user.ssn" name="ssn" id="ssn" swedish-ssn md-colors="{'backgroundColor': (form1.ssn.$invalid && uf.userClicked) ? 'primary-100' : 'primary-50', 'borderColor': (form1.ssn.$invalid && uf.userClicked) ? 'primary' : 'grey-300'}" tabindex="1">
错误提示部分和方法1一致,添加ng-message="swedishSsn"的提示即可。
三、关键说明
- 实时获取输入值:通过
viewValue和modelValue,我们能拿到输入框的最新内容,解决了你之前无法正确获取值的问题。 - 自动触发验证:
ngModel.$validators会在用户输入、模型值变化时自动调用验证函数,完美解决了验证不触发的问题。 - 可选日期验证:如果你不需要校验日期合法性,直接去掉日期相关的代码即可,只保留格式和Luhn校验。
内容的提问来源于stack exchange,提问作者Peter
相关产品推荐
相关产品推荐

