@@ -454,6 +454,16 @@ define('aurelia-validation', ['exports', 'aurelia-binding', 'aurelia-templating'
454
454
* when it updates the model due to a change in the view.
455
455
*/
456
456
validateTrigger [ validateTrigger [ "changeOrBlur" ] = 3 ] = "changeOrBlur" ;
457
+ /**
458
+ * Validate the binding when the binding's target element fires a DOM "focusout" event.
459
+ * Unlike "blur", this event bubbles.
460
+ */
461
+ validateTrigger [ validateTrigger [ "focusout" ] = 4 ] = "focusout" ;
462
+ /**
463
+ * Validate the binding when the binding's target element fires a DOM "focusout" event or
464
+ * when it updates the model due to a change in the view.
465
+ */
466
+ validateTrigger [ validateTrigger [ "changeOrFocusout" ] = 6 ] = "changeOrFocusout" ;
457
467
} ) ( exports . validateTrigger || ( exports . validateTrigger = { } ) ) ;
458
468
459
469
/**
@@ -1041,6 +1051,7 @@ define('aurelia-validation', ['exports', 'aurelia-binding', 'aurelia-templating'
1041
1051
return ValidationController ;
1042
1052
} ( ) ) ;
1043
1053
1054
+ // tslint:disable:no-bitwise
1044
1055
/**
1045
1056
* Binding behavior. Indicates the bound property should be validated.
1046
1057
*/
@@ -1067,23 +1078,48 @@ define('aurelia-validation', ['exports', 'aurelia-binding', 'aurelia-templating'
1067
1078
controller . registerBinding ( binding , target , rules ) ;
1068
1079
binding . validationController = controller ;
1069
1080
var trigger = this . getValidateTrigger ( controller ) ;
1070
- // tslint:disable-next-line:no-bitwise
1071
- if ( trigger & exports . validateTrigger . change ) {
1081
+ var event = ( trigger & exports . validateTrigger . blur ) === exports . validateTrigger . blur ? 'blur'
1082
+ : ( trigger & exports . validateTrigger . focusout ) === exports . validateTrigger . focusout ? 'focusout'
1083
+ : null ;
1084
+ var hasChangeTrigger = ( trigger & exports . validateTrigger . change ) === exports . validateTrigger . change ;
1085
+ binding . isDirty = ! hasChangeTrigger ;
1086
+ // validatedOnce is used to control whether controller should validate upon user input
1087
+ //
1088
+ // always true when validation trigger doesn't include "blur" event (blur/focusout)
1089
+ // else it will be set to true after (a) the first user input & loss of focus or (b) validation
1090
+ binding . validatedOnce = hasChangeTrigger && event === null ;
1091
+ if ( hasChangeTrigger ) {
1072
1092
binding . vbbUpdateSource = binding . updateSource ;
1073
1093
// tslint:disable-next-line:only-arrow-functions
1074
1094
// tslint:disable-next-line:space-before-function-paren
1075
1095
binding . updateSource = function ( value ) {
1076
1096
this . vbbUpdateSource ( value ) ;
1077
- this . validationController . validateBinding ( this ) ;
1097
+ this . isDirty = true ;
1098
+ if ( this . validatedOnce ) {
1099
+ this . validationController . validateBinding ( this ) ;
1100
+ }
1078
1101
} ;
1079
1102
}
1080
- // tslint:disable-next-line:no-bitwise
1081
- if ( trigger & exports . validateTrigger . blur ) {
1082
- binding . validateBlurHandler = function ( ) {
1083
- _this . taskQueue . queueMicroTask ( function ( ) { return controller . validateBinding ( binding ) ; } ) ;
1103
+ if ( event !== null ) {
1104
+ binding . focusLossHandler = function ( ) {
1105
+ _this . taskQueue . queueMicroTask ( function ( ) {
1106
+ if ( binding . isDirty ) {
1107
+ controller . validateBinding ( binding ) ;
1108
+ binding . validatedOnce = true ;
1109
+ }
1110
+ } ) ;
1084
1111
} ;
1112
+ binding . validationTriggerEvent = event ;
1085
1113
binding . validateTarget = target ;
1086
- target . addEventListener ( 'blur' , binding . validateBlurHandler ) ;
1114
+ target . addEventListener ( event , binding . focusLossHandler ) ;
1115
+ if ( hasChangeTrigger ) {
1116
+ var propertyName_1 = getPropertyInfo ( binding . sourceExpression , binding . source ) . propertyName ;
1117
+ binding . validationSubscription = controller . subscribe ( function ( event ) {
1118
+ if ( ! binding . validatedOnce && event . type === 'validate' ) {
1119
+ binding . validatedOnce = event . errors . findIndex ( function ( e ) { return e . propertyName === propertyName_1 ; } ) > - 1 ;
1120
+ }
1121
+ } ) ;
1122
+ }
1087
1123
}
1088
1124
if ( trigger !== exports . validateTrigger . manual ) {
1089
1125
binding . standardUpdateTarget = binding . updateTarget ;
@@ -1105,13 +1141,19 @@ define('aurelia-validation', ['exports', 'aurelia-binding', 'aurelia-templating'
1105
1141
binding . updateTarget = binding . standardUpdateTarget ;
1106
1142
binding . standardUpdateTarget = null ;
1107
1143
}
1108
- if ( binding . validateBlurHandler ) {
1109
- binding . validateTarget . removeEventListener ( 'blur' , binding . validateBlurHandler ) ;
1110
- binding . validateBlurHandler = null ;
1144
+ if ( binding . focusLossHandler ) {
1145
+ binding . validateTarget . removeEventListener ( binding . validationTriggerEvent , binding . focusLossHandler ) ;
1146
+ binding . focusLossHandler = null ;
1111
1147
binding . validateTarget = null ;
1112
1148
}
1149
+ if ( binding . validationSubscription ) {
1150
+ binding . validationSubscription . dispose ( ) ;
1151
+ binding . validationSubscription = null ;
1152
+ }
1113
1153
binding . validationController . unregisterBinding ( binding ) ;
1114
1154
binding . validationController = null ;
1155
+ binding . isDirty = null ;
1156
+ binding . validatedOnce = null ;
1115
1157
} ;
1116
1158
return ValidateBindingBehaviorBase ;
1117
1159
} ( ) ) ;
@@ -1209,6 +1251,34 @@ define('aurelia-validation', ['exports', 'aurelia-binding', 'aurelia-templating'
1209
1251
aureliaBinding . bindingBehavior ( 'validateOnChangeOrBlur' )
1210
1252
] , ValidateOnChangeOrBlurBindingBehavior ) ;
1211
1253
return ValidateOnChangeOrBlurBindingBehavior ;
1254
+ } ( ValidateBindingBehaviorBase ) ) ;
1255
+ var ValidateOnFocusoutBindingBehavior = /** @class */ ( function ( _super ) {
1256
+ __extends ( ValidateOnFocusoutBindingBehavior , _super ) ;
1257
+ function ValidateOnFocusoutBindingBehavior ( ) {
1258
+ return _super !== null && _super . apply ( this , arguments ) || this ;
1259
+ }
1260
+ ValidateOnFocusoutBindingBehavior . prototype . getValidateTrigger = function ( ) {
1261
+ return exports . validateTrigger . focusout ;
1262
+ } ;
1263
+ ValidateOnFocusoutBindingBehavior . inject = [ aureliaTaskQueue . TaskQueue ] ;
1264
+ ValidateOnFocusoutBindingBehavior = __decorate ( [
1265
+ aureliaBinding . bindingBehavior ( 'validateOnFocusout' )
1266
+ ] , ValidateOnFocusoutBindingBehavior ) ;
1267
+ return ValidateOnFocusoutBindingBehavior ;
1268
+ } ( ValidateBindingBehaviorBase ) ) ;
1269
+ var ValidateOnChangeOrFocusoutBindingBehavior = /** @class */ ( function ( _super ) {
1270
+ __extends ( ValidateOnChangeOrFocusoutBindingBehavior , _super ) ;
1271
+ function ValidateOnChangeOrFocusoutBindingBehavior ( ) {
1272
+ return _super !== null && _super . apply ( this , arguments ) || this ;
1273
+ }
1274
+ ValidateOnChangeOrFocusoutBindingBehavior . prototype . getValidateTrigger = function ( ) {
1275
+ return exports . validateTrigger . changeOrFocusout ;
1276
+ } ;
1277
+ ValidateOnChangeOrFocusoutBindingBehavior . inject = [ aureliaTaskQueue . TaskQueue ] ;
1278
+ ValidateOnChangeOrFocusoutBindingBehavior = __decorate ( [
1279
+ aureliaBinding . bindingBehavior ( 'validateOnChangeOrFocusout' )
1280
+ ] , ValidateOnChangeOrFocusoutBindingBehavior ) ;
1281
+ return ValidateOnChangeOrFocusoutBindingBehavior ;
1212
1282
} ( ValidateBindingBehaviorBase ) ) ;
1213
1283
1214
1284
/**
@@ -1865,7 +1935,7 @@ define('aurelia-validation', ['exports', 'aurelia-binding', 'aurelia-templating'
1865
1935
config . apply ( frameworkConfig . container ) ;
1866
1936
// globalize the behaviors.
1867
1937
if ( frameworkConfig . globalResources ) {
1868
- frameworkConfig . globalResources ( ValidateBindingBehavior , ValidateManuallyBindingBehavior , ValidateOnBlurBindingBehavior , ValidateOnChangeBindingBehavior , ValidateOnChangeOrBlurBindingBehavior , ValidationErrorsCustomAttribute , ValidationRendererCustomAttribute ) ;
1938
+ frameworkConfig . globalResources ( ValidateBindingBehavior , ValidateManuallyBindingBehavior , ValidateOnBlurBindingBehavior , ValidateOnFocusoutBindingBehavior , ValidateOnChangeBindingBehavior , ValidateOnChangeOrBlurBindingBehavior , ValidateOnChangeOrFocusoutBindingBehavior , ValidationErrorsCustomAttribute , ValidationRendererCustomAttribute ) ;
1869
1939
}
1870
1940
}
1871
1941
@@ -1880,6 +1950,8 @@ define('aurelia-validation', ['exports', 'aurelia-binding', 'aurelia-templating'
1880
1950
exports . ValidateOnBlurBindingBehavior = ValidateOnBlurBindingBehavior ;
1881
1951
exports . ValidateOnChangeBindingBehavior = ValidateOnChangeBindingBehavior ;
1882
1952
exports . ValidateOnChangeOrBlurBindingBehavior = ValidateOnChangeOrBlurBindingBehavior ;
1953
+ exports . ValidateOnFocusoutBindingBehavior = ValidateOnFocusoutBindingBehavior ;
1954
+ exports . ValidateOnChangeOrFocusoutBindingBehavior = ValidateOnChangeOrFocusoutBindingBehavior ;
1883
1955
exports . ValidateEvent = ValidateEvent ;
1884
1956
exports . ValidateResult = ValidateResult ;
1885
1957
exports . ValidationController = ValidationController ;
0 commit comments