-
Notifications
You must be signed in to change notification settings - Fork 27.4k
fix(input): listen on "change" for radio and checkbox #14685
Conversation
test/helpers/testabilityPatch.js
Outdated
@@ -381,6 +381,9 @@ function generateInputCompilerHelper(helper) { | |||
// Compile the lot and return the input element | |||
$compile(helper.formElm)(scope); | |||
|
|||
$rootElement.append(helper.formElm); | |||
jqLite($document[0].body).append($rootElement); |
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.
OOC, why is this needed ?
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, I see why it is needed. Is it necessary on all browsers or some of them ? Which ones ?
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.
Some browsers don't trigger the "change" event on "click" when the input isn't attached to the document. This may be a bug, but it also makes sense. This is why the BC should only appear in (unit )tests.
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 use browserTrigger, it is needed in Chrome. It works in Firefox. Haven't tested other browsers. If you use the native element.click() fn, you need it in both FF and Chrome
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 recently changed browserTrigger
to manually raise events if the node is not connected to a document - c8bfbfc - would that solve this problem?
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.
Possibly, I'd have to test. It's not so much an issue, rather a slight annoyance. The PR still isn't grren because an ngTouch test fails on Safari iOS - related to the clickbusting code in ngClick. I don't have an iOs device, so I can't test this.
Generally, LGTM. I am a little worried of what might unforseeably break as a result of this change. For example, usecases depending on the relative order of event callbacks (e.g. We should mention it in the commit message imo. Talking about BC messages, mentioning |
You are right, this could break some other cases. |
ee23d70
to
512c031
Compare
I've fixed the test and updated the commit message / BC note |
Something is still failing on mobile Safari 😞 |
@gkalpak Yeah I noticed that. Is probably gonna be a pain in the ass to fix. |
@Narretz This just hit me in a production-running app that also used FastClick. FastClick morphs click handlers into touchstart ones with some hacks around it and in 50% of cases this broke the radios on the site in iOS 10. |
Intereesting. Maybe this will pass in ios10? Because so far ios8 is blocking this ... |
Angular 2 supports iOS 7-10 so I'd rather not drop iOS 8 support in ng1; we On Wed, 23 Nov 2016 at 13:42, Martin Staffa notifications@github.com
|
3c2089a
to
0c9ca91
Compare
ece7f22
to
c9c7d7c
Compare
@petebacondarwin @gkalpak I've updated this (BC) PR for 1.7.0. Can you give it another look? Or probably a new look since it's from last year. ;) |
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.
LGTM 👍
I am not thrilled about the fact that clicking on an detached element does not trigger a change
event (but this is a browser/spec issue, not a problem with this PR 😞). I have briefly thought about changing browserTrigger()
to also emit a change
event if an detached checkbox/radiobutton's checked
property is changed as a result of click
event, but it felt too automagical 😃
A couple of commit message nitpicks:
- Mention "instead of
click
" in the commit message subject (to make it obvious for those readng through the changelog - it now sounds more likechange
is listened to in addition toclick
). - you have don't have to attach --> you don't have to attach
- Put injectables and function names in
`...`
(e.g.$rootElement
,$document
,triggerHandler()
).
test/BinderSpec.js
Outdated
element = $compile( | ||
'<div>' + | ||
'<input type="radio" ng-model="sex" value="female">' + | ||
'<input type="radio" ng-model="sex" value="male">' + | ||
'</div>')($rootScope); | ||
|
||
jqLite($document[0].body).append($rootElement.append(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.
Maybe leave a comment here why this is needed.
test/ng/directive/ngRepeatSpec.js
Outdated
@@ -365,6 +365,8 @@ describe('ngRepeat', function() { | |||
'</li>' + | |||
'</ul>')(scope); | |||
|
|||
jqLite($document[0].body).append($rootElement.append(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.
I think a comment would be nice here too.
…d of "click" input[radio] and inout[checkbox] now listen on the change event instead of the click event. This fixes issue with 3rd party libraries that trigger a change event on inputs, e.g. Bootstrap 3 custom checkbox / radio button toggles. It also makes it easier to prevent specific events that can cause a checkbox / radio to change, e.g. click events. Previously, this was difficult because the custom click handler had to be registered before the input directive's click handler. It is possible that radio and checkbox listened to click because IE8 has broken support for listening on change, see http://www.quirksmode.org/dom/events/change.html Closes angular#4516 Closes angular#14667 BREAKING CHANGE: `input[radio]` and `input[checkbox]` now listen to the "change" event instead of the "click" event. Most apps should not be affected, as "change" is automatically fired by browsers after "click" happens. Two scenarios might need migration: - Custom click events: Before this change, custom click event listeners on radio / checkbox would be called after the input element and `ngModel` had been updated, unless they were specifically registered before the built-in click handlers. After this change, they are called before the input is updated, and can call event.preventDefault() to prevent the input from updating. If an app uses a click event listener that expects ngModel to be updated when it is called, it now needs to register a change event listener instead. - Triggering click events: Conventional trigger functions: The change event might not be fired when the input element is not attached to the document. This can happen in **tests** that compile input elements and trigger click events on them. Depending on the browser (Chrome and Safari) and the trigger method, the change event will not be fired when the input isn't attached to the document. Before: ```js it('should update the model', inject(function($compile, $rootScope) { var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope); inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger() expect($rootScope.checkbox).toBe(true); }); ``` With this patch, `$rootScope.checkbox` might not be true, because the click event hasn't triggered the change event. To make the test, work append the inputElm to the app's `$rootElement`, and the `$rootElement` to the `$document`. After: ```js it('should update the model', inject(function($compile, $rootScope, $rootElement, $document) { var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope); $rootElement.append(inputElm); $document.append($rootElement); inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger() expect($rootScope.checkbox).toBe(true); }); ``` `triggerHandler()`: If you are using this jQuery / jqLite function on the input elements, you don't have to attach the elements to the document, but instead change the triggered event to "change". This is because `triggerHandler(event)` only triggers the exact event when it has been added by jQuery / jqLite.
c9c7d7c
to
f460088
Compare
I Opened an issue on Chromium for future reference: https://bugs.chromium.org/p/chromium/issues/detail?id=773680 |
What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
bug fix
What is the current behavior? (You can also link to an open issue here)
radio and checkbox listen on "click" which can cause unforeseen behavior
What is the new behavior (if this is a feature change)?
radio and checkbox listen on "change"
Does this PR introduce a breaking change?
Yes, for certain unit-test behaviors
Please check if the PR fulfills these requirements
[ ] Docs have been added / updated (for bug fixes / features)Other information:
input[radio] and inout[checkbox] now listen on the change event instead
of the click event. This fixes issue with 3rd party libraries that trigger
a change event on inputs, e.g. Bootstrap 3 custom checkbox / radio button
toggles.
It also makes it easier to prevent specific events that can cause a checkbox / radio
to change, e.g. click events. Previously, this was difficult because the custom click
handler had to be registered before the input directive's click handler.
It is possible that radio and checkbox listened to click because IE8 has
broken support for listening on change, see http://www.quirksmode.org/dom/events/change.html
Closes #4516
Closes #14667
BREAKING CHANGE:
input[radio] and input[checkbox] now need to be attached to the document to propagate events
correctly. This should only be of concern in unit-tests that compile input elements and
trigger click events on them. This is because we now listen to the change event which
gets automatically triggered by browsers when a checkbox or radio is clicked. However,
this may fail in some browsers when the elements are not attached to the document.
Before:
With this patch,
$rootScope.checkbox
might not be true, because the click eventhasn't triggered the change event. To make the test, work append the inputElm to the app's
$rootElement, and the $rootElement to the $document:
After: