4

我有一个需要在 Angular js 中验证的三部分日期字段。我已经创建了一个自定义验证功能,但是我在设计字段应该如何更新彼此状态的逻辑时遇到了麻烦。

我怎样才能让所有三个表单域都唱出同一张赞美诗,并且根据其他表单域都显示它们的状态是有效还是无效?

这是小提琴:http: //jsfiddle.net/4GsMm/1/

和代码:

<div ng-app="myApp" ng-controller="myCtrl">
    <form action="" name="myForm">
        <div class="date-group">
            <input type="text" name="day" ng-model="day" ng-valid-func="validator" />
            <input type="text" name="month" ng-model="month" ng-valid-func="validator" />
            <input type="text" name="year" ng-model="year" ng-valid-func="validator" />
        </div>
    </form>
</div>

和...

input.ng-invalid{
    background-color: #fdd !important;    
}

input.ng-valid{
    background-color: #dfd !important;    
}

input{
    display: inline;
    width: 3em;
}

和...

var app = angular.module('myApp', [])

var myCtrl = function($scope){

    $scope.day = "01"
    $scope.month = "01"
    $scope.year = "2000"

    $scope.validator = function(val){
        var day = $('[name=day]').val()
        var month = $('[name=month]').val()
        var year = $('[name=year]').val()
        var d = new Date([year,month,day].join('-'))
        console.log(d, [year,month,day].join('-'))
        return d > new Date('2000-01-01')
    }

}

app.directive('ngValidFunc', function() {
  return {
    require: 'ngModel',
    link: function(scope, elm, attrs, ctrl) {
      ctrl.$parsers.unshift(function(viewValue) {
        if (attrs.ngValidFunc && scope[attrs.ngValidFunc] && scope[attrs.ngValidFunc](viewValue, scope, elm, attrs, ctrl)) {
          ctrl.$setValidity('custom', true);
        } else {
          ctrl.$setValidity('custom', false);
        }
        return elm.val()
      });
    }
  };
});
4

4 回答 4

4

我也在寻找使用 AngularJS 的三部分日期字段验证,这就是我所做的(为我工作)

HTML:

<form name="myForm" method="post" novalidate ng-submit="somefuncion()" ng-controller="Controller" ng-app="testModule">
    Date (MM-DD-YYYY): <input id="partnerDOBmm" name="partnerDOBmm" class="ddinput" type="text" value="" size="2" maxlength="2" ng-model="partnerDOBmm" required only-digits ng-minlength="2" ng-change="validateDOB(partnerDOBmm,partnerDOBdd,partnerDOByyyy)"  />
    -
    <input id="partnerDOBdd" name="partnerDOBdd" class="ddinput" type="text" value="" size="2" maxlength="2" ng-model="partnerDOBdd" required only-digits ng-minlength="2" ng-change="validateDOB(partnerDOBmm,partnerDOBdd,partnerDOByyyy)" />
    -
    <input id="partnerDOByyyy" name="partnerDOByyyy" class="yyyyinput" type="text" value="" size="4" maxlength="4" ng-model="partnerDOByyyy" required only-digits ng-minlength="4" ng-change="validateDOB(partnerDOBmm,partnerDOBdd,partnerDOByyyy)" />
    <br />
    <span class="error" ng-show="submitted && (myForm.partnerDOBmm.$error.required || myForm.partnerDOBdd.$error.required || myForm.partnerDOByyyy.$error.required)"> Required! </span>
    <span class="error" ng-show="submitted && (myForm.partnerDOBmm.$error.minlength || myForm.partnerDOBdd.$error.minlength || myForm.partnerDOByyyy.$error.minlength)"> Too Short! </span>
    <span class="error" ng-show="notValidDate && !(myForm.partnerDOBmm.$error.required || myForm.partnerDOBdd.$error.required || myForm.partnerDOByyyy.$error.required || myForm.partnerDOBmm.$error.minlength || myForm.partnerDOBdd.$error.minlength || myForm.partnerDOByyyy.$error.minlength)"> Not a Valid Date! </span>
    <br />
    <button type="submit" class="btnSubmit" ng-click="submitted=true">Submit</button>
</form>

脚本:

function Controller ($scope) {
    $scope.notValidDate = false;
    $scope.somefuncion = function(event) {
        if ($scope.myForm.$valid) {
            alert("Form is working as expected");
            return true;
        }
        else
        {
            alert("Something is not correct");
            return false;
        }
    };
    $scope.validateDOB  = function(mm,dd,yyyy)
    {
        var flag = true;
        var month = mm;
        var date = dd;
        var year = yyyy;
        if(month == null || date == null || year == null || month.length != 2 || date.length!= 2 || year.length!= 4)
            flag = false;
        if(month < 1 || month > 12)
            flag = false;
        if(year < 1900 || year > 2100)
            flag = false;
        if(date < 1 || date > 31)
            flag = false;

        if((month == 04 || month == 06 || month == 9 || month == 11) && (date >= 31))
            flag = false;

        if(month == 02)
        {
            if(year % 4 != 0)
            {
                if(date > 28)
                    flag = false;
            }
            if(year % 4 == 0)
            {
                if(date > 29)
                    flag = false;
            }
        }

        var dob = new Date();
        dob.setFullYear(year, month - 1, date);
        var today = new Date();
        if(dob > today)
            flag = false;

        if(flag)
        {
            $scope.notValidDate = false;
            $scope.myForm.$valid = true;
        }
        else
        {
            $scope.notValidDate = true;
            $scope.myForm.$valid = false;
            form.partnerDOBmm.focus();
        }
    }
}

angular.module('testModule', [])
.directive('onlyDigits', function () {

    return {
        restrict: 'A',
        require: '?ngModel',
        link: function (scope, element, attrs, ngModel) {
            if (!ngModel) return;
            ngModel.$parsers.unshift(function (inputValue) {
                var digits = inputValue.replace(/[^\d]/g, "");
                ngModel.$viewValue = digits;
                ngModel.$render();
                return digits;
            });
        }
    };
});

随意即兴回答,希望您可以在“validateDOB”功能中使用“else”

于 2014-02-04T12:30:46.957 回答
2

实际上,最好只使用input type="number"andmaxmin验证器,并添加一个ng-change调用函数来更新日期的指令。

由于如果输入的内容无效,则不会触发更改,因此您永远不会得到“错误”日期:

<input type="number" name="year" ng-model="year" min="2000" ng-change="updateDate()"/>
<input type="number" name="month" ng-model="month" min="1" max="12" ng-change="updateDate()" />
<input type="number" name="day" ng-model="day" min="1" max="31" ng-change="updateDate()" />

这是一个说明此解决方案的 plunk。

然而,使用文本框来表示月份和日期可能是不好的解决方案,因为确保比日期值更复杂会变得更加复杂。(以二月和闰年为例)。对于整个解决方案,我建议至少在几天内使用下拉菜单,也可能在一个月内使用……因为有一组固定的结果是必需的,您可以根据月份选项的值。

这是一个例子:

<form name="myForm">
    <input type="number" name="year" ng-model="year" min="2000" ng-change="updateDate()"/>
    <select name="month" ng-model="month" ng-change="updateDate()">
      <option value="1">Jan</option>
      <option value="2">Feb</option>
      <option value="3">Mar</option>
      <option value="4">Apr</option>
      <option value="5">May</option>
      <option value="6">Jun</option>
      <option value="7">Jul</option>
      <option value="8">Aug</option>
      <option value="9">Sep</option>
      <option value="10">Oct</option>
      <option value="11">Nov</option>
      <option value="12">Dec</option>
    </select>
    <select name="day" ng-model="day" ng-change="udpateDate()">
      <option>1</option>
      <option>2</option>
      <option>3</option>
      <!-- ... SNIP!... -->
      <option>27</option>
      <option>28</option>
      <option ng-show="month != 2 || !(year % 4)")>
        29
      </option>
      <option ng-show="month != 2">
        30
      </option>
      <option ng-show="month == 1 || month == 3 || month == 5 || month == 7 || 
          month == 8 || month == 10 || month == 12">
        31
      </option>
    </select>
    <p>
      {{date | date: 'yyyy-MMM-dd'}}
    </p>
  </form>

但是,为什么不动态创建这些选择呢?

您可以动态创建上述选择吗?当然。这值得么?也许?可能不是。只需 30 秒即可输入选项以及显示/海德逻辑。

这是一个简单的例子。

在上述两种情况下,您只需验证 2001 年的年份验证:

<span ng-show="myForm.year.$error.min">Must be after January 1, 2001</span>
于 2013-09-27T16:38:54.383 回答
1

我有一个非常相似的问题。问题是,您可能不想检查彼此相关的字段。如果您这样做,则很难确定日期是否实际有效,例如,如果不是 2 月 30 日。您要做的是通过中间元素检查整个日期。

作为旁注 - 根据设计,控制器不应参与此类活动。验证应该在指令级别完成,因此它是可重用的并且与 Angular 一致,为您提供验证器链的好处。例如,如果您只需要检查一个有效日期,但在另一个中您需要检查出生日期的年龄,那么您最终会“弄湿”您的代码:)

我想出了一个相当简单的解决方案,它非常适合 Angular 哲学。诀窍是使用中间表单元素(例如隐藏输入),它将所有三个下拉列表组合在一起并一次性对整个日期执行验证。

这是用于设置焦点的 HTML:

<form name="dateForm" novalidation>
  <input type="hidden"
      ng-model="modelDate"
      date-type-multi="viewDate"
      ng-init="viewDate = {}"
      class="form-control"
  />
  <select ng-model="viewDate.day">
    <option value="">select day</option>
    <option value="1">1</option>
    <option value="2">2</option>
    <option value="3">3</option>
    <option value="4">4</option>
    <option value="5">5</option>
    <option value="6">6</option>
    <option value="7">7</option>
    <option value="8">8</option>
    <option value="9">9</option>
    <option value="10">10</option>
    <option value="11">11</option>
    <option value="12">12</option>
    <option value="13">13</option>
    <option value="14">14</option>
    <option value="15">15</option>
    <option value="16">16</option>
    <option value="17">17</option>
    <option value="18">18</option>
    <option value="19">19</option>
    <option value="20">20</option>
    <option value="21">21</option>
    <option value="22">22</option>
    <option value="23">23</option>
    <option value="24">24</option>
    <option value="25">25</option>
    <option value="26">26</option>
    <option value="27">27</option>
    <option value="28">28</option>
    <option value="29">29</option>
    <option value="30">30</option>
    <option value="31">31</option>
  </select>
  <select ng-model="viewDate.month">
    <option value="">select month</option>
    <option value="1">1</option>
    <option value="2">2</option>
    <option value="3">3</option>
    <option value="4">4</option>
    <option value="5">5</option>
    <option value="6">6</option>
    <option value="7">7</option>
    <option value="8">8</option>
    <option value="9">9</option>
    <option value="10">10</option>
    <option value="11">11</option>
    <option value="12">12</option>
  </select>
  <select ng-model="viewDate.year">
    <option value="">select year</option>
    <option value="1981">1981</option>
    <option value="1982">1982</option>
    <option value="1983">1983</option>
    <option value="1984">1984</option>
    <option value="1985">1985</option>
    <option value="1986">1986</option>
    <option value="1987">1987</option>
    <option value="1988">1988</option>
    <option value="1989">1989</option>
    <option value="1990">1990</option>
    <option value="1991">1991</option>
    <option value="1992">1992</option>
  </select>
</form>

和JS代码:

angular.module('dateApp', []).
  directive('dateTypeMulti', function() {
    return {
      priority: -1000,
      require: 'ngModel',
      link: function(scope, elem, attrs, ngModel) {
        ngModel.$render = function() {
          angular.extend(scope.$eval(attrs.dateTypeMulti), ngModel.$viewValue);
        };

        scope.$watch(attrs.dateTypeMulti, function(viewValue) {
          ngModel.$setViewValue(viewValue);
        }, true);

        ngModel.$formatters.push(function(modelValue) {
          if (!modelValue) return;

          var parts = String(modelValue).split('/');

          return {
            year: parts[0],
            month: parts[1],
            day: parts[2]
          };
        });

        ngModel.$parsers.unshift(function(viewValue) {
          var isValid = true,
            modelValue = '',
            date;

          if (viewValue) {
            date = new Date(viewValue.year, viewValue.month - 1, viewValue.day);
            modelValue = [viewValue.year, viewValue.month, viewValue.day].join('/');

            if ('//' === modelValue) {
              modelValue = '';
            } else if (
              date.getFullYear() != viewValue.year ||
              date.getMonth() != viewValue.month - 1 ||
              date.getDate() != viewValue.day) {
              isValid = false;
            }
          }

          ngModel.$setValidity('dateTypeMulti', isValid);

          return isValid ? modelValue : undefined;
        });
      }
    };
  });

在指令上设置低优先级很重要,因为这样解析器将作为第一个触发(与格式化程序的工作顺序相反),并且该字段上的其他验证器将获得解析日期。

你可以在这里玩它:http: //codepen.io/jciolek/pen/oxBch

我在这里详细描述了思考过程:http: //float-middle.com/multiple-fields-validation-in-angularjs/

我希望它有帮助,杰克

于 2014-10-10T22:15:28.820 回答
1

我们在最后一个字段中添加一个指令,并在指令中使用 $watchCollection,如下所示(请原谅 coffeescript)

@validateDateDirective = ->
    require: "ngModel"
    link: (scope, elem, attr, ngModel) ->
        # Get the base model name without the _day _month _year appended
        # e.g for field 'birth_date_year', this would equal 'birth_date'
        modelName = attr.ngModel.replace('_day', '').replace('_month', '').replace('_year', '')

        # use base model name to watch all models in target fieldset
        scope.$watchCollection '['+modelName+'_day, '+modelName+'_month, '+modelName+'_year]', ->
            # Set global value
            value = [scope.$eval(modelName+'_year'),
                    scope.$eval(modelName+'_month'), 
                    scope.$eval(modelName+'_day')]

            # << do your validation here >>
于 2014-11-08T04:22:54.803 回答