You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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:

  1. 移除ng-pattern="isValidSwedishSSN",因为我们用了自定义验证器
  2. 在错误提示中添加自定义验证失败的信息:
<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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:50:49