自定义DatePicker Angular Formly字段不显示验证错误消息

Osk*_*son 6 javascript validation datetimepicker angularjs angular-formly

我正在尝试使用Angular Datetime Picker作为Angular Formly输入类型.我已经使它工作,我可以编辑和设置一个正确添加到绑定模型的值.

但是,我无法在常规输入字段中显示验证错误消息.

JS Bin和我到目前为止所得到的.正如您所看到的,当您退出该字段时,只有在您尝试提交时才会显示红色.并且错误消息永远不会显示出来.

形式配置:

formlyConfigProvider.setType({
  name: 'datepicker',
  templateUrl: "custom-template.html",
  overwriteOk: true,
  wrapper: ['bootstrapHasError'],
  defaultOptions: function defaultOptions(options) {
    return {
      templateOptions: {
        validation: {
          show: true
        }
      }
    };
  }
});

formlyConfigProvider.setWrapper({
  name: 'validation',
  types: ['input', 'datepicker'],
  templateUrl: 'error-messages.html'
});
Run Code Online (Sandbox Code Playgroud)

字段

vm.fields = [
  {
    key: 'text',
    type: 'input',
    templateOptions: {
      label: 'Text',
      placeholder: 'Write something',
      required: true
    },
  },
  {
    key: 'date',
    type: 'datepicker',
    templateOptions: {
      label: 'Date',
      placeholder: 'Pick a date',
      required: true
    },
  }
];
Run Code Online (Sandbox Code Playgroud)

模板

<script type="text/ng-template" id="custom-template.html">
  <div class="form-group">

      <label class="control-label" for="{{::id}}">{{to.label}} {{to.required ? '*' : ''}}</label>
      <div class="dropdown">
          <a class="dropdown-toggle" id="dropdown-{{options.key}}" role="button" data-toggle="dropdown">
          <div class="input-group">
              <input id="{{::id}}" name="{{::id}}" type="text" data-date-time-input="YYYY-MM-DD" class="form-control" data-ng-model="model[options.key]"><span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>
          </div>
        </a>
        <ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
          <datetimepicker
              data-ng-model="model[options.key]"
              data-datetimepicker-config="{ dropdownSelector: '#dropdown-' + options.key, minView: 'day', startView: 'year', modelType: 'YYYY-MM-DDTHH:mm:ssZ'}"/>
        </ul>
      </div>
  </div>

</script>

<script type="text/ng-template" id="error-messages.html">
  <formly-transclude></formly-transclude>
  <div ng-messages="fc.$error" ng-if="form.$submitted || options.formControl.$touched" class="error-messages">
    <div ng-message="{{ ::name }}" ng-repeat="(name, message) in ::options.validation.messages" class="message">{{ message(fc.$viewValue, fc.$modelValue, this)}}</div>
  </div>
</script>
Run Code Online (Sandbox Code Playgroud)

Aru*_*una 3

经过更深入的调查后,我可以看到您使用的控件Angular Datetime Picker与Angular Formly不完全兼容。

这是因为它覆盖了 AngularJS 的 ngModelController.$render()方法,因此不像其他输入控件那样设置值$touched

代码中的另一个原因是,配置和模板 error-messages.html将自定义控件视为单个元素fc.$touchedfc.$errorfc.$viewValueDatePicker渲染为元素组(数组)。

要摆脱所有这些问题,您可以设置一个自定义指令,$touched如下所示,

app.directive('setTouched', function MainCtrl() {
    return {
      restrict: 'A', // only activate on element attribute
      require: '?ngModel', // get a hold of NgModelController
      link: function(scope, element, attrs, ngModel) {
         if (!ngModel) return; // do nothing if no ng-model
         element.on('blur', function() {
            var modelControllers = scope.$eval(attrs.setTouched);
            if(angular.isArray(modelControllers)) {
              angular.forEach(modelControllers, function(modelCntrl) {
                modelCntrl.$setTouched();
              });
            }            
         });
      }
    };
  });
Run Code Online (Sandbox Code Playgroud)

并且在custom-template.html

<div class="input-group">
   <input set-touched="options.formControl" id="{{::id}}" name="{{::id}}" type="text" data-date-time-input="YYYY-MM-DD" class="form-control" data-ng-model="model['date1']"><span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>
</div>
Run Code Online (Sandbox Code Playgroud)

并添加fc[0].$touched下面的配置来处理字段数组,

app.run(function run(formlyConfig, formlyValidationMessages) {
    formlyConfig.extras.errorExistsAndShouldBeVisibleExpression = 'form.$submitted || fc.$touched || fc[0].$touched';
    formlyValidationMessages.addStringMessage('required', 'This field is required');
});
Run Code Online (Sandbox Code Playgroud)

并添加以下部分error-messages.html来处理字段数组,

<div ng-messages="fc[0].$error" ng-if="form.$submitted || options.formControl[0].$touched" class="error-messages">
    <div ng-message="{{ ::name }}" ng-repeat="(name, message) in ::options.validation.messages" class="message">{{ message(fc[0].$viewValue, fc[0].$modelValue, this)}}</div>
</div>
Run Code Online (Sandbox Code Playgroud)

此更改将解决该问题。

正如您可以看到下面显示的错误消息存在一些设计问题,

custom-template.html您可以通过删除 div 包装来更改如下<div class="form-group">

<script type="text/ng-template" id="custom-template.html">
    <label class="control-label" for="{{::id}}"
           uib-popover="{{options.templateOptions.desc}}"
           popover-trigger="mouseenter"
           popover-placement="top-left"
           popover-popup-delay="500"
           popover-append-to-body="true">{{to.label}} {{to.required ? '*' : ''}}</label>

          <div class="dropdown">
              <a class="dropdown-toggle" id="dropdown-{{options.key}}" role="button" data-toggle="dropdown">
              <div class="input-group">
                  <input set-touched="options.formControl" id="{{::id}}" name="{{::id}}" type="text" data-date-time-input="YYYY-MM-DD" class="form-control" data-ng-model="model['date1']"><span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>
              </div>
            </a>
            <ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
              <datetimepicker
                  data-ng-model="model[options.key]"
                  data-datetimepicker-config="{ dropdownSelector: '#dropdown-' + options.key, minView: 'day', startView: 'year', modelType: 'YYYY-MM-DDTHH:mm:ssZ'}"/>
            </ul>
          </div>

    </script>
Run Code Online (Sandbox Code Playgroud)

我已经用这些更改更新了您的JSBin 。

片段

app.directive('setTouched', function MainCtrl() {
    return {
      restrict: 'A', // only activate on element attribute
      require: '?ngModel', // get a hold of NgModelController
      link: function(scope, element, attrs, ngModel) {
         if (!ngModel) return; // do nothing if no ng-model
         element.on('blur', function() {
            var modelControllers = scope.$eval(attrs.setTouched);
            if(angular.isArray(modelControllers)) {
              angular.forEach(modelControllers, function(modelCntrl) {
                modelCntrl.$setTouched();
              });
            }            
         });
      }
    };
  });
Run Code Online (Sandbox Code Playgroud)
<div class="input-group">
   <input set-touched="options.formControl" id="{{::id}}" name="{{::id}}" type="text" data-date-time-input="YYYY-MM-DD" class="form-control" data-ng-model="model['date1']"><span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>
</div>
Run Code Online (Sandbox Code Playgroud)
app.run(function run(formlyConfig, formlyValidationMessages) {
    formlyConfig.extras.errorExistsAndShouldBeVisibleExpression = 'form.$submitted || fc.$touched || fc[0].$touched';
    formlyValidationMessages.addStringMessage('required', 'This field is required');
});
Run Code Online (Sandbox Code Playgroud)