I have a custom element (MdTextField
that renders Material Web’s <md-filled-text-field>
) that acts similar to <input />
. Now the problem is, when using @bind-Value
, what Blazor does is setting its value
HTML attribute and not element (DOM) property. This does not work for the following case (please note I use input
here to demonstrate the behavior, the custom component acts exactly like this standard input
):
-
An input value is not updated when its
value
attribute is updated, for example:input.setAttribute("value", "new value");
. This happen after the input value is changed by users. -
Its value is changed if its property is set programmatically, for example:
input.value = "new value"
.
document.querySelector(".btn1").addEventListener("click", () => {
document.querySelector("input").setAttribute("value", "");
});
document.querySelector(".btn2").addEventListener("click", () => {
document.querySelector("input").value = "";
});
<p>Edit the input below and try the buttons</p>
<input value="123" />
<button class="btn1">Clear value</button>
<button class="btn2">Correctly clear value</button>
Update: I just realized simply changing the value of the input
and then click the button and it would not work at all. Basically the button doesn’t work if the input
was changed.
Here, when you click the Clear button, it works once, then if you change the <input>
again, the value
attribute doesn’t change so if you click the button the 2nd time it doesn’t work anymore. In fact, the value is of the input
is still the value that user entered.
In Javascript, I can simply use txt.value = ""
but it’s not simple in Blazor.
I can solve the issue if I can do either of these:
-
Somehow use
@bind="variable"
in Blazor forMdTextField
<md-filled-text-field>
or any custom tag like how it is working for standardinput
. -
Tell Blazor that
@bind-Value
should set thevalue
property and not attribute, similar to Lit’s Property Expressions:html`<my-list .listItems=${this.items}></my-list>`;
I know if all else fails, I can use Javascript to solve the problem but I’d rather avoid it if possible.
Here’s an example component:
@* MyTextField.razor *@
@* Following HH's answer: *@
<md-filled-text-field @bind-value="Value" @bind-value:event="onchange" @bind-value:after="OnAfterValueChanged">
@(ChildContent)
</md-filled-text-field>
@* My original code: *@
<md-filled-text-field value="@(Value)" @onchange="OnValueChanged">
@(ChildContent)
</md-filled-text-field>
@code {
[Parameter]
public RenderFragment? ChildContent { get; set; }
[Parameter, EditorRequired]
public string Value { get; set; } = "";
[Parameter]
public EventCallback<string> ValueChanged { get; set; }
async Task OnValueChanged(ChangeEventArgs e)
{
await ValueChanged.InvokeAsync(e.Value?.ToString() ?? "");
}
async Task OnAfterValueChanged()
{
await ValueChanged.InvokeAsync(Value);
}
}
And usage:
@page "/test"
<MyTextField @bind-Value="text" />
<button @onclick="@(() => text = "")">Clear</button>
@code {
string text = "123";
}
2
Answers
How about
I have include both an
<input>
and an<InputText>
, the latter should be the same as a<MdTextField>
.Not final, but. My
my-input
[a quick and dirty mock formd-filled-text-field
] isn’t working like it should, probably reflecting my rudimentary JS coding skills. Perhaps someone reading this will help me out?What it demonstrates is how to assign stuff in C# rather than Razor markup. It compiles and runs!
It’s in DotNet 8.
My
App
which adds the custom element.And then my
Home
: