Как заставить двухсторонние привязки данных работать в blazor?

#blazor #blazor-server-side

#blazor #blazor-на стороне сервера

Вопрос:

У меня есть следующий компонент, который напоминает TextBox компонент. Однако по какой-то причине содержимое Value , похоже, никогда не меняется.

Я бы ожидал, что это будет работать с привязкой данных без ручного JavaScript, нет?

 
<div class="form-group">
    <input type="text" class="form-control" @bind="Value" @onkeyup="OnKeyUp" placeholder="@Placeholder"/>
</div>

@code {
    
    [Parameter]
    public string Value { get; set; }

    [Parameter]
    public EventCallback<string> ValueChanged { get; set; }

    [Parameter]
    public string Placeholder { get; set; }

    private void OnKeyUp(KeyboardEventArgs obj)
    {
        ValueChanged.InvokeAsync(Value);
    }

}
  

Ответ №1:

Вы не должны привязываться к a [Parameter] , поскольку это может вызвать побочные эффекты (см. Комментарий Стива Сандерсона здесь )

Похоже, вы хотите двустороннюю привязку к каждому изменению, для чего я бы использовал oninput вместо onkeyup , но вы можете использовать onkeyup , если это действительно то, что вам нужно.

Главное — привязаться к локальному и никогда не изменять общедоступный параметр вручную:

 <div class="form-group">
    <input type="text" class="form-control" 
           @bind-value=@InternalValue 
           @bind-value:event="oninput" 
           placeholder="@Placeholder"/>
</div>

@code {

    string InternalValue
    {
        get => Value;
        set 
        {
            if (value!=Value)
            {
                ValueChanged.InvokeAsync(value);        
            }
        }
    }

    [Parameter]
    public string Value { get; set; }

    [Parameter]
    public EventCallback<string> ValueChanged { get; set; }

    [Parameter]
    public string Placeholder { get; set; }
}
  

Редактировать: добавление заметок Стива Сандерсона на случай, если они исчезнут…

Цитата Стива Сандерсона по проблеме GitHub, связанной выше:

Хорошо, я посмотрел более подробно и посмотрел, что происходит. Ниже приведено объяснение, но прежде чем перейти к этому, я повторю свое утверждение о том, что основная проблема заключается в том, что дочерний компонент перезаписывает свое собственное значение свойства [Parameter] . Избегая этого, вы можете избежать этой проблемы.

В этом случае и в других Blazor полагается на безопасное назначение параметров от родителя к потомку (не перезаписывает информацию, которую вы хотите сохранить) и не имеет побочных эффектов, таких как запуск большего количества рендеров (потому что тогда вы можете оказаться в бесконечном цикле). Вероятно, нам следует усилить руководство в документах, а не просто сказать «не записывайте свои собственные параметры», а расширить его, чтобы предостеречь от побочных эффектов в таких установщиках свойств. Использование Blazor свойств C # для представления канала связи между родительскими и дочерними компонентами довольно удобно для разработчиков и хорошо смотрится в исходном коде, но возможность побочных эффектов проблематична. У нас уже есть анализатор времени компиляции, который предупреждает, если у вас нет правильных модификаторов доступности для свойств [Parameter] — возможно, нам следует расширить его, чтобы выдавать предупреждения / ошибки, если параметр является чем-то иным, кроме простого свойства { get; set; } auto .

Итак, в этом случае решение довольно простое: замените свойство значения Component1 на простое { get; set; } , и вместо того, чтобы пытаться записать в него, пусть Component1 реагирует на нажатия кнопок, вызывая valueChanged . Тогда у вас нет никакого рекурсивного цикла рендеринга, и перезапись данных не происходит.

 private void DecrementValue()
{
    //Value--; <-- Don't do this

    // Do this instead:
    ValueChanged.InvokeAsync(Value - 1);
}
  

Комментарии:

1. Начиная с .NET 7, вы получите предупреждение компилятора BL0007, если параметр компонента не является свойством auto. Решение в этом ответе помогло мне избавиться от этого предупреждения, поскольку свойство internalValue не является параметром.