Many thanks to @NevilleS and @jbedard for collaborating with me on a solution to this!
Closes#9394Closes#9865
BREAKING CHANGE: previously, ngModel invoked getter/setters in the global context.
For example:
```js
<input ng-model="model.value" ng-model-options="{ getterSetter: true }">
```
would previously invoke `model.value()` in the global context.
Now, ngModel invokes `value` with `model` as the context.
It's unlikely that real apps relied on this behavior. If they did they can use `.bind` to explicilty
bind a getter/getter to the global context, or just reference globals normally without `this`.
Previously, setting the maxlength to a negative number, would make all
input values invalid (since their length should be less than maxlength,
which is impossible).
This commit changes the behaviour of maxlength/ngMaxlength, effectively
disabling the maxlength validation (always returning true) when maxlength
is set to a negative number. This is more inline to how the HTML5
`maxlength` attribute works (both in browsers and according to the spec:
http://dev.w3.org/html5/spec-preview/attributes-common-to-form-controls.html#attr-fe-maxlength).
Related to #9874Closes#9995
- extract existing functionality to public method: $setDirty
- add tests to corresponding changes
- refactor code to use extracted method
Closes#10038Closes#10049
Previously, $validate would execute the parsers to obtain a
modelValue for validation. This was necessary, because a validator
that is called outside of model / view update (e.g. from an observer)
otherwise might only an undefined modelValue, because a previous
view update has found a validation $error and set the model
to undefined (as is tradition in angular)
This is problematic as validators that are run immediately after
the ngModelController initializes would parse the modelValue
and replace the model, even though there had been no user input.
The solution is to go back to an older design: the ngModelController
will now internally record the $$rawModelValue. This means a model
or view update will store the set / parsed modelValue regardless
of validity, that is, it will never set it to undefined because of
validation errors.
When $validate is called, the $$rawModelValue will passed to the
validators. If the validity has changed, the usual behavior is kept:
if it became invalid, set the model to undefined, if valid,
restore the last available modelValue - the $$rawModelValue.
Additionally, $validate will only update the model when the validity
changed. This is to prevent setting initially invalid models other
than undefined to undefined (see #9063)
Fixes: #9063Fixes: #9959Fixes: #9996Fixes: #10025Closes: #9890Closes: #9913Closes: #9997Closes: #10048
Check if the attribute is undefined before manually applying the function because if not an
undefined property is added to the scope of the form controller when the input control does not
have a name.
Closes#9707Closes#9720
When ngAnimate is used, it will defer changes to classes until postDigest. Previously,
AngularJS (when ngAnimate is not loaded) would always immediately perform these DOM
operations.
Now, even when the ngAnimate module is not used, if $rootScope is in the midst of a
digest, class manipulation is deferred. This helps reduce jank in browsers such as
IE11.
BREAKING CHANGE:
The $animate class API will always defer changes until the end of the next digest. This allows ngAnimate
to coalesce class changes which occur over a short period of time into 1 or 2 DOM writes, rather than
many. This prevents jank in browsers such as IE, and is generally a good thing.
If you're finding that your classes are not being immediately applied, be sure to invoke $digest().
Closes#8234Closes#9263
Previously, builtin parsers/formatters for e.g. `input[date]`
or `input[number]` were added in the post linking phase to `ngModelController`,
which in most cases was after a custom formatter/parser was registered.
This commit registers builtin parsers/formatters already
in the pre linking phase. With that builtin
parsers run first, and builtin formatters run last.
Closes#9218Closes#9358
Similar to `input[number]` Angular will throw if the model value
for a `input[date]` is not a `Date` object.
For `Invalid Date`s (dates whose `getTime()` is `NaN`) `input[date]`
will render an empty string.
Closes#8949Closes#9375
Adds an additional test verifying that a number which is not required will validate successfully
when ngModelCtrl.$validate() is called. Before 92f05e5 landed, this would have failed because of
a parse error.
Closes#9193
Previously, if a viewValue had not yet been set on the element, it could incorrectly produce a
parse error.
This change prevents the parsers from running if a view value has not yet been committed.
Closes#9106Closes#9260
Interpolates the form and form control attribute name, so that dynamic form controls (such as those
rendered in an ngRepeat) will always have their expected interpolated name.
The control will be present in its parent form controller with the interpolated property name, and
this name can change when the interpolated value changes.
Closes#4791Closes#1404
Previously, if you bound a `Date` object to `<input type="time">`,
whenever you changed the time, the day, month, and year fields of
the new resulting bound `Date` object would be reset. Now fields
not modified by bound time input elements are copied to the new
resulting object.
Same for input types of `month`, `week`, etc.
Closes#6666
Calling `ctrl.$setValidity()` with a an error key that
does not belong to a validator in `ctrl.$validator` should
not result in setting the model to `undefined` on the next
input change. This bug was introduced in 1.3.0-beta.12.
Closes#8357Fixes#8080
This option allows to write invalid values to the model instead of having them become undefined.
Use this together with calling `ctrl.$setValidity` directly for displaying errors
from serverside validation.
Closes#8290Closes#8313
If the view value changed in the first digest and there are async validators,
the view value was never applied to the model after the validators were
resolved. Only important for tests.
- define `ngModelGet` and `ngModelSet` to already use
the getter/setter semantics, so the rest of the code does
not need to care about it.
- remove `ctrl.$$invalidModelValue` to simplify the internal logic
The previous logic for async validation in
`ngModelController` and `formController` was not maintainable:
- control logic is in multiple parts, e.g. `ctrl.$setValidity`
waits for end of promises and continuous the control flow
for async validation
- logic for updating the flags `ctrl.$error`, `ctrl.$pending`, `ctrl.$valid`
is super complicated, especially in `formController`
This refactoring makes the following changes:
- simplify async validation: centralize control logic
into one method in `ngModelController`:
* remove counters `invalidCount` and `pendingCount`
* use a flag `currentValidationRunId` to separate
async validator runs from each other
* use `$q.all` to determine when all async validators are done
- centralize way how `ctrl.$modelValue` and `ctrl.$invalidModelValue`
is updated
- simplify `ngModelController/formCtrl.$setValidity` and merge
`$$setPending/$$clearControlValidity/$$clearValidity/$$clearPending`
into one method, that is used by `ngModelController` AND
`formController`
* remove diff calculation, always calculate the correct state anew,
only cache the css classes that have been set to not
trigger too many css animations.
* remove fields from `ctrl.$error` that are valid and add private `ctrl.$$success`:
allows to correctly separate states for valid, invalid, skipped and pending,
especially transitively across parent forms.
- fix bug in `ngModelController`:
* only read out `input.validity.badInput`, but not
`input.validity.typeMismatch`,
to determine parser error: We still want our `email`
validator to run event when the model is validated.
- fix bugs in tests that were found as the logic is now consistent between
`ngModelController` and `formController`
BREAKING CHANGE:
- `ctrl.$error` does no more contain entries for validators that were
successful.
- `ctrl.$setValidity` now differentiates between `true`, `false`,
`undefined` and `null`, instead of previously only truthy vs falsy.
Closes#8941
Note that this change means that anyone watching `$viewValue` will have to
wait for a new digest before they are aware that it has been updated.
Closes#8814Closes#8850Closes#8911
Due to the nature of how date objects are rendered when JSON.stringify
is called, the resulting string contains two sets of quotes surrounding
it. This commit fixes that issue.
Closes#6755
With this fix ngModel will treat ngMin as a min error and ngMax as a max error.
This also means that when either of these two values is changed then ngModel will
revaliate itself.
As of this fix if the max or min value is changed via scope or by another ngModel
then it will trigger the model containing the min/max attributes to revalidate itself.
Closes#2404
NgModel will format all scope-based values to string when setting the viewValue for
the associated input element. The formatting, however, only applies to input elements
that contain a text, email, url or blank input type. In the event of a null or undefined
scope or model value, the viewValue will be set to null or undefined instead of being
converted to an empty string.
Use the viewValue rather than modelValue when validating. The viewValue should always be a string, and
should reflect what the user has entered, or the formatted model value.
BREAKING CHANGE:
Always uses the viewValue when validating minlength and maxlength.
Closes#7967Closes#8811
This commit introduces a 2nd validation queue called `$asyncValidators`. Each time a value
is processed by the validation pipeline, if all synchronous `$validators` succeed, the value
is then passed through the `$asyncValidators` validation queue. These validators should return
a promise. Rejection of a validation promise indicates a failed validation.
With this commit, ngModel will now handle parsing first and then validation
afterwards once the parsing is successful. If any parser along the way returns
`undefined` then ngModel will break the chain of parsing and register a
a parser error represented by the type of input that is being collected
(e.g. number, date, datetime, url, etc...). If a parser fails for a standard
text input field then an error of `parse` will be placed on `model.$error`.
BREAKING CHANGE
Any parser code from before that returned an `undefined` value
(or nothing at all) will now cause a parser failure. When this occurs
none of the validators present in `$validators` will run until the parser
error is gone.
The HTML5 spec allows to use seconds for `input[time]` and `input[datetime-local]`,
even though they are not displayed by all browsers.
Related to #8447.
Angular used to always use the browser timezone when parsing
`input[date]`, `input[time]`, … The timezone can now be changed
to `UTC` via `ngModelOptions`.
Closes#8447.
BREAKING CHANGE:
According to the HTML5 spec `input[time]` should create dates
based on the year 1970 (used to be based on the year 1900).
Related to #8447.
Do not trim input[type=password] values
BREAKING CHANGE:
Previously, input[type=password] would trim values by default, and would require an explicit ng-trim="false"
to disable the trimming behaviour. After this CL, ng-trim no longer effects input[type=password], and will
never trim the password value.
Closes#8250Closes#8230
@kevinjamesus86 noticed that the input control would trigger a $digest
cycle every time it was blurred, adcc5a00bf (commitcomment-7129512).
After the control is in a $touched state, other $digest cycles are
unnecesary.
Closes#8450