@@ -862,15 +862,29 @@ function validate(ctrl, validatorName, validity, value){
862862 return validity ? value : undefined ;
863863}
864864
865+ function testFlags ( validity , flags ) {
866+ var i , flag ;
867+ if ( flags ) {
868+ for ( i = 0 ; i < flags . length ; ++ i ) {
869+ flag = flags [ i ] ;
870+ if ( validity [ flag ] ) {
871+ return true ;
872+ }
873+ }
874+ }
875+ return false ;
876+ }
865877
866- function addNativeHtml5Validators ( ctrl , validatorName , element ) {
867- var validity = element . prop ( ' validity' ) ;
878+ // Pass validity so that behaviour can be mocked easier.
879+ function addNativeHtml5Validators ( ctrl , validatorName , badFlags , ignoreFlags , validity ) {
868880 if ( isObject ( validity ) ) {
881+ ctrl . $$hasNativeValidators = true ;
869882 var validator = function ( value ) {
870883 // Don't overwrite previous validation, don't consider valueMissing to apply (ng-required can
871884 // perform the required validation)
872- if ( ! ctrl . $error [ validatorName ] && ( validity . badInput || validity . customError ||
873- validity . typeMismatch ) && ! validity . valueMissing ) {
885+ if ( ! ctrl . $error [ validatorName ] &&
886+ ! testFlags ( validity , ignoreFlags ) &&
887+ testFlags ( validity , badFlags ) ) {
874888 ctrl . $setValidity ( validatorName , false ) ;
875889 return ;
876890 }
@@ -881,8 +895,9 @@ function addNativeHtml5Validators(ctrl, validatorName, element) {
881895}
882896
883897function textInputType ( scope , element , attr , ctrl , $sniffer , $browser ) {
884- var validity = element . prop ( 'validity' ) ;
898+ var validity = element . prop ( VALIDITY_STATE_PROPERTY ) ;
885899 var placeholder = element [ 0 ] . placeholder , noevent = { } ;
900+ ctrl . $$validityState = validity ;
886901
887902 // In composition mode, users are still inputing intermediate text buffer,
888903 // hold the listener until composition is done.
@@ -921,16 +936,16 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
921936 value = trim ( value ) ;
922937 }
923938
924- if ( ctrl . $viewValue !== value ||
925- // If the value is still empty/falsy, and there is no `required` error, run validators
926- // again. This enables HTML5 constraint validation errors to affect Angular validation
927- // even when the first character entered causes an error.
928- ( validity && value === '' && ! validity . valueMissing ) ) {
939+ // If a control is suffering from bad input, browsers discard its value, so it may be
940+ // necessary to revalidate even if the control's value is the same empty value twice in
941+ // a row.
942+ var revalidate = validity && ctrl . $$hasNativeValidators ;
943+ if ( ctrl . $viewValue !== value || ( value === '' && revalidate ) ) {
929944 if ( scope . $$phase ) {
930- ctrl . $setViewValue ( value , event ) ;
945+ ctrl . $setViewValue ( value , event , revalidate ) ;
931946 } else {
932947 scope . $apply ( function ( ) {
933- ctrl . $setViewValue ( value , event ) ;
948+ ctrl . $setViewValue ( value , event , revalidate ) ;
934949 } ) ;
935950 }
936951 }
@@ -1079,6 +1094,8 @@ function createDateInputType(type, regexp, parseDate, format) {
10791094 } ;
10801095}
10811096
1097+ var numberBadFlags = [ 'badInput' ] ;
1098+
10821099function numberInputType ( scope , element , attr , ctrl , $sniffer , $browser ) {
10831100 textInputType ( scope , element , attr , ctrl , $sniffer , $browser ) ;
10841101
@@ -1093,7 +1110,7 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
10931110 }
10941111 } ) ;
10951112
1096- addNativeHtml5Validators ( ctrl , 'number' , element ) ;
1113+ addNativeHtml5Validators ( ctrl , 'number' , numberBadFlags , null , ctrl . $$validityState ) ;
10971114
10981115 ctrl . $formatters . push ( function ( value ) {
10991116 return ctrl . $isEmpty ( value ) ? '' : '' + value ;
@@ -1773,11 +1790,11 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
17731790 * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
17741791 * usually handles calling this in response to input events.
17751792 */
1776- this . $commitViewValue = function ( ) {
1793+ this . $commitViewValue = function ( revalidate ) {
17771794 var viewValue = ctrl . $viewValue ;
17781795
17791796 $timeout . cancel ( pendingDebounce ) ;
1780- if ( ctrl . $$lastCommittedViewValue === viewValue ) {
1797+ if ( ! revalidate && ctrl . $$lastCommittedViewValue === viewValue ) {
17811798 return ;
17821799 }
17831800 ctrl . $$lastCommittedViewValue = viewValue ;
@@ -1842,14 +1859,14 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
18421859 * @param {string } value Value from the view.
18431860 * @param {string } trigger Event that triggered the update.
18441861 */
1845- this . $setViewValue = function ( value , trigger ) {
1862+ this . $setViewValue = function ( value , trigger , revalidate ) {
18461863 ctrl . $viewValue = value ;
18471864 if ( ! ctrl . $options || ctrl . $options . updateOnDefault ) {
1848- ctrl . $$debounceViewValueCommit ( trigger ) ;
1865+ ctrl . $$debounceViewValueCommit ( trigger , revalidate ) ;
18491866 }
18501867 } ;
18511868
1852- this . $$debounceViewValueCommit = function ( trigger ) {
1869+ this . $$debounceViewValueCommit = function ( trigger , revalidate ) {
18531870 var debounceDelay = 0 ,
18541871 options = ctrl . $options ,
18551872 debounce ;
@@ -1868,10 +1885,10 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
18681885 $timeout . cancel ( pendingDebounce ) ;
18691886 if ( debounceDelay ) {
18701887 pendingDebounce = $timeout ( function ( ) {
1871- ctrl . $commitViewValue ( ) ;
1888+ ctrl . $commitViewValue ( revalidate ) ;
18721889 } , debounceDelay ) ;
18731890 } else {
1874- ctrl . $commitViewValue ( ) ;
1891+ ctrl . $commitViewValue ( revalidate ) ;
18751892 }
18761893 } ;
18771894
0 commit comments