6

Próbuję użyć Angular Datetime Picker jako typu wejściowego Angular Formly. Mam działanie tak, że mogę edytować i ustawić wartość, która jest poprawnie dodana do modelu powiązanego.Komunikat o błędzie sprawdzania poprawności nie jest wyświetlany dla niestandardowego obiektu DatePicker Angular Formly field

Jednak nie mogę wyświetlić komunikatów o błędach sprawdzania poprawności, aby były wyświetlane tak jak w zwykłych polach wprowadzania.

JS Bin with what I've got so far. Jak widać czerwony kolor nie pojawia się po wyjściu z pola, tylko wtedy, gdy próbujesz przesłać. I komunikat o błędzie nigdy się nie wyświetla.

Formly Config:

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

Pola

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

Szablony

<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

można wyjaśnić tę kwestię nieco więcej, ponieważ jeśli kliknę pierwsze pole tekstowe z etykietą "Text" i kliknij następny tekstowe "więcej tekstu" pierwsze pole tekstowe pokazuje błąd sprawdzania poprawności w chrome. Ale to samo dzieje się z "Date". Czy to jest problem? – Aruna

+0

Tak, to jest problem –

+0

Czy możesz sprawdzić moją odpowiedź poniżej? – Aruna

Odpowiedz

3

Po zbadaniu tego głębiej, widzę, że kontrolka, której użyłeś, jest niezupełnie kompatybilna z Angular Formly.

Wynika to z tego powodu, że jest to nadpisując ngModelController.$render() metoda angularjs i stąd nie ustalającego wartość dla $touched jak innych formantów wejściowych.

Innym powodem w kodzie to, że konfiguracja i szablon error-messages.html traktują sterowania własnego jako pojedynczego elementu z fc.$touched , fc.$error i fc.$viewValue natomiast DatePicker renderuje jak grupa pierwiastków (array).

Aby pozbyć się wszystkich tych problemów, można mieć dyrektywę niestandardowy, aby ustawić $touched jak poniżej,

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

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

I dodać fc[0].$touched w poniższej konfiguracji dbać o tablicę pól,

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

a także dodać poniżej sekcji w error-messages.html dbać o tablicę pól

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

Te zmiany będą rozwiązać problem.

Jak widać trochę kwestii projektowania z komunikatem o błędzie, który jest wyświetlany dalej,

Można zmienić custom-template.html jak poniżej usuwając otoki 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> 

I zaktualizowałeś swoje JSBin z tymi zmianami.

Snippet:

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

Działa to w większości przypadków, ale komunikat o błędzie nie pojawia się w polu datepicker na przesyłanie. Tylko wtedy, gdy skupia się na nim, a następnie go opuszcza. –

0

Powinieneś return swój validation w formlyConfigProvider bez przekazywania go jako wartość dla templateOptions. Powrót

validation: { 
    show: true 
} 

zamiast

templateOptions: { 
    validation: { 
     show: true 
    } 
} 

Twój formlyConfigProvider powinien wyglądać mniej więcej tak:

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

Here jest JSBin kodu roboczego.

+0

To nie rozwiązuje problemu. Pole datepicker powinno działać tak jak pole tekstowe. Powinien zmienić kolor na czerwony, gdy wyjdziesz z pola bez wprowadzania żadnej wartości, przed jego wybraniem nie powinno być czerwone. Komunikat o błędzie wciąż nie pojawia się pod polem. –