Skip to content

Server-side blazor : <select> value is bound after being rendered #12667

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

Closed
glacasa opened this issue Jul 28, 2019 · 15 comments
Closed

Server-side blazor : <select> value is bound after being rendered #12667

glacasa opened this issue Jul 28, 2019 · 15 comments
Assignees
Labels
area-blazor Includes: Blazor, Razor Components bug This issue describes a behavior which is not expected - a bug. Done This issue has been fixed

Comments

@glacasa
Copy link

glacasa commented Jul 28, 2019

When I want to display a select tag with its value bound to a property, the select is rendered with the value not bound (first item selected), the correct option is then selected immediately.

To reproduce, on the default Blazor template, I replaced the Counter.razor with this :

@page "/counter"

<select @bind="TestValue">
    <option value="0">0</option>
    <option value="1">1</option>
    <option value="2">2</option>
</select>

<input type="number" @bind="TestValue" />

@code {    
    int TestValue { get; set; } = 1;
}

When we go to this page and reload, we can briefly see the "0" before the "1" in the select.
The input is bound immediately.

I don't see this happens when navigating to the page, but it happens when I press F5 to reload the page.

I have this behavior in preview 7, I didn't notice it in previous versions.

@blowdart blowdart added the area-blazor Includes: Blazor, Razor Components label Jul 28, 2019
@mkArtakMSFT mkArtakMSFT added this to the 3.0.0-rc1 milestone Jul 29, 2019
@hongkuancn
Copy link

hongkuancn commented Jul 30, 2019

I came across the same issue when I use <input type="checkbox">. The code I use is as follows:

@page

<input type="checkbox" value="@val" @onchange="@((UIChangeEventArgs __e) => val = (bool) __e.Value  )" />
      
<p>@val</p>

@code {
    private bool val = true;
}

The checkbox is not checked on UI when I load the page.

I am using preview 6 and I didn't check other versions.

@javiercn
Copy link
Member

@hongkuancn Can you provide more details for the issue? Is the checkbox not checked during prerendering, is it not checked in reaction to the event handler?

@glacasa
Copy link
Author

glacasa commented Jul 30, 2019

@hongkuancn
I don't think it's the same issue.
With checkbox you should use the checked property, or just bind the value :

<input type="checkbox" checked="@val" @onchange="@((UIChangeEventArgs __e) => val = (bool) __e.Value  )" />

<input type="checkbox" @bind="val" />

They both work with preview 7 (and no rendering issue like with the select)

@javiercn
Copy link
Member

@glacasa Ok, so this seems like a prerendering issue, where the default value is not populated properly and gets fixed once its bound.

@hongkuancn
Copy link

@glacasa
Oh, yes. I should use checked instead of value. Thanks a lot.
Anyway, I am also getting the same behavior on <select> with preview 6.
@javiercn
There is no problem with checkbox now.

@mkArtakMSFT mkArtakMSFT modified the milestones: 3.0.0-rc1, 3.0.0-preview9 Aug 5, 2019
@SteveSandersonMS
Copy link
Member

SteveSandersonMS commented Aug 8, 2019

@javiercn @rynowak We need to make a decision about whether (or perhaps more reasonably, how) to support this. It's nontrivial so might end up in 3.1.

The problem is that bind on a <select> represents its chosen value by setting a value attribute on the <select>, which is not the same as how HTML represents it natively. HTML looks for a selected attribute on an <option> element. The reason we do what we do is that it aligns select with other input element types and isn't a problem when applying a renderbatch via JavaScript, because the JavaScript can simply write to the selectedValue property on the select.

The extra big problem for prerendering is that the block of <option> elements might be embedded inside a Markup node. So if we wanted to solve this by updating the prerendering logic only, we'd need to make it re-parse the markup (welcome back, AngleSharp?), walk it, mutate it, then re-serialize it. Ugh.

Perhaps a cleaner solution would involve changing the Razor compiler so that it specifically forbids <option> elements from appearing inside Markup nodes, in the same way it excludes C# nodes from markup. Then the change in prerendering logic would be to track whether we're currently inside a <select> and its associated value attribute value, and then when we see an <option> descendant, conditionally apply an extra selected attribute to it. That's a bit messy but not hugely difficult.

@lpm-nova
Copy link

lpm-nova commented Aug 8, 2019

Will this change allow selected to work as intended?

@rynowak
Copy link
Member

rynowak commented Aug 8, 2019

Are there meaningful and valid use cases option inside markup inside select? What about optiongroup?

@SteveSandersonMS
Copy link
Member

Are there meaningful and valid use cases option inside markup inside select?

Yes, it's what happens by default if your <select> contains only static content in your source code.

What about optiongroup?

We shouldn't assume the <option> is a direct child of <select>, but rather should allow for it being a descendant at any depth. In practice the only intermediate element is likely to be <optgroup> but we don't need to rely on that.

My suggestion above doesn't rely on <option> being a direct child of <select>.

@rynowak
Copy link
Member

rynowak commented Aug 8, 2019

Ah, I think I misinterpreted what you wrote. So if we changed the markup-block pass to bail out of a select element, that would enable us to fix this on the JS side? That seems like a straightforward change, and I have no objections.

@SteveSandersonMS
Copy link
Member

Yes. I don't recall precisely how the markup-block pass works, but it would need to ensure that an <option> element and its ancestors get flagged as non-candidates for coalescence.

I expect it has to be triggered at the level of the <option>, not the <select>, because we don't want the contents of the <select> to become markup.

@rynowak
Copy link
Member

rynowak commented Aug 8, 2019

The pass works top-down-bottom-up, so we'd just prevent recursion into a select.

@SteveSandersonMS
Copy link
Member

OK

would enable us to fix this on the JS side

Not in JS, but in the prerendering logic (C#)

@SteveSandersonMS
Copy link
Member

Done in #12996 and dotnet/razor#952

@SteveSandersonMS SteveSandersonMS added the Done This issue has been fixed label Aug 13, 2019
@gulbanana
Copy link

if anyone else found this issue searching for why their custom StateHasChanged-based binding wasn't working - render your selection using option.selected now, not select.value!

@ghost ghost locked as resolved and limited conversation to collaborators Dec 2, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-blazor Includes: Blazor, Razor Components bug This issue describes a behavior which is not expected - a bug. Done This issue has been fixed
Projects
None yet
Development

No branches or pull requests

10 participants