Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

ng-model gets updated before ng-click on input radio #14667

Closed
victorherraiz opened this issue May 25, 2016 · 1 comment · Fixed by #14685
Closed

ng-model gets updated before ng-click on input radio #14667

victorherraiz opened this issue May 25, 2016 · 1 comment · Fixed by #14685

Comments

@victorherraiz
Copy link

Note: for support questions, please use one of these channels: https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#question. This repository's issues are reserved for feature requests and bug reports.

Do you want to request a feature or report a bug?

Bug

What is the current behavior?

ng-model gets updated before ng-click on input radio and there is no way to prevent the default action

What is the expected behavior?

It should be a simple way to prevent default action.

Which versions of Angular, and which browser / OS are affected by this issue? Did this work in previous versions of Angular? Please also test with the latest stable and snapshot (https://code.angularjs.org/snapshot/) versions.

Browsers: Chrome, IE11
Angularjs: 1.5.0

@Narretz
Copy link
Contributor

Narretz commented May 25, 2016

In general, we don't make guarantees about the order of events in connection with ngModel. ngclick cannot prevent the default in your case, because its click handler is added after the click handler that gets added by the radioInput. A few thoughts:

  • we should probably listen to the change event in the radio directive. I assume it wasn't done because support for this is buggy in IE8.
  • you can achieve what you want by creating a directive that adds a click handler before the radio input
  • you'll also need to call e.stopImmediatePropagation, otherwise you only prevent the visual update

See http://plnkr.co/edit/yQ4qOlUczgimDPtMUmHa?p=preview

HOWEVER, I don't think it's good ui to stop such a basic function as selecting a radio, especially when it looks like the user can click the radio. Instead, you should use ngDisabled to disable the radio when you don't want anyone to select it.

@Narretz Narretz added this to the Ice Box milestone May 25, 2016
Narretz added a commit to Narretz/angular.js that referenced this issue May 27, 2016
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 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:

```js
    it('should update the model', inject(function($compile, $rootScope) {
      var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);

      browserTrigger(inputElm[0], 'click');
      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);

      browserTrigger(inputElm[0], 'click');
      expect($rootScope.checkbox).toBe(true);
    });
```
Narretz added a commit to Narretz/angular.js that referenced this issue May 27, 2016
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:

- 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 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 have 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.

- Custom click events:

Before this change, custom click handlers 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() and
event.stopImmediatePropagation() to prevent the input from updating.
If an app relied on the model being updated when the custom click handler has been called, you should
register a "change" handler instead.
Narretz added a commit to Narretz/angular.js that referenced this issue Mar 29, 2017
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:

- 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 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 have 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.

- Custom click events:

Before this change, custom click handlers 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() and
event.stopImmediatePropagation() to prevent the input from updating.
If an app relied on the model being updated when the custom click handler has been called, you should
register a "change" handler instead.
Narretz added a commit to Narretz/angular.js that referenced this issue Oct 6, 2017
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 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 have 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.
Narretz added a commit to Narretz/angular.js that referenced this issue Oct 6, 2017
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 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 have 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.
Narretz added a commit to Narretz/angular.js that referenced this issue Oct 6, 2017
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 have 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.
Narretz added a commit to Narretz/angular.js that referenced this issue Oct 11, 2017
…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.
Narretz added a commit that referenced this issue Oct 13, 2017
…ngModels

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
Closes #14685

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.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants