-
Notifications
You must be signed in to change notification settings - Fork 27.4k
perf(input): prevent multiple validations on compilation #16760
Changes from all commits
c1bef69
0d826b8
f2e1bd6
b4d7b61
b069361
57b45e1
4d82020
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1497,7 +1497,7 @@ function createDateParser(regexp, mapping) { | |
} | ||
|
||
function createDateInputType(type, regexp, parseDate, format) { | ||
return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) { | ||
return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) { | ||
badInputChecker(scope, element, attr, ctrl, type); | ||
baseInputType(scope, element, attr, ctrl, $sniffer, $browser); | ||
|
||
|
@@ -1540,24 +1540,34 @@ function createDateInputType(type, regexp, parseDate, format) { | |
}); | ||
|
||
if (isDefined(attr.min) || attr.ngMin) { | ||
var minVal; | ||
var minVal = attr.min || $parse(attr.ngMin)(scope); | ||
var parsedMinVal = parseObservedDateValue(minVal); | ||
|
||
ctrl.$validators.min = function(value) { | ||
return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal; | ||
return !isValidDate(value) || isUndefined(parsedMinVal) || parseDate(value) >= parsedMinVal; | ||
}; | ||
attr.$observe('min', function(val) { | ||
minVal = parseObservedDateValue(val); | ||
ctrl.$validate(); | ||
if (val !== minVal) { | ||
parsedMinVal = parseObservedDateValue(val); | ||
minVal = val; | ||
ctrl.$validate(); | ||
} | ||
}); | ||
} | ||
|
||
if (isDefined(attr.max) || attr.ngMax) { | ||
var maxVal; | ||
var maxVal = attr.max || $parse(attr.ngMax)(scope); | ||
var parsedMaxVal = parseObservedDateValue(maxVal); | ||
|
||
ctrl.$validators.max = function(value) { | ||
return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal; | ||
return !isValidDate(value) || isUndefined(parsedMaxVal) || parseDate(value) <= parsedMaxVal; | ||
}; | ||
attr.$observe('max', function(val) { | ||
maxVal = parseObservedDateValue(val); | ||
ctrl.$validate(); | ||
if (val !== maxVal) { | ||
parsedMaxVal = parseObservedDateValue(val); | ||
maxVal = val; | ||
ctrl.$validate(); | ||
} | ||
}); | ||
} | ||
|
||
|
@@ -1709,50 +1719,68 @@ function isValidForStep(viewValue, stepBase, step) { | |
return (value - stepBase) % step === 0; | ||
} | ||
|
||
function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) { | ||
function numberInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) { | ||
badInputChecker(scope, element, attr, ctrl, 'number'); | ||
numberFormatterParser(ctrl); | ||
baseInputType(scope, element, attr, ctrl, $sniffer, $browser); | ||
|
||
var minVal; | ||
var maxVal; | ||
var parsedMinVal; | ||
|
||
if (isDefined(attr.min) || attr.ngMin) { | ||
var minVal = attr.min || $parse(attr.ngMin)(scope); | ||
parsedMinVal = parseNumberAttrVal(minVal); | ||
|
||
ctrl.$validators.min = function(modelValue, viewValue) { | ||
return ctrl.$isEmpty(viewValue) || isUndefined(minVal) || viewValue >= minVal; | ||
return ctrl.$isEmpty(viewValue) || isUndefined(parsedMinVal) || viewValue >= parsedMinVal; | ||
}; | ||
|
||
attr.$observe('min', function(val) { | ||
minVal = parseNumberAttrVal(val); | ||
// TODO(matsko): implement validateLater to reduce number of validations | ||
ctrl.$validate(); | ||
if (val !== minVal) { | ||
parsedMinVal = parseNumberAttrVal(val); | ||
minVal = val; | ||
// TODO(matsko): implement validateLater to reduce number of validations | ||
ctrl.$validate(); | ||
} | ||
}); | ||
} | ||
|
||
if (isDefined(attr.max) || attr.ngMax) { | ||
var maxVal = attr.max || $parse(attr.ngMax)(scope); | ||
var parsedMaxVal = parseNumberAttrVal(maxVal); | ||
|
||
ctrl.$validators.max = function(modelValue, viewValue) { | ||
return ctrl.$isEmpty(viewValue) || isUndefined(maxVal) || viewValue <= maxVal; | ||
return ctrl.$isEmpty(viewValue) || isUndefined(parsedMaxVal) || viewValue <= parsedMaxVal; | ||
}; | ||
|
||
attr.$observe('max', function(val) { | ||
maxVal = parseNumberAttrVal(val); | ||
// TODO(matsko): implement validateLater to reduce number of validations | ||
ctrl.$validate(); | ||
if (val !== maxVal) { | ||
parsedMaxVal = parseNumberAttrVal(val); | ||
maxVal = val; | ||
// TODO(matsko): implement validateLater to reduce number of validations | ||
ctrl.$validate(); | ||
} | ||
}); | ||
} | ||
|
||
if (isDefined(attr.step) || attr.ngStep) { | ||
var stepVal; | ||
var stepVal = attr.step || $parse(attr.ngStep)(scope); | ||
var parsedStepVal = parseNumberAttrVal(stepVal); | ||
|
||
ctrl.$validators.step = function(modelValue, viewValue) { | ||
return ctrl.$isEmpty(viewValue) || isUndefined(stepVal) || | ||
isValidForStep(viewValue, minVal || 0, stepVal); | ||
return ctrl.$isEmpty(viewValue) || isUndefined(parsedStepVal) || | ||
isValidForStep(viewValue, parsedMinVal || 0, parsedStepVal); | ||
}; | ||
|
||
attr.$observe('step', function(val) { | ||
stepVal = parseNumberAttrVal(val); | ||
// TODO(matsko): implement validateLater to reduce number of validations | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move to before |
||
ctrl.$validate(); | ||
if (val !== stepVal) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suspect that we should be also checking for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You mean ensure that the minVal has changed? That doesn't work, because we need to run this also if the minVal is the same. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, I don't meant ensure that I am pretty sure that indeed Matias meant avoid multiple calls to We could use a similar technique in the validators as this PR uses in observers:
Actually, I believe it will happen all the time 😛 If we can keep it clean and BC-free (with reasonable confidence 😁), then it is definitely worth landing. But I wouldn't go to great extends, complicating the internal logic too much etc., just to save a few runs 😁 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Right, but if the minVal has changed, it will call $validate, which will also run the step validator. Since we are not fixing multiple runs after initialization , we don't have to deal with this,
It will only call $validate multiple times if observed values change, which I think is much rarer than initialization, which happens always. ;) |
||
parsedStepVal = parseNumberAttrVal(val); | ||
stepVal = val; | ||
ctrl.$validate(); | ||
} | ||
|
||
}); | ||
|
||
} | ||
} | ||
|
||
|
@@ -1782,6 +1810,8 @@ function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) { | |
originalRender; | ||
|
||
if (hasMinAttr) { | ||
minVal = parseNumberAttrVal(attr.min); | ||
|
||
ctrl.$validators.min = supportsRange ? | ||
// Since all browsers set the input to a valid value, we don't need to check validity | ||
function noopMinValidator() { return true; } : | ||
|
@@ -1794,6 +1824,8 @@ function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) { | |
} | ||
|
||
if (hasMaxAttr) { | ||
maxVal = parseNumberAttrVal(attr.max); | ||
|
||
ctrl.$validators.max = supportsRange ? | ||
// Since all browsers set the input to a valid value, we don't need to check validity | ||
function noopMaxValidator() { return true; } : | ||
|
@@ -1806,6 +1838,8 @@ function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) { | |
} | ||
|
||
if (hasStepAttr) { | ||
stepVal = parseNumberAttrVal(attr.step); | ||
|
||
ctrl.$validators.step = supportsRange ? | ||
function nativeStepValidator() { | ||
// Currently, only FF implements the spec on step change correctly (i.e. adjusting the | ||
|
@@ -1827,7 +1861,13 @@ function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) { | |
// attribute value when the input is first rendered, so that the browser can adjust the | ||
// input value based on the min/max value | ||
element.attr(htmlAttrName, attr[htmlAttrName]); | ||
attr.$observe(htmlAttrName, changeFn); | ||
var oldVal = attr[htmlAttrName]; | ||
attr.$observe(htmlAttrName, function wrappedObserver(val) { | ||
if (val !== oldVal) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above: I believe |
||
oldVal = val; | ||
changeFn(val); | ||
} | ||
}); | ||
} | ||
|
||
function minChange(val) { | ||
|
@@ -1881,11 +1921,11 @@ function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) { | |
} | ||
|
||
// Some browsers don't adjust the input value correctly, but set the stepMismatch error | ||
if (supportsRange && ctrl.$viewValue !== element.val()) { | ||
ctrl.$setViewValue(element.val()); | ||
} else { | ||
if (!supportsRange) { | ||
// TODO(matsko): implement validateLater to reduce number of validations | ||
ctrl.$validate(); | ||
} else if (ctrl.$viewValue !== element.val()) { | ||
ctrl.$setViewValue(element.val()); | ||
} | ||
} | ||
} | ||
|
Uh oh!
There was an error while loading. Please reload this page.