Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

select option with default selection.... broken in 3.42? #6954

Closed
Shubedobedo opened this issue Nov 21, 2021 · 20 comments
Closed

select option with default selection.... broken in 3.42? #6954

Shubedobedo opened this issue Nov 21, 2021 · 20 comments

Comments

@Shubedobedo
Copy link

Shubedobedo commented Nov 21, 2021

Describe the bug

This looks to work in 3.41 and before but breaks at 3.42 or newer. Docs don't mention it at all so it is hard to trouble shoot.

Reproduction

Run this code.

<script>
let options = [
	{ id: "4", value: "2019" },
	{ id: "3", value: "2018" },
	{ id: "2", value: "2017" },
	{ id: "1", value: "2016" },
];
	
let selected = { id: "2", value: "2018" };
	
	
</script>

<select bind:value={selected}>
	{#each options as option}
		<option value={option} selected={selected.id === option.id}>{option.value}</option>
	{/each}	
</select>
<p>
	Selected {JSON.stringify(selected)}
</p>

Logs

No response

System Info

Version 3.42 or newer

Severity

annoyance

@bluwy
Copy link
Member

bluwy commented Nov 22, 2021

The repro doesn't use a select or option element so I can't reproduce this. Please make a repro in the REPL.

@Conduitry
Copy link
Member

The code in the message wasn't displaying correctly because it wasn't wrapped in code blocks - I've just edited the original message.

@rmunn
Copy link
Contributor

rmunn commented Nov 22, 2021

Pasted OP's code into a REPL, and found that https://svelte.dev/repl/01e1ffe5302d4ed19a9f180bcd87dbe4?version=3.42.1 works but https://svelte.dev/repl/01e1ffe5302d4ed19a9f180bcd87dbe4?version=3.42.2 does not. (Here "works" means "select element shows a value").

@rmunn
Copy link
Contributor

rmunn commented Nov 22, 2021

Probably caused by #6170, as that's one of the PRs mentioned in the 3.42.2 changelog.

@rmunn
Copy link
Contributor

rmunn commented Nov 22, 2021

In fact, now that I look closely at the repro code, this is certainly caused by #6170, and deliberately so. The select options are:

let options = [
	{ id: "4", value: "2019" },
	{ id: "3", value: "2018" },
	{ id: "2", value: "2017" },
	{ id: "1", value: "2016" },
];

And the selected value is { id: "2", value: "2018" }. Note how that doesn't match any of the four options: ID 2 has value 2017 in the options list, but 2018 in the selected value. So this example appears to be working as intended.

@Shubedobedo
Copy link
Author

Thanks for fixing my post. Didn't realize I need to post it in a code block.

@Shubedobedo
Copy link
Author

@rmunn no, in that case, I agree that I typoed that but if you change it to a correct option it still has the issue.

updated
let selected = { id: "2", value: "2017" };

@baseballyama
Copy link
Member

I found the cause.
But I'm still trying to figure out how to fix it.

The selected attribute of the <option> tag supports JavaScript expressions, and the compiler supports it for initial value assignments.

option.selected = option_selected_value = /*selected*/ ctx[0].id === /*option*/ ctx[3].id;

However, select_option simply performs a string comparison.
That's why Strict equality (===) between JSON will always be false and the last statement will be executed.

export function select_option(select, value) {
for (let i = 0; i < select.options.length; i += 1) {
const option = select.options[i];
if (option.__value === value) {
option.selected = true;
return;
}
}
select.selectedIndex = -1; // no option should be selected
}

I think the way to fix this is to support JavaScript expressions in select_option.
But I need to think about more details.

@Shubedobedo
Copy link
Author

really I am just looking for a way to set a select box to edit a record that has already been selected. Each select option needs to be an object though. If there is a better way to do that?

@baseballyama
Copy link
Member

@Shubedobedo
Copy link
Author

Then what is being loaded from db wouldn't match. Your storing an object but looking up a string.

@Shubedobedo
Copy link
Author

I was able to get a workaround with this though. https://svelte.dev/repl/e4698e605aca48d8937cb1ee32220835?version=3.44.2

@RaiVaibhav
Copy link
Contributor

https://svelte.dev/repl/5699ad3517074fe8a7333ae52880b255?version=3.44.2 or use codepen, inspect the HTML and check the selected attr value both are strings so select takes the last value, selected={selected.id === option.id} will result in the string value (not sure this should be considered as a bug?) of the selected attr, also like @baseballyama mentioned even strict equality comparison will not help.

#6874 is the PR to fix the regression which checks if selected attr is present (which was caused because of the (selectedIndex = -1) but if the attr value is a string then its behaviour goes back to the default one.

@RaiVaibhav
Copy link
Contributor

using the primitive type value to check will fix the behaviour in both versions but not sure about
I think the way to fix this is to support JavaScript expressions in select_option.?

@Shubedobedo
Copy link
Author

@RaiVaibhav but I don't want it to go back to the default one I want it to go to the correct one. I created a more real example of what I am trying to do here: https://svelte.dev/repl/e4698e605aca48d8937cb1ee32220835?version=3.44.2. With @baseballyama's workaround, I can set additional properties of the object but in the case of an object that has 4+ properties, it isn't that useful.

@baseballyama
Copy link
Member

baseballyama commented Nov 23, 2021

IMO, in general, it would be better to use the on:change event handler and use primitive types for the value attribute in such cases.

Nevertheless, I think it would be useful to have object type support.
For instance, Vue.js has such feature. (REPL)

@Shubedobedo
Copy link
Author

SO I'm not sure if I did something incorrectly or if there is another issue with select somehow. I took @baseballyama advice and change to a on:change event. That seemed to work at first glance but if you change the select multiple time is starts giving back weird results. In this repl for example. If you chanhge it to "unarmed" Everything works as it should but if you then changhe it back to the starting value of "Horrible Claws & Teeth" it doesn't update correctly.

That being said I am not sure why this is happening but I would doubt it is related to the same select issue. Man this is frustrating is seems like it should be so easy to do this. I might be better off just using vue? I check out that repl and that is basically what I am looking for.

@baseballyama
Copy link
Member

I'm not sure about your specific use-case.
But at least your REPL can be fixed as this REPL.

You use property of object for bind:value. Therefore it was updated by Svelte when selected option is changed.
I mean options[0].name is changed when selected option is changed.
This is not Svelte bug.
I think this is one of the common programming mistakes.

@theodorejb
Copy link
Contributor

theodorejb commented Nov 28, 2021

This is expected behavior, IMO. It works fine to bind to an object, but the selected variable needs to be a reference to the desired value (rather than a different object copy) so that strict equality comparison will work. Example: https://svelte.dev/repl/4601f621ce364685b1baee2d7b5c21a4?version=3.44.2

Basically this line:

let selected = { id: "2", value: "2017" };

should be replaced with:

$: selected = options.find(o => o.id === "2");

@dummdidumm
Copy link
Member

Closing as "works as designed". Whether or not the value is selected is determined by object equality (===), not object shape (deepEquals).

@dummdidumm dummdidumm closed this as not planned Won't fix, can't repro, duplicate, stale Feb 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants