asp.net-coreblazortextfieldfluent-ui

FluentUI Blazor TextField doesn't update current-value correctly


I'm using the FluentUI Blazor FluentTextField component, where I implement the ValueChanged EventCallback to apply some changes to the input value. When I apply changes to the input value, I want the displayed value to reflect these changes, so I update the variable that is bound to the Value parameter. This works fine for the first time, but when the input is changed again and my modifications result in the same value, the displayed value is not updated correctly.

<FluentTextField Value="@_textValue" ValueChanged="@TextValueChanged"/>

@code {
    string? _textValue = null;

    public void TextValueChanged(string? input)
    {
       // Apply some modifications to input...
       
       // Update _textValue
       _textValue = input;

       // The displayed value in the text box should now reflect the changes that I made to the input value
       // This works fine the first time a user inputs something.
       // But if the user inputs something again, that results in the same _textValue after the modifications, the displayed value is not updated.
    }
}

I've tried calling StateHasChanged() and InvokeAsync(StateHasChanged) after I set the _textValue, but that didn't change anything.

Here is some working code-snippet to demonstrate and reproduce the behaviour:

<h5>Field with max length @_maxLength</h5>
<FluentTextField Value="@_textValue" ValueChanged="@TextValueChanged"></FluentTextField>
<p>You entered: @_textValue</p>

@code {
    int _maxLength = 4;
    string? _textValue = null;

    public void TextValueChanged(string? newValue)
    {
        if(newValue is not null && newValue.Length > _maxLength)
        {
            _textValue = newValue.Substring(0, _maxLength);
        }
        else
        {
            _textValue = newValue;
        }
    }
}

If you type "abcdef" and enter, _textValue is set to "abcd" and the text field displayes the value "abcd" as expected. If you now type "gh" and enter again, the _textValue is still set to "abcd", but the current-value, that is displayed in the text field is "abcdgh".

NOTE: As pointed out in this answer https://stackoverflow.com/a/79521735/23529199, in the example the MaxLength attribute could be used to achive the desired behaviour. This does not work for my use-case. I used the max length only as example to demonstrate the behaviour in a much simpler scenario than my actual code.


Solution

  • You can use the MaxLength attribute to limit user input, so the text box only allows entering 4 characters.

    @page "/text"
    @rendermode InteractiveServer
    <h5>Field with max length @_maxLength</h5>
    <FluentTextField Value="@_textValue" Maxlength="@_maxLength" ValueChanged="@TextValueChanged"></FluentTextField>
    <p>You entered: @_textValue</p>
    <FluentTextField @bind-Value="@value2" Maxlength="@_maxLength" Label="Maxlength"></FluentTextField>
    <p>You entered: @value2</p>
    @code {
        int _maxLength = 4;
        string? _textValue = null;
        string? value2 = null;
        public void TextValueChanged(string? newValue)
        {
            if (newValue is not null && newValue.Length > 4)
            {
                _textValue = newValue.Substring(0, 4);
            }
            else
            {
                _textValue = newValue;
            }
        }
    }
    

    More detail information, see Fluent UI Text field.

    Update:

    If you type "abcdef" and enter, _textValue is set to "abcd" and the text field displayes the value "abcd" as expected. If you now type "gh" and enter again, the _textValue is still set to "abcd", but the current-value, that is displayed in the text field is "abcdgh".

    Using your code, I was able to reproduce this issue. If I replace the FluentTextField with the ASP.NET Core InputText component, the issue does not occur. Therefore, this issue might be a bug in FluentUI's TextField. I suggest you report it to Fluent UI.

    To achieve the result you want, you can try to use the InputText component as below:

    <InputText Value="@textValue" ValueChanged="OnValueChanged" ValueExpression="@(() => textValue)" class="form-control" />
    
    <p>Current Value: @textValue</p>
    @code { 
        private string textValue = string.Empty;
    
        public void OnValueChanged(string newvalue)
        {
            if (newvalue is not null && newvalue.Length > 4)
            {
    
                textValue = newvalue.Substring(0, 4).ToString();
                Console.WriteLine($"Text changed to: {textValue}");
            }
            else
            {
                textValue = newvalue;
            }
        }
    }
    

    Or, you can call a JavaScript function to update the text field's value in the ValueChanged event:

        @page "/text"
        @rendermode InteractiveServer
        @inject IJSRuntime JSRuntime
    
        <h5>Field with max length @_maxLength</h5>
        <FluentTextField Value="@_textValue" ValueChanged="TextValueChanged" Id="fluenttext"  ValueExpression="@(() => _textValue)"></FluentTextField>
        <p>You entered: @_textValue</p>
    
        @code {
            int _maxLength = 4;
            private string? _textValue = string.Empty; 
            public async void TextValueChanged(string? newValue)
            {  
                if (newValue is not null && newValue.Length > 4)
                {
                    _textValue = newValue.Substring(0, 4);
                    newValue =textValue;
                }
                else
                {
                    _textValue = newValue;
                } 
                //call a Javascript function to change the text field value.
                await JSRuntime.InvokeVoidAsync("changetextvalue", "fluenttext", _textValue); 
            }
    
        }
    

    And add the changetextvalue function in App.Razor page:

    <body>
        <Routes />
        <script src="_framework/blazor.web.js"></script>
        <script>
            window.changetextvalue = (elementId,value) => {
                var element = document.getElementById(elementId);
                if (element) {
                    element.value = value; 
                }
            };
        </script>
    </body>