Элемент ввода не перерисовывается

Элемент ввода не отображается повторно, вариант использования заключается в том, что когда пользователь вводит некоторый текст, текущая дата заполняется в элемент ввода через атрибут «value». См. код ниже.

@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")
        };
    }
}

Если пользователь вводит тот же текст или любой текст еще раз, дата не отображается в элементе ввода во второй раз. Даже если для StateHasChanged() и ShouldRender установлено значение true, значение не отображается.

Таким образом, согласно механизму Blazor, элемент на самом деле не изменяется, поэтому дерево рендеринга не будет отображать входной элемент в DOM. Как я могу решить эту проблему и как я могу повторно отобразить элемент, который не изменился? Я подумал SateHasChanged() и ShouldRender обновить пользовательский интерфейс, разница в элементах его не волнует.

Для развертывания Сайтов с использованием Blazor, Angular и React с репозиторием на GitHub на Cloudflare
Для развертывания Сайтов с использованием Blazor, Angular и React с репозиторием на GitHub на Cloudflare
Как развернуть сайты с помощью Blazor, Angular и React с репозиторием на GitHub на Cloudflare.
0
0
51
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Возможно, это связано с оптимизацией рендеринга компонентов Blazor. У меня был тест с вашими кодами, и я также обнаружил, что исходное значение для TextData равно 12-12-2022, а затем, когда мы вводим data в поле ввода, TextData будет изменено на DataTime.Now, что равно 26-08-2024. В следующий раз, когда мы изменим входное значение, оно будет изменено на 26-08-2024, что на самом деле не меняет значение. Тогда, возможно, из-за некоторой оптимизации, даже если мы вызовем StateHasChanged(), страница не будет перерисовываться.

Мой обходной путь — использовать JS для прямого обновления DOM.

<input type = "text" id = "testInput" value = "@TextData" @onchange = "OnChange_Text" />

private void OnChange_Text(ChangeEventArgs e)
{
    TextData = GetValue(e.Value?.ToString());
    JS.InvokeVoidAsync("setInputValue", "testInput", TextData); 
}

И поместите js ниже сразу за <script src = "_framework/blazor.server.js"></script> в _Layout.cshtml, так как ваш тег — сервер blazor. Если у вас есть веб-приложение blazor, то это должно быть <script src = "_framework/blazor.web.js"></script>, которое есть в App.razor.

<script>
    window.setInputValue = (elementId, value) => {
        //alert(elementId);//for test purpose
        //alert(value);
        document.getElementById(elementId).value = value;
    };
</script>

Для этого вам не нужно прибегать к JS Interop.

MrC aka Shaun Curtis 26.08.2024 12:28

Да, я просто тестирую серверное приложение blazor и нахожу этот обходной путь, чтобы лучше понять, предпочитает ли кто-то еще метод JS. Спасибо за то, что поделились!

Tiny Wang 26.08.2024 12:47

В тот момент, когда вы начинаете изменять разделы DOM, контролируемые компонентами Blazor, реальный DOM и виртуальный DOM Blazor рассинхронизируются, и вы получаете всевозможные неожиданные результаты.

MrC aka Shaun Curtis 26.08.2024 16:12

Лично я бы избегал использования JS при работе с Blazor, если только вы действительно не можете найти альтернативное или жизнеспособное решение. Blazor уже предоставляет все, чтобы мы могли продолжать использовать C#.

Прежде всего, вам понадобится интерактивность для вашей страницы, иначе изменения не будут отображены (по крайней мере, для новых версий .NET и blazor). Это можно активировать с помощью следующей строки @rendermode InteractiveServer.

Почему бы вам не использовать стандартный компонент InputText? Здесь вы можете привязать значение к своему свойству и обновить его напрямую. Это также позволит Blazor узнать, что что-то изменилось. StateHasChanged следует вызывать только в случае необходимости, поскольку Blazor знает, когда произошли изменения (другая история с дочерними компонентами).

@page "/"
@rendermode InteractiveServer

<PageTitle>SO Test</PageTitle>

<label>Text Binding check:</label>
<InputText @bind-Value = "@TextData" />
<br />

<br />
<label>Text Dummy:</label>
<input type = "text" value = "dummy" />

@code
{
    private string? _text;

    private string? TextData
    {
        get => _text ?? "12-12-2022";
        set
        {
            _text = value 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")
                    };
        }
    }
}

Если вы не хотите иметь логику непосредственно в свойстве, то вы также можете передать ее методу и использовать соответствующие атрибуты в компоненте InputText (но, на мой взгляд, это выглядит немного менее привлекательно).

// removed for brevity
<label>Text Binding check:</label>
<InputText id = "name" Value = "@TextData" ValueChanged = "HandleValueChange" ValueExpression = "() => TextData"></InputText>
<br />

// removed for brevity

@code
{
    private string? TextData { get; set; } = "12-12-2022";

    private void HandleValueChange(string? obj)
    {
        TextData = obj 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")
                   };
    }
}
Ответ принят как подходящий

Я предполагаю, что это интерактивная страница.

Используйте @bind и @bind:after. Пусть привязка и обработчик событий пользовательского интерфейса сделают всю работу.

Обратный вызов привязки [для установки TextData] — это событие пользовательского интерфейса. Он устанавливает значение, а затем вызывает зарегистрированный метод after. Наконец, он вызывает StateHasChanged для рендеринга нового состояния.

Я изменил полученные даты и форматирование, чтобы было легче видеть разные результаты.

@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")
        };
    }
}

он же Шон Спасибо за решение. Но почему мой код не работал так, как ожидалось? Привязка также устанавливает значение свойства в атрибут «значение» и повторно отображает элемент, верно?

Naveen 27.08.2024 02:52
private **async** void OnChange_Text(ChangeEventArgs e)
{
    TextData = GetValue(e.Value?.ToString());

    **await** InvokeAsync(StateHasChanged);
}

Конечно, это не способ достичь той функциональности, на которую вы смотрите. Просто используйте @bind-Value и @bind-Value:after

Другие вопросы по теме