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

fix(select): keep original selection when using shift to add options … #15676

Merged
merged 2 commits into from
Feb 8, 2017

Conversation

Narretz
Copy link
Contributor

@Narretz Narretz commented Feb 3, 2017

…in IE/Edge

In IE9-11 + Edge, the selected options were previously incorrect under the following
circumstances:

  • at least two options are selected
  • shift+click or shift+down/up is used to add to the selection (any number of options)

In these cases, only the last of the previously selected options and the newly selected
options would be selected.

The problems seems to be that the render engine gets confused when an option that
already has selected = true gets selected = true set again.

Note that this is not testable via unit test because it's not possible to simulate
click / keyboard events on option elements (the events are delegated to the select element
change event).

Fixes #15675

What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
Bugfix

What is the current behavior? (You can also link to an open issue here)
See #15675

Does this PR introduce a breaking change?
No

Please check if the PR fulfills these requirements

Other information:

// IE and Edge will de-select selected options when you set the selected property again, e.g.
// when you add to the selection via shift+click/UP/DOWN
// Therefore, only set it if necessary
if ((shouldBeSelected && !currentlySelected) || (!shouldBeSelected && currentlySelected)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldBeSelected !== currentlySelected?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I had the feeling this could be simpler :D

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you swithc to !== you need to make sure both values are booleans.
(I am not sure about option.selected. Theoretically it should always be boolean, but you never know especially with older browsers.)

Copy link
Member

@gkalpak gkalpak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't this be tested by directly calling selectCtrl.writeValue?

option.selected = !!value && (includes(value, option.value) ||
includes(value, selectCtrl.selectValueMap[option.value]));
var shouldBeSelected = !!value && (includes(value, option.value) ||
includes(value, selectCtrl.selectValueMap[option.value]));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Align with includes above.

// IE and Edge will de-select selected options when you set the selected property again, e.g.
// when you add to the selection via shift+click/UP/DOWN
// Therefore, only set it if necessary
if ((shouldBeSelected && !currentlySelected) || (!shouldBeSelected && currentlySelected)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you swithc to !== you need to make sure both values are booleans.
(I am not sure about option.selected. Theoretically it should always be boolean, but you never know especially with older browsers.)

…in IE/Edge

In IE9-11 + Edge, the selected options were previously incorrect under the following
circumstances:
- at least two options are selected
- shift+click or shift+down/up is used to add to the selection (any number of options)

In these cases, only the last of the previously selected options and the newly selected
options would be selected.

The problems seems to be that the render engine gets confused when an option that
already has selected = true gets selected = true set again.

Note that this is not testable via unit test because it's not possible to simulate
click / keyboard events on option elements (the events are delegated to the select element
change event), and the problem also doesn't appear when modifying the option elements directly
and then using the selectController API. It seems that this only happens when you manipulate the
select directly in the user interface.

Fixes angular#15675
Closes angular#15676
@Narretz Narretz force-pushed the fix-select-multiple-ie-15675 branch from 471c4de to 14f8ce3 Compare February 5, 2017 16:32
@Narretz
Copy link
Contributor Author

Narretz commented Feb 5, 2017

I tried to test it directly with the selectCtrl API, but this doesn't replicate the bug. It must have something to do with the ui of the select box in IE / Edge.

@gkalpak
Copy link
Member

gkalpak commented Feb 5, 2017

Maybe we could just test that we don't re-set the selected property. (I just realized this seems equivalent to #15478, but using <option> instead of ngOptions).

Maybe something like this would work (although it borderline testing "implementation details").

@Narretz
Copy link
Contributor Author

Narretz commented Feb 6, 2017

Hm. I agree that this is an implementation detail. I'll let you decide if I should add this test or not.

Copy link
Member

@gkalpak gkalpak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"No test" is fine by me.

if ((shouldBeSelected && !currentlySelected)) {
setOptionAsSelected(jqLite(option));
} else if ((!shouldBeSelected && currentlySelected)) {
option.selected = shouldBeSelected;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder why we are using option.selected here, but option.prop('selected', true) in setOptionAsSelected().
Probably doesn't matter, so we should use the same for consistency. In fact, I think it is better to make setOptionSelected() a helper that can both select and unselect an option and use that whenever needed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(BTW, there is a double space before the = 😱

function setOptionSelectedStatus(optionEl, value) {
optionEl.prop('selected', value); // needed for IE
if (value) {
optionEl.attr('selected', value);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW: .attr('selected', null) also removes the attribute. So, this could be:

optionEl.prop('selected', value);
optionEl.attr('selected', value || null);

* However, screenreaders might react to the selected attribute instead, see
* https://github.com/angular/angular.js/issues/14419
*/
optionEl.attr('selected', value || null);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I said it works, but do we have tests that the property is actually removed? 😛

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, you little rascal!
We have the toBeMarkedAsSelected() custom matcher which complains when the selected attr is in the wrong state. And jqlite also removes the attribute when you pass false and the element is boolean. So I'm going with that.

This helps screen readers identify the selected options,
see angular#14419
@Narretz Narretz force-pushed the fix-select-multiple-ie-15675 branch from dcc9b5e to 0684b39 Compare February 8, 2017 16:19
@Narretz Narretz merged commit 538f460 into angular:master Feb 8, 2017
Narretz added a commit that referenced this pull request Feb 8, 2017
…in IE/Edge

In IE9-11 + Edge, the selected options were previously incorrect under the following
circumstances:
- at least two options are selected
- shift+click or shift+down/up is used to add to the selection (any number of options)

In these cases, only the last of the previously selected options and the newly selected
options would be selected.

The problems seems to be that the render engine gets confused when an option that
already has selected = true gets selected = true set again.

Note that this is not testable via unit test because it's not possible to simulate
click / keyboard events on option elements (the events are delegated to the select element
change event), and the problem also doesn't appear when modifying the option elements directly
and then using the selectController API. It seems that this only happens when you manipulate the
select directly in the user interface.

Fixes #15675
Closes #15676
ellimist pushed a commit to ellimist/angular.js that referenced this pull request Mar 15, 2017
…in IE/Edge

In IE9-11 + Edge, the selected options were previously incorrect under the following
circumstances:
- at least two options are selected
- shift+click or shift+down/up is used to add to the selection (any number of options)

In these cases, only the last of the previously selected options and the newly selected
options would be selected.

The problems seems to be that the render engine gets confused when an option that
already has selected = true gets selected = true set again.

Note that this is not testable via unit test because it's not possible to simulate
click / keyboard events on option elements (the events are delegated to the select element
change event), and the problem also doesn't appear when modifying the option elements directly
and then using the selectController API. It seems that this only happens when you manipulate the
select directly in the user interface.

Fixes angular#15675
Closes angular#15676
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

not able to select multiple options on IE with ng-model
4 participants