dotnet / dotnet/aspnetcore

Blazor TextArea Not Updating

Open
#45,951 5 comments 1 reaction 0 assignees View on GitHub
area-blazor bug copilot-candidate feature-blazor-component-model Pillar: Technical Debt
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 5h
Merged PRs (30d)
276

Description

### Is there an existing issue for this?

- [X] I have searched the existing issues

### Describe the bug

If you have a , 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)
```
That code will initialize the 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 <input> element's value and onchange attributes ([change](https://developer.mozilla.org/docs/Web/API/HTMLElement/change_event)). The second <input> element in the following example is a concept demonstration and isn't meant to suggest how you should bind data in Razor components.
![image](https://user-images.githubusercontent.com/22459191/211207126-b56bef72-421a-4a3f-b0a0-367cd853378b.png)

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](https://github.com/Kevin-Andrew/BlazorTextAreaNotUpdating), 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](https://user-images.githubusercontent.com/22459191/211207595-354659d2-c33d-4c94-9c69-4ee1811cf4b1.png)
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)







@* If, however, we make the 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">*@

}

@code
{
private Dictionary cache = new Dictionary
{
{ "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](https://user-images.githubusercontent.com/22459191/211207707-1c61b969-5237-4fc8-b798-faf44c53f2f9.png)
and
![image](https://user-images.githubusercontent.com/22459191/211207723-53cd2b5c-eeea-442f-bd71-66358f0265db.png)

Then change the text in the of the first link:
![image](https://user-images.githubusercontent.com/22459191/211207932-2aa10cb6-2c09-43ca-9c4a-4083412995ba.png)

Finally, click on the "Method 2" link and notice that the text in the <textarea> is not updated:
![image](https://user-images.githubusercontent.com/22459191/211208137-39bfef35-b87d-42dd-af84-8e7937e5fa22.png)

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](https://user-images.githubusercontent.com/22459191/211208588-96b23dfd-4259-4742-b75a-424819f06116.png)

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:
https://github.com/dotnet/aspnetcore/issues/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)
```
versus being written as:
```

```

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_

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.