-
Notifications
You must be signed in to change notification settings - Fork 27.5k
feat(input): Allow custom events and timeouts to trigger model updates #2129
Conversation
BTW, I already signed the CLA |
One note about the missing support for timeouts in radio buttons: Radios are, in fact, different input controls that are tied to the same model. The timeout timer gets reset & launched on every input content change, but it can't know about the other radio controls' timers. This has an unpleasant side effect: imagine you have 3 radio buttons bound to the same model with a timeout of 1 second. If you click the three options one after the other fast enough, after (approx.) one second there will be 3 timeouts that will trigger a model update three times in a row instead of just one. If this is acceptable for you, I can also support timeouts in radio buttons. |
I like the sound of this a lot. Take, for example, a search input box. At the moment as the user types it keeps triggering the update of the model, and if you have a filter based on that, it could be a lot of work between key presses, slowing the UI down. The ability to use different events and a timeout would make this scenario more performant. It would be useful if there was some way to set globally the event (and perhaps timeout too) used for model updates so that consistency can be achieved without having to add attributes to every input, although I suppose you could create a directive which applies the attributes across the app. |
Thanks for the suggestion @symblify. I've updated the PR so you can specify both Also, if you set I've added the new bits of documentation and a new spec that tests the feature. Enjoy! |
Very nice! Thanks for taking on board my suggestion @lrlopez, this looks like a great solution. Hopefully this will get merged in soon. |
I'm not too crazy about pulling the global default from the rootScope. I need to review the rest |
@IgorMinar, well... In fact it actually pulls the value from the current scope, but due to scope inheritance if options get set into the root scope it will be available everywhere unless they are overridden. This allows granularity when applying defaults (i.e. app-wide, controller-wide, etc.) Is that considered a bad practice? |
primarily I'm concerned about what is the correct behavior when you have components with isolated scopes. |
Hmmm... You are right, but then there is not much to do unless you use a global config service, import $rootScope using DI (which would ignore any intermediate override) or start pulling info directly from the DOM (which seems way too bad). May be having a standard AngularJS global config provider would be nice for handling these cases. Anyway, wouldn't make sense that behavior for elements inside an isolated scope component? They idea behind isolation is to prevent "interferences" from the outside... BTW, if the Angular team thinks that having defaults this way is not desirable, I can undo it in order to have the basic functionality merged. We can support defaults later once a better approach is found. Just tell me what you do... |
Just an update. I've been using this feature on a couple of personal projects and it seems to work ok. Also, most of my students applied Like @IgorMinar, I'm still concerned about isolated scopes, but I fear there's not much we can do about it unless we define & use a global configuration service or mess with the DOM, which doesn't sound too Angular. But I might be wrong... |
Perhaps to solve the issues @IgorMinar refers to, the feature should be limited to individual controls but the default should come from a provider ( I think it is more likely you would want to have consistency across all forms in an app which suggests making it a global configurable setting rather than having to set it up on all forms. Plus it sorts the issues with scopes and |
@symblify, thanks for the proposal... it's a good idea too. I've been thinking on this during the weekend and found another way to implement defaults in an intuitive way: What about extending Lots of possibilities... I don't know what to do! Now that AngularJS 1.1.4 has launched may be we can claim some time from the core team in order to throw some light into the approach they like... 😜 |
I agree, I would like to hear how the core team would approach this. Model updating is a core feature, and at the moment there isn't much control over how it happens, and it would be good to have more flexibility. As an aside, It is actually possible to change the way model updating works in current Angular builds by using a decorator to change the default behaviour. See my example plunk here: http://plnkr.co/edit/nMruiQdhdSS6B2j1XT8f?p=preview |
I've rebased the PR to latest master and also fixed some doc typos. Reference to $rootScope has been replaced into a generic explanation about setting default values from the scope. Directives that choose to have an isolated scope will have the same problems using other core features, like May be it's enough. Let's wait for the team response... |
@@ -48,6 +48,15 @@ function FormController(element, attrs) { | |||
form.$valid = true; | |||
form.$invalid = false; | |||
|
|||
// allows specifying default values for ng-update-model | |||
if (attrs.ngUpdateModelOn != undefined) { |
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.
use isDefined()
I like this, it's a valuable feature. I think it's fine, if you can't change this for isolate components. If they want to allow you to do so, they can export a way of doing it (either attribute or some config service). |
@@ -125,6 +125,19 @@ This allows us to extend the above example with these features: | |||
- SAVE button is enabled only if form has some changes and is valid | |||
- custom error messages for `user.email` and `user.agree` | |||
|
|||
By default, any change on the content will trigger a model update and form validation. You can override this behavior using the ng-update-on |
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.
typo: ng-update-on -> ng-update-model-on
@lrlopez - regarding this comment: #2129 (comment). I think that this is actually the way forward. We should have a new pair of directives,
If ngModel finds one or both of these controllers then it will delegate updating the model to these controllers. This removes the need to put anything onto the scope, which is nasty, and supports both adding to individual inputs, forms, groups of inputs (inside a div, say) or even the whole page (say on the body element). It also removes the need to create a global provider. I think it would also make the implementation cleaner as the the new stuff is hidden nicely in the new directves, no changes to form would be necessary and minimal changes to the input directives. @lrlopez, @IgorMinar , @vojtajina and @symblify : what do you think of this? |
Pete, I really like your approach. Once I get home I'll start working on it... Thanks! |
@petebacondarwin LGTM |
how about creating a generic $debounce service instead of the custom timeout code? |
Like this :-) ? On 3 May 2013 18:48, Igor Minar [email protected] wrote:
|
@IgorMinar, can you elaborate the idea? |
Ok, I got it. It's an interesting Idea, I'll split the timeout code into a generic service |
Oops, just saw Pete's reply. I can reuse that code, reference it or implement a new one, whatever you want... |
Instead of creating a new service, I modified I also followed Pete's advice and created two new directives All these features (debouncing+custom model updates) combined add 1103 bytes to the minified build. I've splitted both enhancements into two different pull requests in order to ease the creation of the changelog. I hope everything is ok now... |
By default, any change on the content will trigger an immediate model update and form validation. This PR implements a new directive `ng-model-options` that allow you to override this default behavior in several ways. You should specify an object with the different parameters. For example, it allows to trigger an update only when a particular event or list of events is received by the input using the `updateOn` key. Should you need multiple events, just assign an array to it. I.e. `ng-model-options="{ updateOn: 'blur' }"` will update and validate only after the control loses focus. If you want to keep the default behavior and just add new events that may trigger the model update and validation, add "default" as one of the specified events. I.e. `ng-model-options="{ updateOn: ['default','submit'] }"` Also, with the `debounce` option, `ng-model-options` will allow defering the actual model update until a timer expires. The timer will be reset each time an event is triggered. I.e. `ng-model-options="{ debounce: 500 }" for 500ms after the latest event. Custom timeouts for each event can be set for each event if you use an object in `debounce`. This can be useful to force immediate updates on some specific circunstances (like blur events). I.e. `ng-model-options="{ updateOn: ['default', 'blur'], debounce: { default: 500, blur: 0} }"` You can use the directive in any tag so its contents became the default settings for any child control, although they can be overriden. Closes angular#1285, angular#2129
By default, any change on the content will trigger an immediate model update and form validation. This PR implements a new directive `ng-model-options` that allow you to override this default behavior in several ways. You should specify an object with the different parameters. For example, it allows to trigger an update only when a particular event or list of events is received by the input using the `updateOn` key. Should you need multiple events, just assign an array to it. I.e. `ng-model-options="{ updateOn: 'blur' }"` will update and validate only after the control loses focus. If you want to keep the default behavior and just add new events that may trigger the model update and validation, add "default" as one of the specified events. I.e. `ng-model-options="{ updateOn: ['default','submit'] }"` Also, with the `debounce` option, `ng-model-options` will allow defering the actual model update until a timer expires. The timer will be reset each time an event is triggered. I.e. `ng-model-options="{ debounce: 500 }" for 500ms after the latest event. Custom timeouts for each event can be set for each event if you use an object in `debounce`. This can be useful to force immediate updates on some specific circunstances (like blur events). I.e. `ng-model-options="{ updateOn: ['default', 'blur'], debounce: { default: 500, blur: 0} }"` You can use the directive in any tag so its contents became the default settings for any child control, although they can be overriden. Closes angular#1285, angular#2129
By default, any change on the content will trigger an immediate model update and form validation. This PR implements a new directive `ng-model-options` that allow you to override this default behavior in several ways. You should specify an object with the different parameters. For example, it allows to trigger an update only when a particular event or list of events is received by the input using the `updateOn` key. Should you need multiple events, just assign an array to it. I.e. `ng-model-options="{ updateOn: 'blur' }"` will update and validate only after the control loses focus. If you want to keep the default behavior and just add new events that may trigger the model update and validation, add "default" as one of the specified events. I.e. `ng-model-options="{ updateOn: ['default','submit'] }"` Also, with the `debounce` option, `ng-model-options` will allow defering the actual model update until a timer expires. The timer will be reset each time an event is triggered. I.e. `ng-model-options="{ debounce: 500 }" for 500ms after the latest event. Custom timeouts for each event can be set for each event if you use an object in `debounce`. This can be useful to force immediate updates on some specific circumstances (like blur events). I.e. `ng-model-options="{ updateOn: ['default', 'blur'], debounce: { default: 500, blur: 0} }"` You can use the directive in any tag so its contents became the default settings for any child control, although they can be overriden. Closes angular#1285, angular#2129
By default, any change on the content will trigger an immediate model update and form validation. This PR implements a new directive `ng-model-options` that allow you to override this default behavior in several ways. You should specify an object with the different parameters. For example, it allows to trigger an update only when a particular event or list of events is received by the input using the `updateOn` key. Should you need multiple events, just assign an array to it. I.e. `ng-model-options="{ updateOn: 'blur' }"` will update and validate only after the control loses focus. If you want to keep the default behavior and just add new events that may trigger the model update and validation, add "default" as one of the specified events. I.e. `ng-model-options="{ updateOn: ['default','submit'] }"` Also, with the `debounce` option, `ng-model-options` will allow defering the actual model update until a timer expires. The timer will be reset each time an event is triggered. I.e. `ng-model-options="{ debounce: 500 }" for 500ms after the latest event. Custom timeouts for each event can be set for each event if you use an object in `debounce`. This can be useful to force immediate updates on some specific circumstances (like blur events). I.e. `ng-model-options="{ updateOn: ['default', 'blur'], debounce: { default: 500, blur: 0} }"` You can use the directive in any tag so its contents became the default settings for any child control, although they can be overriden. Closes angular#1285, angular#2129
By default, any change on the content will trigger an immediate model update and form validation. This PR implements a new directive `ng-model-options` that allow you to override this default behavior in several ways. You should specify an object with the different parameters. For example, it allows to trigger an update only when a particular event or list of events is received by the input using the `updateOn` key. Should you need multiple events, just assign an array to it. I.e. `ng-model-options="{ updateOn: 'blur' }"` will update and validate only after the control loses focus. If you want to keep the default behavior and just add new events that may trigger the model update and validation, add "default" as one of the specified events. I.e. `ng-model-options="{ updateOn: ['default','submit'] }"` Also, with the `debounce` option, `ng-model-options` will allow deferring the actual model update until a timer expires. The timer will be reset each time an event is triggered. I.e. `ng-model-options="{ debounce: 500 }" for 500ms after the latest event. Custom timeouts for each event can be set for each event if you use an object in `debounce`. This can be useful to force immediate updates on some specific circumstances (like blur events). I.e. `ng-model-options="{ updateOn: ['default', 'blur'], debounce: { default: 500, blur: 0} }"` You can use the directive in any tag so its contents became the default settings for any child control, although they can be overridden. Closes angular#1285, angular#2129
By default, any change on the content will trigger an immediate model update and form validation. This PR implements a new directive `ng-model-options` that allow you to override this default behavior in several ways. You should specify an object with the different parameters. For example, it allows to trigger an update only when a particular event or list of events is received by the input using the `updateOn` key. Should you need multiple events, just assign an array to it. I.e. `ng-model-options="{ updateOn: 'blur' }"` will update and validate only after the control loses focus. If you want to keep the default behavior and just add new events that may trigger the model update and validation, add "default" as one of the specified events. I.e. `ng-model-options="{ updateOn: ['default','submit'] }"` Also, with the `debounce` option, `ng-model-options` will allow deferring the actual model update until a timer expires. The timer will be reset each time an event is triggered. I.e. `ng-model-options="{ debounce: 500 }" for 500ms after the latest event. Custom timeouts for each event can be set for each event if you use an object in `debounce`. This can be useful to force immediate updates on some specific circumstances (like blur events). I.e. `ng-model-options="{ updateOn: ['default', 'blur'], debounce: { default: 500, blur: 0} }"` You can use the directive in any tag so its contents became the default settings for any child control, although they can be overridden. Closes angular#1285, angular#2129
By default, any change on the content will trigger an immediate model update and form validation. This PR implements a new directive `ng-model-options` that allow you to override this default behavior in several ways. You should specify an object with the different parameters. For example, it allows to trigger an update only when a particular event or list of events is received by the input using the `updateOn` key. Should you need multiple events, just assign an array to it. I.e. `ng-model-options="{ updateOn: 'blur' }"` will update and validate only after the control loses focus. If you want to keep the default behavior and just add new events that may trigger the model update and validation, add "default" as one of the specified events. I.e. `ng-model-options="{ updateOn: ['default','submit'] }"` Also, with the `debounce` option, `ng-model-options` will allow deferring the actual model update until a timer expires. The timer will be reset each time an event is triggered. I.e. `ng-model-options="{ debounce: 500 }" for 500ms after the latest event. Custom timeouts for each event can be set for each event if you use an object in `debounce`. This can be useful to force immediate updates on some specific circumstances (like blur events). I.e. `ng-model-options="{ updateOn: ['default', 'blur'], debounce: { default: 500, blur: 0} }"` You can use the directive in any tag so its contents became the default settings for any child control, although they can be overridden. Closes angular#1285, angular#2129
By default, any change on the content will trigger an immediate model update and form validation. This PR implements a new directive `ng-model-options` that allow you to override this default behavior in several ways. You should specify an object with the different parameters. For example, it allows to trigger an update only when a particular event or list of events is received by the input using the `updateOn` key. Should you need multiple events, just assign an array to it. I.e. `ng-model-options="{ updateOn: 'blur' }"` will update and validate only after the control loses focus. If you want to keep the default behavior and just add new events that may trigger the model update and validation, add "default" as one of the specified events. I.e. `ng-model-options="{ updateOn: ['default','submit'] }"` Also, with the `debounce` option, `ng-model-options` will allow deferring the actual model update until a timer expires. The timer will be reset each time an event is triggered. I.e. `ng-model-options="{ debounce: 500 }" for 500ms after the latest event. Custom timeouts for each event can be set for each event if you use an object in `debounce`. This can be useful to force immediate updates on some specific circumstances (like blur events). I.e. `ng-model-options="{ updateOn: ['default', 'blur'], debounce: { default: 500, blur: 0} }"` You can use the directive in any tag so its contents became the default settings for any child control, although they can be overridden. Closes angular#1285, angular#2129
By default, any change to the content will trigger an immediate model update and form validation. This PR implements a new directive `ng-model-options` that allow you to override this default behavior in several ways. You should specify an object with the different parameters. For example, it allows to trigger an update only when a particular event or list of events is received by the input using the `updateOn` key. Should you need multiple events, just assign an array to it. I.e. `ng-model-options="{ updateOn: 'blur' }"` will update and validate only after the control loses focus. If you want to keep the default behavior and just add new events that may trigger the model update and validation, add "default" as one of the specified events. I.e. `ng-model-options="{ updateOn: ['default','submit'] }"` Also, with the `debounce` option, `ng-model-options` will allow deferring the actual model update until a timer expires. The timer will be reset each time an event is triggered. I.e. `ng-model-options="{ debounce: 500 }" for 500ms after the latest event. Custom timeouts for each event can be set for each event if you use an object in `debounce`. This can be useful to force immediate updates on some specific circumstances (like blur events). I.e. `ng-model-options="{ updateOn: ['default', 'blur'], debounce: { default: 500, blur: 0} }"` You can use the directive in any tag so its contents became the default settings for any child control, although they can be overridden. Closes angular#1285, angular#2129
By default, any change to the content will trigger an immediate model update and form validation. This PR implements a new directive `ng-model-options` that allow you to override this default behavior in several ways. You should specify an object with the different parameters. For example, it allows to trigger an update only when a particular event or list of events is received by the input using the `updateOn` key. Should you need multiple events, just assign an array to it. I.e. `ng-model-options="{ updateOn: 'blur' }"` will update and validate only after the control loses focus. If you want to keep the default behavior and just add new events that may trigger the model update and validation, add "default" as one of the specified events. I.e. `ng-model-options="{ updateOn: ['default','submit'] }"` Also, with the `debounce` option, `ng-model-options` will allow deferring the actual model update until a timer expires. The timer will be reset each time an event is triggered. I.e. `ng-model-options="{ debounce: 500 }" for 500ms after the latest event. Custom timeouts for each event can be set for each event if you use an object in `debounce`. This can be useful to force immediate updates on some specific circumstances (like blur events). I.e. `ng-model-options="{ updateOn: ['default', 'blur'], debounce: { default: 500, blur: 0} }"` You can use the directive in any tag so its contents became the default settings for any child control, although they can be overridden. Closes angular#1285, angular#2129
By default, any change to the content will trigger an immediate model update and form validation. This PR implements a new directive `ng-model-options` that allow you to override this default behavior in several ways. You should specify an object with the different parameters. For example, it allows to trigger an update only when a particular event or list of events is received by the input using the `updateOn` key. Should you need multiple events, just assign an array to it. I.e. `ng-model-options="{ updateOn: 'blur' }"` will update and validate only after the control loses focus. If you want to keep the default behavior and just add new events that may trigger the model update and validation, add "default" as one of the specified events. I.e. `ng-model-options="{ updateOn: ['default','submit'] }"` Also, with the `debounce` option, `ng-model-options` will allow deferring the actual model update until a timer expires. The timer will be reset each time an event is triggered. I.e. `ng-model-options="{ debounce: 500 }" for 500ms after the latest event. Custom timeouts for each event can be set for each event if you use an object in `debounce`. This can be useful to force immediate updates on some specific circumstances (like blur events). I.e. `ng-model-options="{ updateOn: ['default', 'blur'], debounce: { default: 500, blur: 0} }"` You can use the directive in any tag so its contents became the default settings for any child control, although they can be overridden. Closes angular#1285, angular#2129
By default, any change to an input will trigger an immediate model update, form validation and run a $digest. This is not always desirable, especially when you have a large number of bindings to update. This PR implements a new directive `ngModelOptions`, which allow you to override this default behavior in several ways. It is implemented as an attribute, to which you pass an Angular expression, which evaluates to an **options** object. All inputs, using ngModel, will search for this directive in their ancestors and use it if found. This makes it easy to provide options for a whole form or even the whole page, as well as specifying exceptions for individual inputs. * You can specify what events trigger an update to the model by providing an `updateOn` property on the **options** object. This property takes a string containing a space separated list of events. For example, `ng-model-options="{ updateOn: 'blur' }"` will update the model only after the input loses focus. There is a special pseudo-event, called "default", which maps to the default event used by the input box normally. This is useful if you want to keep the default behavior and just add new events. * You can specify a debounce delay, how long to wait after the last triggering event before updating the model, by providing a `debounce` property on the **options** object. This property can be a simple number, the debounce delay for all events. For example, `ng-model-options="{ debounce: 500 }" will ensure the model is updated only when there has been a period 500ms since the last triggering event. The property can also be an object, where the keys map to events and the values are a corresponding debounce delay for that event. This can be useful to force immediate updates on some specific circumstances (like blur events). For example, `ng-model-options="{ updateOn: 'default blur', debounce: { default: 500, blur: 0} }"` This commit also brings to an end one of the longest running Pull Requests in the history of AngularJS (#2129)! A testament to the patience of @lrlopez. Closes #1285, #2129, #6945
Landed! Thanks to everyone who has worked toward getting this into Angular. |
Thanks, everyone, for all your hard work on this! I'm excited to tell my On Fri, Apr 4, 2014 at 7:55 AM, Pete Bacon Darwin
|
OMG is this really happening!? |
It totally is! |
Thank you @lrlopez and everyone who collaborated to make this happen 👍 |
This is the dawn of a new era. |
Great work everyone .. Enjoyed reading the complete comments of an year .. Awesome AngularJS community |
Couldn't wait to use this feature. well done! |
Great! This is what I was waiting for... any chance to get this by bower right now? |
@jaulz, just specify version as snapshot build:
|
@just-boris thanks a lot! |
And just to clarify, will this feature be available in version 1.3.0 when it comes out?— On Mon, Apr 14, 2014 at 12:20 AM, jaulz [email protected] wrote:
|
Yep. It will be in 1.3.0 |
I just created an issue with that feature. Unsure if it's a bug. #7117 |
This is awesome! |
By default, any change on the content will trigger an immediate model update and form validation. With this PR you can override this behavior using the
ng-update-model-on
attribute to bind only to a comma-delimited list of events.I.e.
ng-update-model-on="blur"
will update and validate only after the control loses focus.If you want to keep the default behavior and just add new events that may trigger the model update and validation, add "default" as one of the specified events.
I.e.
ng-update-model-on="default,submit"
Also, a
ng-update-model-debounce
attribute will allow defering the actual model update after the last triggered event. This feature is not available in radio buttons.I.e.
ng-update-model-debounce="500"
for 500msCustom timeouts for each event can be set for each event if you use an object in
ng-update-model-on
. I.e.ng-update-model-on="{default: 500, blur: 0}"
You can specify both attributes in any tag so they became the default settings for any child control, although they can be overriden.
Closes #1285