The input element is not re-rendered, the use case is that when the user enters some text the current date is filled into the input element via the 'value
' attribute. See the code below.
@page "/"
<label>Text Binding check:</label>
<input type="text" value="@TextData" @onchange="OnChange_Text" />
<br />
<br />
<label>Text Dummy:</label>
<input type="text" value="dummy" />
@code
{
public string? TextData { get; set; } = "12-12-2022";
protected override bool ShouldRender()
{
return true;
}
private void OnChange_Text(ChangeEventArgs e)
{
TextData = GetValue(e.Value?.ToString());
InvokeAsync(() => StateHasChanged());
}
private string GetValue(string? strValue)
{
return strValue switch
{
"test" => DateTime.Now.ToString("dd-MM-yyyy"),
"data" => DateTime.Now.ToString("dd-MM-yyyy"),
"nash" => DateTime.Now.ToString("dd-MM-yyyy"),
_ => DateTime.Now.ToString("dd-MM-yyyy")
};
}
}
If the user enters the same text or any text again, the date is not rendered in the input element the second time. Even with StateHasChanged()
and ShouldRender
set to true, the value is not rendered.
So as per the Blazor mechanism the element is not really changed so the render tree will not render the input element to DOM. How can I solve this problem and how can I re-render the element that has not changed?. I thought SateHasChanged()
and ShouldRender
refresh the UI, it doesn't care about the element difference.
I'm assuming this is an Interactive page.
Use @bind
and @bind:after
. Let binding and the UI event handler do the work.
The bind callback [to set TextData
] is a UI event. It sets the value and then calls the registered after
method. Finally it calls StateHasChanged
to render the new state.
I've modified the dates produced and formatting so it's easier to see the different results.
@page "/"
<label>Text Binding check:</label>
<input type="text" @bind="TextData" @bind:after="OnChange_Text" />
<br />
<br />
<label>Text Dummy:</label>
<input type="text" value="@EnteredValue" />
@code
{
public string? TextData { get; set; } = "12-Dec-2022";
public string? EnteredValue;
private void OnChange_Text()
{
this.EnteredValue = this.TextData;
this.TextData = GetValue(this.TextData);
}
private string GetValue(string? strValue)
{
return strValue switch
{
"test" => DateTime.Now.AddMonths(2).ToString("dd-MMM-yyyy"),
"data" => DateTime.Now.AddMonths(1).ToString("dd-MMM-yyyy"),
"nash" => DateTime.Now.ToString("dd-MMM-yyyy"),
_ => DateTime.Now.AddMonths(-1).ToString("dd-MMM-yyyy")
};
}
}
It's complicated, but your problem is caused by synchronisation issues between the various DOMs when you modify the actual DOM in an edit element.
First Change
Starting state:
When you change the Actual DOM to "Fred"
When the onChange UI event completes:
After the UI event render:
At the completion of the render the Renderer runs the diffing engine, detects the difference and send the updates to the actual DOM.
Second Change
Now we edit the input to "Fred" again.
When the onChange UI event completes:
After the UI event render:
At the completion of the render the Renderer runs the diffing engine, detects no difference and does nothing. The actual DOM isn't updated.
You can use my solution, or add a workaround to your code by changing to this OnChange_Text
:
private async Task OnChange_Text(ChangeEventArgs e)
{
// Set to nonsense value so the diffing engine sees the change
TextData = " ";
// yield and let the UI event handler render the component
await Task.Yield();
// set to the nee value
TextData = GetValue(e.Value?.ToString());
// final UI event handler render will happen here
}