6

我试图使用Angular Datetime Picker作为Angular Formly输入类型。我已经使其工作,以便我可以编辑并设置正确添加到绑定模型中的值。验证错误消息不会出现在自定义DatePicker Angular Formly字段

但是,我无法得到验证错误消息显示像常规输入字段。

JS Bin with what I've got so far。正如你所看到的,只有当你尝试提交时,退出该字段时才会出现红色。并且错误消息从不出现。

Formly配置

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' 
}); 

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 
    }, 
    } 
]; 

模板

<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> 
+0

你能解释的问题多一点,因为如果我点击标签为“文本”的第一个文本框,然后点击下一个文本框“其他文本”,第一个文本框在chrome中显示验证错误。但是“日期”没有发生。这是问题吗? – Aruna

+0

是的,这是问题 –

+0

你能检查我的下面的答案吗? – Aruna

回答

3

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

这是因为,它的覆盖AngularJS的 ngModelController.$render()方法,因此不为$touched像其他的输入控制设定值 的原因。

在代码的另一个原因是,在配置和模板 error-messages.htmlfc.$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(); 
       }); 
      }    
     }); 
     } 
    }; 
    }); 

而且在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> 

,并在下面配置中添加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'); 
}); 

,并添加下面的部分在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> 

这种变化将解决这个问题。

正如你可以看到有些设计问题与显示进一步下跌的错误消息,

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

<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> 

我已更新您的JSBin这些更改。

片段

/* global angular */ 
 
(function() { 
 
    
 
    'use strict'; 
 

 
    var app = angular.module('formlyExample', ['formly', 'formlyBootstrap', 'ngAnimate', 'ngMessages', 'ui.bootstrap.datetimepicker', 'ui.dateTimeInput'], function config(formlyConfigProvider) { 
 
    
 
    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' 
 
    }); 
 

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

 
    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(); 
 
       }); 
 
      }    
 
     }); 
 
     } 
 
    }; 
 
    }); 
 
    
 

 
    app.controller('MainCtrl', function MainCtrl(formlyVersion) { 
 
    var vm = this; 
 
    
 
    vm.onSubmit = onSubmit; 
 
    vm.model = {}; 
 
    vm.options = {}; 
 
     vm.env = { 
 
     angularVersion: angular.version.full, 
 
     formlyVersion: formlyVersion 
 
    }; 
 
    
 
    vm.fields = [ 
 
     { 
 
     key: 'text', 
 
     type: 'input', 
 
     templateOptions: { 
 
      label: 'Text', 
 
      placeholder: 'Write something', 
 
      required: true 
 
     }, 
 
     }, 
 
     { 
 
     key: 'moretext', 
 
     type: 'input', 
 
     templateOptions: { 
 
      label: 'More Text', 
 
      placeholder: 'Write something else', 
 
     }, 
 
     }, 
 
     { 
 
     key: 'date', 
 
     type: 'datepicker', 
 
     templateOptions: { 
 
      label: 'Date', 
 
      placeholder: 'Pick a date', 
 
      required: true 
 
     }, 
 
     } 
 
    ]; 
 
    
 

 
    vm.originalFields = angular.copy(vm.fields); 
 

 
    // function definition 
 
    function onSubmit() { 
 
     if (vm.form.$valid) { 
 
     vm.options.updateInitialValue(); 
 
     alert(JSON.stringify(vm.model), null, 2); 
 
     } 
 
    } 
 
    }); 
 

 
})();
body { 
 
    margin: 20px 
 
} 
 

 
.formly-field { 
 
    margin-bottom: 30px; 
 
} 
 

 
.error-messages { 
 
    position: relative; 
 
} 
 

 
.error-messages, .message { 
 
    opacity: 1; 
 
    transition: .3s linear all; 
 
} 
 

 
.message { 
 
    font-size: .8em; 
 
    position: absolute; 
 
    width: 100%; 
 
    color: #a94442; 
 
    margin-top: 4px; 
 
} 
 

 
.error-messages.ng-enter.ng-enter-active, 
 
.message.ng-enter.ng-enter-active { 
 
    opacity: 1; 
 
    top: 0; 
 
} 
 

 
.error-messages.ng-enter, 
 
.message.ng-enter { 
 
    opacity: 0; 
 
    top: -10px; 
 
} 
 

 
.error-messages.ng-leave, 
 
.message.ng-leave { 
 
    opacity: 1; 
 
    top: 0; 
 
} 
 

 
.error-messages.ng-leave-active, 
 
.message.ng-leave-active { 
 
    opacity: 0; 
 
    top: -10px; 
 
}
<!DOCTYPE html> 
 
<html> 
 

 
    <head> 
 
    <!-- jQuery --> 
 
    <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script> 
 
    
 
    <!-- Twitter bootstrap --> 
 
    <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.css" rel="stylesheet"> 
 
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> 
 
    
 
    <!-- apiCheck is used by formly to validate its api --> 
 
    <script src="//npmcdn.com/[email protected]/dist/api-check.js"></script> 
 
    <!-- This is the latest version of angular (at the time this template was created) --> 
 
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.js"></script> 
 

 
    <!-- This is the latest version of formly core. --> 
 
    <script src="//npmcdn.com/[email protected]/dist/formly.js"></script> 
 
    <!-- This is the latest version of formly bootstrap templates --> 
 
    <script src="//npmcdn.com/[email protected]/dist/angular-formly-templates-bootstrap.js"></script> 
 

 
     <script src="https://rawgit.com/angular/bower-angular-messages/v1.4.4/angular-messages.js"></script> 
 
    <script src="https://rawgit.com/angular/bower-angular-animate/v1.4.4/angular-animate.js"></script> 
 
    
 
    <!-- Moment --> 
 
    <script src="https://cdn.rawgit.com/moment/moment/develop/min/moment-with-locales.min.js"></script> 
 
    
 
    <!-- Datetime picker --> 
 
    <script type="text/javascript" src="https://cdn.rawgit.com/dalelotts/angular-bootstrap-datetimepicker/master/src/js/datetimepicker.js"></script> 
 
    <script type="text/javascript" src="https://cdn.rawgit.com/dalelotts/angular-bootstrap-datetimepicker/master/src/js/datetimepicker.templates.js"></script> 
 
    <link href="https://cdn.rawgit.com/dalelotts/angular-bootstrap-datetimepicker/master/src/css/datetimepicker.css" rel="stylesheet"> 
 
    <script type="text/javascript" src="https://cdn.rawgit.com/dalelotts/angular-date-time-input/master/src/dateTimeInput.js"></script> 
 
    
 
    
 
    <title>Angular Formly Example</title> 
 
    </head> 
 

 
    <body ng-app="formlyExample" ng-controller="MainCtrl as vm"> 
 
    <div> 
 
     <form ng-submit="vm.onSubmit()" name="vm.form" novalidate> 
 
     <formly-form model="vm.model" fields="vm.fields" options="vm.options" form="vm.form"> 
 
      <button type="submit" class="btn btn-primary submit-button">Submit</button> 
 
      <button type="button" class="btn btn-default" ng-click="vm.options.resetModel()">Reset</button> 
 
     </formly-form> 
 
     </form> 
 
     <hr /> 
 
     <h2>Model</h2> 
 
     <pre>{{vm.model | json}}</pre> 
 
     <h2>Fields <small>(note, functions are not shown)</small></h2> 
 
     <pre>{{vm.originalFields | json}}</pre> 
 
     <h2>Form</h2> 
 
     <pre>{{vm.form | json}}</pre> 
 
    </div> 
 

 
    <!-- Put custom templates here --> 
 
    <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> 
 
    
 
    <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> 
 
     <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> 
 
    </script> 
 

 
    </body> 
 

 
</html>

+0

这大部分工作,但是,错误消息不会出现在提交日期选择器字段上。只有当它给予焦点,然后离开它。 –

0

你应该return您的validationformlyConfigProvider而不通过它作为templateOptions的值。返回

validation: { 
    show: true 
} 

,而不是

templateOptions: { 
    validation: { 
     show: true 
    } 
} 

formlyConfigProvider应该是这个样子:

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

Here是JSBin的工作代码。

+0

这并不能解决问题。 datepicker字段应该像文本字段一样工作。当您不输入任何值而退出该字段时,它应该变成红色,在选择它之前,它不应该是红色。并且该错误消息仍然不会出现在该字段下方。 –