Skip to content

Commit 24ef4e1

Browse files
authored
set select multiple value with spread (#4894)
1 parent d61a7a0 commit 24ef4e1

File tree

6 files changed

+80
-38
lines changed

6 files changed

+80
-38
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
* Update `<select>` with `bind:value` when the available `<option>`s change ([#1764](https://github.com/sveltejs/svelte/issues/1764))
66
* Fix inconsistencies when setting a two-way bound `<input>` to `undefined` ([#3569](https://github.com/sveltejs/svelte/issues/3569))
7+
* Fix setting `<select multiple>` when there are spread attributes ([#4392](https://github.com/sveltejs/svelte/issues/4392))
78
* Fix resize listening on certain older browsers ([#4752](https://github.com/sveltejs/svelte/issues/4752))
89
* Add `a11y-no-onchange` warning ([#4788](https://github.com/sveltejs/svelte/pull/4788))
910
* Add `muted` binding for media elements ([#2998](https://github.com/sveltejs/svelte/issues/2998))

src/compiler/compile/render_dom/wrappers/Element/Attribute.ts

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -102,25 +102,12 @@ export default class AttributeWrapper {
102102
} else if (is_select_value_attribute) {
103103
// annoying special case
104104
const is_multiple_select = element.node.get_static_attribute_value('multiple');
105-
const i = block.get_unique_name('i');
106-
const option = block.get_unique_name('option');
107-
108-
const if_statement = is_multiple_select
109-
? b`
110-
${option}.selected = ~${last}.indexOf(${option}.__value);`
111-
: b`
112-
if (${option}.__value === ${last}) {
113-
${option}.selected = true;
114-
${{ type: 'BreakStatement' }};
115-
}`; // TODO the BreakStatement is gross, but it's unsyntactic otherwise...
116-
117-
updater = b`
118-
for (var ${i} = 0; ${i} < ${element.var}.options.length; ${i} += 1) {
119-
var ${option} = ${element.var}.options[${i}];
120-
121-
${if_statement}
122-
}
123-
`;
105+
106+
if (is_multiple_select) {
107+
updater = b`@select_options(${element.var}, ${last});`;
108+
} else {
109+
updater = b`@select_option(${element.var}, ${last});`;
110+
}
124111

125112
block.chunks.mount.push(b`
126113
${last} = ${value};

src/compiler/compile/render_dom/wrappers/Element/index.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -705,10 +705,27 @@ export default class ElementWrapper extends Wrapper {
705705
);
706706

707707
block.chunks.update.push(b`
708-
${fn}(${this.var}, @get_spread_update(${levels}, [
708+
${fn}(${this.var}, ${data} = @get_spread_update(${levels}, [
709709
${updates}
710710
]));
711711
`);
712+
713+
// handle edge cases for elements
714+
if (this.node.name === 'select') {
715+
const dependencies = new Set();
716+
for (const attr of this.attributes) {
717+
for (const dep of attr.node.dependencies) {
718+
dependencies.add(dep);
719+
}
720+
}
721+
722+
block.chunks.mount.push(b`
723+
if (${data}.multiple) @select_options(${this.var}, ${data}.value);
724+
`);
725+
block.chunks.update.push(b`
726+
if (${block.renderer.dirty(Array.from(dependencies))} && ${data}.multiple) @select_options(${this.var}, ${data}.value);
727+
`);
728+
}
712729
}
713730

714731
add_transitions(

test/js/samples/select-dynamic-value/expected.js

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import {
77
init,
88
insert,
99
noop,
10-
safe_not_equal
10+
safe_not_equal,
11+
select_option
1112
} from "svelte/internal";
1213

1314
function create_fragment(ctx) {
@@ -33,26 +34,11 @@ function create_fragment(ctx) {
3334
append(select, option0);
3435
append(select, option1);
3536
select_value_value = /*current*/ ctx[0];
36-
37-
for (var i = 0; i < select.options.length; i += 1) {
38-
var option = select.options[i];
39-
40-
if (option.__value === select_value_value) {
41-
option.selected = true;
42-
break;
43-
}
44-
}
37+
select_option(select, select_value_value);
4538
},
4639
p(ctx, [dirty]) {
4740
if (dirty & /*current*/ 1 && select_value_value !== (select_value_value = /*current*/ ctx[0])) {
48-
for (var i = 0; i < select.options.length; i += 1) {
49-
var option = select.options[i];
50-
51-
if (option.__value === select_value_value) {
52-
option.selected = true;
53-
break;
54-
}
55-
}
41+
select_option(select, select_value_value);
5642
}
5743
},
5844
i: noop,
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
export default {
2+
async test({ assert, component, target, window }) {
3+
const [input1, input2] = target.querySelectorAll('input');
4+
const select = target.querySelector('select');
5+
const [option1, option2] = select.childNodes;
6+
7+
let selections = Array.from(select.selectedOptions);
8+
assert.equal(selections.length, 2);
9+
assert.ok(selections.includes(option1));
10+
assert.ok(selections.includes(option2));
11+
12+
const event = new window.Event('change');
13+
14+
input1.checked = false;
15+
await input1.dispatchEvent(event);
16+
17+
selections = Array.from(select.selectedOptions);
18+
assert.equal(selections.length, 1);
19+
assert.ok(!selections.includes(option1));
20+
assert.ok(selections.includes(option2));
21+
22+
input2.checked = false;
23+
await input2.dispatchEvent(event);
24+
input1.checked = true;
25+
await input1.dispatchEvent(event);
26+
27+
selections = Array.from(select.selectedOptions);
28+
assert.equal(selections.length, 1);
29+
assert.ok(selections.includes(option1));
30+
assert.ok(!selections.includes(option2));
31+
32+
component.spread = { value: ['Hello', 'World'] };
33+
34+
selections = Array.from(select.selectedOptions);
35+
assert.equal(selections.length, 2);
36+
assert.ok(selections.includes(option1));
37+
assert.ok(selections.includes(option2));
38+
}
39+
};
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<script>
2+
let value = ['Hello', 'World'];
3+
export let spread = {};
4+
</script>
5+
6+
<select multiple {value} {...spread}>
7+
<option>Hello</option>
8+
<option>World</option>
9+
</select>
10+
11+
<input type="checkbox" value="Hello" bind:group={value}>
12+
<input type="checkbox" value="World" bind:group={value}>

0 commit comments

Comments
 (0)