Home > Back-end >  Blazor Server Side race condition with form fields
Blazor Server Side race condition with form fields

Time:06-15

I have an issue where a radio button selection is reset immediately after clicking it.

I have the following fields in a form:

enter image description here

I think what's happening is:

  1. The user types a value into the input
  2. The user then clicks a radio option (whilst having focus in the text input)
  3. This click triggers the onChange/bind event for the text input to fire server side
  4. For a brief moment, the user see's their radio option selected
  5. Presumably when the server responds from the input's onChange event, it re-renders the radio button with the state it received at the time - i.e. no radio selection

Code:

    <InputText @bind-Value="_viewModel.Amount"></InputText>


    <InputRadioGroup @bind-Value="_viewModel.SelectedOptionId" Name="name">
        @foreach (var option in _viewModel.RadioOptions)
        {
            <InputRadio Name="name" Value="@option.Id.ToString()" />
            <text>&nbsp;</text>@option.Name<br>
        }
    </InputRadioGroup>

How do you prevent this?

CodePudding user response:

What is happening here is that you bind to the wrong type.

I assume that both RadioOption.Id and SelectedOptionId are of type int.

That is fine, but then don't convert the Options' Ids to something else. It will hinder binding to the Selected value.

   <InputRadio Name="name" Value="@option.Id.ToString()" />  -- wrong
   <InputRadio Name="name" Value="@option.Id" />  -- ok

CodePudding user response:

The issue is that the click (change event) is not intercepted by the framework, as the component is being re-rendered once the focus is lost from the input text element, triggering its change event.

The following technique describes how you handle this:

You simply have to prevent the re-rendering of the component when the input text element lose its focus. To achieve this, you should override the ShouldRender method like this:

protected override bool ShouldRender()
   {
      return shouldRender;
   }

Define a boolean local variable that should be returned from the ShouldRender method. When it has the value false, the framework will not re-render even if the state of the component has changed. When it has the value true, the framework will re-render:

private bool shouldRender = false;

Now, we want to prevent the re-rendering of the input type text element... To achieve this you should add the following to it:

@onfocusin="@(() => shouldRender = false)"

Full definition:

<InputText @bind-Value="model.Amount" @onfocusin="@(() => shouldRender = false)"></InputText>

But then we want the radio group to re-render the component as we select a radio button. This can be done by adding:

@onfocusin="@(() => shouldRender = true)"

to the InputRadioGroup component.

Full definition:

<InputRadioGroup @bind-Value="model.SelectedOption" Name="name" @onfocusin="@(() => shouldRender = true)">

</InputRadioGroup>

Note: The initial rendering of the component must always takes place. That means that the component will always rendered initally, whether the ShouldRender method returns true or false.

Note: Your model should not contain a list of options. It should contain a single int value pointing to the selected option.

  • Related