-
Notifications
You must be signed in to change notification settings - Fork 27.5k
Ng model options extend #15389
Ng model options extend #15389
Conversation
…stor ngModelOptions This reverts commit 87a2ff7
@@ -0,0 +1,378 @@ | |||
'use strict'; | |||
|
|||
/* global $DefaultModelOptionsProvider: true */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't it be exported
, rather than global
?
* @name $defaultModelOptionsProvider | ||
* @description | ||
* | ||
* Here, you can change the default settings from which {@link ngModelOptions} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This service is not only used for ngModelOptions
, but for ngModel
as well.
It would be good to clarify that, so people know what they are modifying 😃
* @description | ||
* This directive allows you to modify the behaviour of ngModel and input directives within your | ||
* application. You can specify an ngModelOptions directive on any element and the settings affect | ||
* the ngModel and input directives on all descendent elements. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
on all descendent elements
Not exactly 😃 It is worth clarifying that it doesn't (necessarily) affect elements past its closest ngModelOptions
descendant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
* @name ngModelOptions | ||
* | ||
* @description | ||
* This directive allows you to modify the behaviour of ngModel and input directives within your |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why input directives? It only affects ngModel
afaict.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK OK
* ``` | ||
* | ||
* Notice that the `debounce` setting now inherits The value from the outer `<div>` element. | ||
* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is a good place to add a warning alert for library authors, mentioning that "*"
should be used with caution, because of the possibility of new options being introduced in future versions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
if (key === '*') { | ||
inheritAll = true; | ||
} else { | ||
options[key] = isDefined(parentOptions[key]) ? parentOptions[key] : defaultOptions[key]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we always extended options
with defaultOptions
(see comment above), then here too we could simply use: options[key] = parentOptions[key]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
* specified. | ||
*/ | ||
$get: function() { | ||
return new ModelOptions(defaultOptions, defaultOptions); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would simplify the ModelOptions
implementation if defaultOptions
was a top-level var, so we didn't have to pass it around from parent to child.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you suggesting we create a global variable!?!
I agree.
expect(inputOptions.getOption('updateOnDefault')).toEqual($defaultModelOptions.getOption('updateOnDefault')); | ||
expect(inputOptions.getOption('debounce')).toEqual($defaultModelOptions.getOption('debounce')); | ||
expect(inputOptions.getOption('getterSetter')).toEqual($defaultModelOptions.getOption('getterSetter')); | ||
expect(inputOptions.getOption('allowInvalid')).toEqual($defaultModelOptions.getOption('allowInvalid')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about timezone
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right
/* globals generateInputCompilerHelper: false */ | ||
describe('ngModelOptions', function() { | ||
|
||
describe('$defaultModelOptions', function() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add a test showing the default options are overwritable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually they are not :-P
I just need to fix the docs - see my other comment
'</form>')($rootScope); | ||
$rootScope.$digest(); | ||
|
||
var inputElm = doc.find('input').eq(0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Isn't .eq(0)
redundant?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that find
does not return the object that we want (i.e. some collection thing) so we must convert it to get an instance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see that it is not used elsewhere... I'll try removing it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
eq
returns a new collection with a subset of the items (typically just one):
eq: function(index) {
return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
}
Here there is only one input
element anyway, so it is redundant.
898ba1a
to
24be474
Compare
…ModelOptions` Previously, you had to apply the complete set of `ngModelOptions` at every point where you might want to modify just one or two settings. This change allows more general settings to be applied nearer to the top of the DOM and then for more specific settings to override those general settings further down in the DOM. To prevent unwanted inheritance you must opt-in on a case by case basis: * To inherit as single property you simply provide the special value `"$inherit"`. * To inherit all properties not specified locally then include a property `"*": "$inherit"`. Closes angular#10922 Closes angular#15389 BREAKING CHANGE: The programmatic API for `ngModelOptions` has changed. You must now read options via the `ngModelController.getOption(name)` method, rather than accessing the option directly as a property of the `ngModelContoller.$options` object. This does not affect the usage in templates and only affects custom directives that might have been reading options for their own purposes.
…ModelOptions` Previously, you had to apply the complete set of `ngModelOptions` at every point where you might want to modify just one or two settings. This change allows more general settings to be applied nearer to the top of the DOM and then for more specific settings to override those general settings further down in the DOM. To prevent unwanted inheritance you must opt-in on a case by case basis: * To inherit as single property you simply provide the special value `"$inherit"`. * To inherit all properties not specified locally then include a property `"*": "$inherit"`. Closes angular#10922 Closes angular#15389 BREAKING CHANGE: The programmatic API for `ngModelOptions` has changed. You must now read options via the `ngModelController.getOption(name)` method, rather than accessing the option directly as a property of the `ngModelContoller.$options` object. This does not affect the usage in templates and only affects custom directives that might have been reading options for their own purposes.
24be474
to
82f32ec
Compare
this.$$options[key] = this.$$parentOptions[key]; | ||
} | ||
} | ||
}, this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Who needs arrow functions 😄
* { allowInvalid: true, updateOn: 'default', debounce: 200 } | ||
* ``` | ||
* | ||
* Notice that the `debounce` setting now inherits The value from the outer `<div>` element. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The value --> the value
$$processOptions: function() { | ||
var options = this.$$options; | ||
// updateOn and updateOnDefault | ||
if (isDefined(options.updateOn) && options.updateOn.trim()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to handle this differently now. For example:
<div ng-model-options="{updateOn: 'default blur'}">
<div ng-model-options="{'*': '$inherit'}"></div>
</div>
- The outer
ModelOptions
will haveupdateOn: 'blur', updateOnDefault: true
. - During the
$$extendParent()
phase, the values will be copied onto the innerModelOptions
. - Then, during the
$$processOptions
phase,updateOn: 'blur'
will be treated as the initial value andupdateOnDefault
will be reset tofalse
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right! Now this is a good review point. I knew there must be something that needed fixing. I'll take a look right now.
…ModelOptions` Previously, you had to apply the complete set of `ngModelOptions` at every point where you might want to modify just one or two settings. This change allows more general settings to be applied nearer to the top of the DOM and then for more specific settings to override those general settings further down in the DOM. To prevent unwanted inheritance you must opt-in on a case by case basis: * To inherit as single property you simply provide the special value `"$inherit"`. * To inherit all properties not specified locally then include a property `"*": "$inherit"`. Closes angular#10922 Closes angular#15389 BREAKING CHANGE: The programmatic API for `ngModelOptions` has changed. You must now read options via the `ngModelController.getOption(name)` method, rather than accessing the option directly as a property of the `ngModelContoller.$options` object. This does not affect the usage in templates and only affects custom directives that might have been reading options for their own purposes.
@@ -244,7 +245,7 @@ function NgModelController($scope, $exceptionHandler, $attr, $element, $parse, $ | |||
this.$pending = undefined; // keep pending keys here | |||
this.$name = $interpolate($attr.name || '', false)($scope); | |||
this.$$parentForm = nullFormCtrl; | |||
this.$options = $modelOptions; | |||
this.$options = $defaultModelOptions; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Typically, internal/private variables are not prefixed. The $
prefix is supposed to indicate public stuff.
} else { | ||
options[key] = this.$$options[key]; | ||
} | ||
} else if (key === 'updateOn') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are still some issues with this implementation:
- If
{updateOn: '$inherit'}
it will inherit theupdateOn
, but not theupdateOnDefault
(which might be inherited by$defaultModelOptions
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great. I am really working your reviewing skills today!
…ModelOptions` Previously, you had to apply the complete set of `ngModelOptions` at every point where you might want to modify just one or two settings. This change allows more general settings to be applied nearer to the top of the DOM and then for more specific settings to override those general settings further down in the DOM. To prevent unwanted inheritance you must opt-in on a case by case basis: * To inherit as single property you simply provide the special value `"$inherit"`. * To inherit all properties not specified locally then include a property `"*": "$inherit"`. Closes angular#10922 Closes angular#15389 BREAKING CHANGE: The programmatic API for `ngModelOptions` has changed. You must now read options via the `ngModelController.getOption(name)` method, rather than accessing the option directly as a property of the `ngModelContoller.$options` object. This does not affect the usage in templates and only affects custom directives that might have been reading options for their own purposes.
d44e5b9
to
e0b5bd2
Compare
…ModelOptions` Previously, you had to apply the complete set of `ngModelOptions` at every point where you might want to modify just one or two settings. This change allows more general settings to be applied nearer to the top of the DOM and then for more specific settings to override those general settings further down in the DOM. To prevent unwanted inheritance you must opt-in on a case by case basis: * To inherit as single property you simply provide the special value `"$inherit"`. * To inherit all properties not specified locally then include a property `"*": "$inherit"`. Closes angular#10922 Closes angular#15389 BREAKING CHANGE: The programmatic API for `ngModelOptions` has changed. You must now read options via the `ngModelController.getOption(name)` method, rather than accessing the option directly as a property of the `ngModelContoller.$options` object. This does not affect the usage in templates and only affects custom directives that might have been reading options for their own purposes.
OK, trying again. See if you can find another failing scenario @gkalpak |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fourth time's a charm 😃
LGTM
it('should `updateOnDefault` as well if we have `updateOn: "$inherit"`', function() { | ||
var container = $compile( | ||
'<div ng-model-options="{updateOn: \'keyup\'}">' + | ||
'<input ng-model-options="{\'updateOn\': \'$inherit\'}">' + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary quotes around updateOn
(here and below).
…ModelOptions` Previously, you had to apply the complete set of `ngModelOptions` at every point where you might want to modify just one or two settings. This change allows more general settings to be applied nearer to the top of the DOM and then for more specific settings to override those general settings further down in the DOM. To prevent unwanted inheritance you must opt-in on a case by case basis: * To inherit as single property you simply provide the special value `"$inherit"`. * To inherit all properties not specified locally then include a property `"*": "$inherit"`. Closes angular#10922 Closes angular#15389 BREAKING CHANGE: The programmatic API for `ngModelOptions` has changed. You must now read options via the `ngModelController.getOption(name)` method, rather than accessing the option directly as a property of the `ngModelContoller.$options` object. This does not affect the usage in templates and only affects custom directives that might have been reading options for their own purposes.
e0b5bd2
to
1ba33e7
Compare
…ModelOptions` Previously, you had to apply a complete set of `ngModelOptions` at many places in the DOM where you might want to modify just one or two settings. This change allows more general settings to be applied nearer to the root of the DOM and then for more specific settings to inherit those general settings further down in the DOM. To prevent unwanted inheritance you must opt-in on a case by case basis: * To inherit as single property you simply provide the special value `"$inherit"`. * To inherit all properties not specified locally then include a property `"*": "$inherit"`. Closes angular#10922 Closes angular#15389 BREAKING CHANGE: The programmatic API for `ngModelOptions` has changed. You must now read options via the `ngModelController.$options.getOption(name)` method, rather than accessing the option directly as a property of the `ngModelContoller.$options` object. This does not affect the usage in templates and only affects custom directives that might have been reading options for their own purposes. One benefit of these changes, though, is that the `ngModelControler.$options` property is now guaranteed to be defined so there is no need to check before accessing. So, previously: ``` var myOption = ngModelController.$options && ngModelController.$options['my-option']; ``` and now: ``` var myOption = ngModelController.$options.getOption('my-option'); ```
What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
feat
What is the current behavior? (You can also link to an open issue here)
ngModelOptions
cannot be inherited from ancestorngModelOptions
What is the new behavior (if this is a feature change)?
ngModelOptions
can inherit if specified via$inherit
valuesDoes this PR introduce a breaking change?
A small internal-ish change to the programmatic API for reading options.
Please check if the PR fulfills these requirements
Other information: