Skip to content

Blazor TextArea Not Updating #45951

Open
Open
@Kevin-Andrew

Description

@Kevin-Andrew

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

If you have a <textarea>, which needs to get its data from a method, not a bindable property, and we also want to save any changes made by the user to the text in that <textarea>, one may think to write the following code:

<textarea rows="3" style="width: 350px;" @onchange="Text_OnChange">@GetData(someParameter)</textarea>

That code will initialize the <textarea> with the string from the GetData(someParameter) method, which is placed between the opening and closing tag of the <textarea>. This is the recommended practice for initializing a <textarea> in HTML. What I mean by that is, according to https://www.w3schools.com/tags/tag_textarea.asp, a <textarea> does not, for example, have/support a value attribute like other <input> elements.

Then, because we also want to get any changes that the user may make to the text, we wire up the onchange event. This should conceptually and effectively give us two-way binding of the <textarea>. In the first example of the following documentation, https://learn.microsoft.com/en-us/aspnet/core/blazor/components/data-binding?view=aspnetcore-6.0, it says the same thing:

As a demonstration of how data binding composes in HTML, the following example binds the InputValue property to the second element's value and onchange attributes (change). The second element in the following example is a concept demonstration and isn't meant to suggest how you should bind data in Razor components.
image

Again, the reason why we don't simply just use a @bind attribute on the <textarea> is because we don't have a bindable property. The data is coming from a method.

Initially, this code appears to work. However, in the following complete example, it does not work as expected. You can download the example from the repo, but I'll also outline and include most of the details and code directly here too for others to quickly see and read.

Create a new solution/project using either the Blazor WebAssembly App or Blazor Server App project templates:
image
In the Index.razor page, replace the code with the following:

@page "/"

<PageTitle>Index</PageTitle>

<h1>Instructions</h1>
<br/>
<ol>
    <li>Click one of the links below. A textarea and text box will be rendered with some initial text.</li>
    <li>Click back and forth between each link. Notice that both textarea and text box will be populated
        with, and change between, the initial/default values assigned to each link.
    </li>
    <li>Click the Method 1 link, then change the value of the text in the <b>textarea</b>.</li>
    <li>Then click the Method 2 link. Notice that the text in the textarea did not get updated/changed
        to the Method 2 text, as the text box did. This doesn't seem like correct behavior.
    </li>
</ol>

<a href="./?method=method1">Method 1</a>
<br />
<a href="./?method=method2">Method 2</a>

@if (!String.IsNullOrWhiteSpace(Method))
{
    <form>
        @* In HTML, according to https://www.w3schools.com/tags/tag_textarea.asp, the text that is to be
        displayed in the textarea should be placed between the opening and closing <textarea> tags. *@
        <textarea rows="3" style="width: 350px;" @onchange="Text_OnChange">@GetData(Method)</textarea>
        <br />
        <input type="text" style="width: 350px;" value="@GetData(Method)" @onchange="Text_OnChange" />
        <br/>
        <br/>
        @* If, however, we make the <textarea> similar to the <input type="text"> by specifying a 'value'
        attribute, then the behavior matches that of the <input type="text">, as desired. However, according
        to https://www.w3schools.com/tags/tag_textarea.asp, the <textarea> doesn't have/support a 'value'
        attribute.
        (Also, while this workaround happens to work in this simple sample, I have another project where
        this workaround doesn't fix the problem.) *@
        @* Uncomment the following code to text this workaround. *@
        @*<textarea rows="3" style="width: 350px;" value="@GetData(Method)" @onchange="Text_OnChange"></textarea>*@
    </form>
}

@code
{
    private Dictionary<string, string> cache = new Dictionary<string, string>
    {
        { "method1", "Initial value for method 1" },
        { "method2", "Method 2 initial value." },
    };

    [Parameter, SupplyParameterFromQuery(Name = "method")]
    public string? Method { get; set; }

    protected override Task OnInitializedAsync()
    {
        return base.OnInitializedAsync();
    }

    protected override void OnParametersSet()
    {
        base.OnParametersSet();
    }

    private void Text_OnChange(ChangeEventArgs e)
    {
        Console.WriteLine($"Text_OnChange: {e.Value}");

        if (Method != null && cache.ContainsKey(Method))
        {
            cache[Method] = (string)e.Value!;
        }
    }

    private string GetData(string key)
    {
        Console.WriteLine($"Data for {key} is: {cache[key]}");
        return cache[key];
    }
}

Run/debug the app and follow the instructions by clicking on both links to see the initial data:
image
and
image

Then change the text in the <textarea> of the first link:
image

Finally, click on the "Method 2" link and notice that the text in the <textarea> is not updated:
image

As you see in the example, I have also include an <input type="text"> element. With an <input type="text"> element, the proper way to set/initialize the text value is to set the value attribute. So we can accomplish what we need to do, that is, conceptually/effectively perform two-way binding by setting the value attribute to the result of the GetData(someParameter) method and wiring up and handling the @onchange event.

If you noticed in the last screen shot, the text in the regular text box was correctly updated. So why wasn't the <textarea>? We can take it a step further and have the user change the text in the regular text box too, then click back and forth between the two links and the text in the text box is always updated correctly:
image

So why is the behavior between those to, very similar elements, different?

Doing more tests on my own, I also discovered that if you change the <textarea> to instead use/have a value attribute, which again, according to the HTML specification is not a valid attribute, then the behavior is as expected and matches that of the regular <input type="text"> element. At least, in this simple example, that workaround yields the correct/desired behavior. However, in my real project, that workaround still doesn't give the correct/expected behavior.

I have found and read the following:
#45797
I have also read the following, that is new in .NET 7.
https://learn.microsoft.com/en-us/aspnet/core/blazor/components/data-binding?view=aspnetcore-7.0#use-bindgetbindset-modifiers-and-avoid-event-handlers-for-two-way-data-binding
But in my example, I'm not trying to do. Specifically, I am not attempting to modify the value that the user enters and expect Blazor to render that modified value.

I have not tried the new @bind:get/@bind:set modifiers available in .NET 7 to know whether or not it would fix my problem. But if the new @bind:get only supports binding to a C# property, then no, that won't solve my problem because I need to pass in a parameter to a method.

Expected Behavior

Having said all of that, I have worked around all of the issues, but I wanted to report this nonetheless and at least try to get an answer to the following:
Why, in the provided example code, is there a behavior difference between a <textarea> that is written as:

<textarea rows="3" style="width: 350px;" @onchange="Text_OnChange">@GetData(Method)</textarea>

versus being written as:

<textarea rows="3" style="width: 350px;" value="@GetData(Method)" @onchange="Text_OnChange"></textarea>

To me, the second one, that uses the value attribute, shouldn't even be allowed. But at a minimum, isn't it a bug that those two behave differently?

Steps To Reproduce

https://github.com/Kevin-Andrew/BlazorTextAreaNotUpdating

Exceptions (if any)

No response

.NET Version

.NET 6

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Pillar: Technical Debtarea-blazorIncludes: Blazor, Razor ComponentsbugThis issue describes a behavior which is not expected - a bug.feature-blazor-component-modelAny feature that affects the component model for Blazor (Parameters, Rendering, Lifecycle, etc)

    Type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions