Я вижу этот общий шаблон в некоторых библиотеках (MatBlazor, Telerik) наличия свойств ValueChanged
и ValueExpression
, и это меня действительно смущает.
В чем разница между ними? А когда его использовать?
Я вижу этот общий шаблон в некоторых библиотеках (MatBlazor, Telerik) наличия свойств ValueChanged
и ValueExpression
, и это меня действительно смущает.
В чем разница между ними? А когда его использовать?
Я хотел бы добавить несколько вариантов использования для ValueChanged
и ValueExpression
,
Прежде всего, как сказал enet, эти свойства больше похожи на триединство свойств, в которых у вас есть Foo
, FooChanged
и FooExpression
, и они используются в двусторонней привязке данных, например. @bind-Foo="SomeProperty"
.
Чтобы создать пользовательский компонент со свойством, которое может использоваться с @bind-
, вам необходимо предоставить эти 3 свойства (только если Foo
и FooChanged
также работают) как [Parameter]
и вызывать FooChanged
при изменении свойства внутри вашего пользовательского компонента.
например из enet
[Parameter]
public TValue Foo
{
get => text
set
{
if (text != value) {
text = value;
if (FooChanged.HasDelegate)
{
FooChanged.InvokeAsync(value);
}
}
}
}
[Parameter]
public EventCallback<TValue> FooChanged { get; set; }
[Parameter]
public Expression<Func<TValue>> FooExpression { get; set; }
Добавление @bind-Foo
будет таким же, как передача Value
и ValueChanged
, с той лишь разницей, что @bind-
установит только свойство, но если вы добавите свой собственный ValueChanged
, вы можете делать все, что захотите (проверка, изменение значения для установки и т. Д.) .
Случаи применения
@bind-
Если у вас есть компонент, у которого уже есть @bind-Foo
, и вы хотите создать компонент поверх него и по-прежнему передавать как параметр @bind-Foo
, у вас может быть только одно свойство и перейти к @bind-Foo
, вам необходимо передать свойства Foo
, FooChanged
и / или FooExpression
.
e.g.
CustomInputWrapper.razor
<div>
<p>My custom input wrapper</p>
@* If you pass @bind-Value it won't work*@
@* You need to pass the properties that are used in the bind*@
<InputText Text="@Value" TextChanged="@ValueChanged" TextExpression="@ValueExpression" />
</div>
@code {
[Parameter]
public virtual string Value { get; set; }
[Parameter]
public EventCallback<string > ValueChanged { get; set; }
[Parameter]
public Expression<Func<string >> ValueExpression { get; set; }
}
Такая ситуация обертывания другого компонента будет происходить часто, если вы создаете много настраиваемых компонентов или не хотите напрямую использовать какой-либо сторонний компонент.
Пример моего проекта: в моем проекте я использую MatBlazor и Telerik, но не все компоненты в обеих библиотеках полностью стабильны, поэтому я создал оболочку для всех компонентов и однажды, когда одна из этих библиотек полностью станет стабильный, я перейду на использование только одной библиотеки. Это позволяет мне иметь свои пользовательские компоненты, и если я хочу изменить один, я изменяю только одну вещь в моем пользовательском компоненте и изменяю все приложение.
Если вы хотите иметь значение по умолчанию внутри настраиваемого компонента, вы «можете» просто передать значение по умолчанию в свойство.
[Parameter]
public virtual DateTime Value { get; set; } = new DateTime(/* some default value*/);
Но это большая проблема, если вы используете этот компонент внутри формы.
Почему? Потому что вы измените значение только внутри своего компонента, но если свойство передается в @bind-Value
, оно не изменится.
Чтобы добавить это значение по умолчанию и заставить его работать при двусторонней привязке данных, вам нужно вызвать ValueChanged
и передать значение по умолчанию. Это приведет к тому, что ваш компонент будет иметь значение по умолчанию, а также изменит любое свойство в @bind-Value
на значение по умолчанию.
e.g.
// Lifecycle after all parameters are set
protected override void OnParametersSet()
{
// Check if the ValueChanged is set
if (ValueChanged.HasDelegate)
{
ValueChanged.InvokeAsync(DateTime.Now);
}
}
FooExpression
Если у вас есть тип, допускающий значение NULL, например int?
, иногда, когда значение равно null
, он не может знать его тип, поэтому вам нужно передать FooExpression
, чтобы он мог получить тип путем отражения. Вот пример, в котором вам нужно его использовать.
Вариант использования этих свойств будет использоваться чаще, если вы создаете настраиваемые компоненты и должны работать с привязанным свойством или изменять способ работы привязки.
Если вы используете только уже изготовленные компоненты, то в редких случаях вам придется их использовать.
Фактически, вы забыли третий элемент этого шаблона: Value
. Эта троица свойств часто используется для двусторонней привязки данных компонентов. Примечательно, что эти свойства используются во встроенных компонентах формы Blazor, таких как <InputText>
.
Давайте посмотрим на пример:
<InputText @bind-Value="employee.FirstName" />
Value
- свойство, предоставленное в форме @bind-Value="model.PropertyName"
.
ValueChanged
относится к типу EventCallback<TValue>
. Он обозначает обратный вызов, который обновляет связанное значение. Как видите, в приведенном выше примере мы не используем его - в этом нет необходимости. Компилятор знает свое дело и заботится об этом, а это означает, что он добавляет EventCallback
делегата со всеми необходимыми настройками за вашей спиной.
ValueExpression
, наконец, относится к выражению, которое определяет связанное значение. Он автоматически создается компилятором, и вам редко, если вообще когда-либо, нужно его устанавливать.
Теперь давайте сравним приведенный выше код с приведенным ниже кодом. В следующем примере создается двусторонняя привязка данных между родительским компонентом и дочерним компонентом. Однако вместо использования стандартной троицы (Value
, ValueChanged
, ValueExpression
) мы воспроизведем базовый шаблон для себя:
ParentComponent.razor:
<ChildComponent @bind-Text="FirstName" />
@code {
[Parameter]
public string FirstName { get; set; }
}
ChildComponent.razor:
<input @bind="Text" />
@code {
private string text;
[Parameter]
public string Text
{
get { return text; }
set
{
if (text != value) {
text = value;
if (TextChanged.HasDelegate)
{
TextChanged.InvokeAsync(value);
}
}
}
}
[Parameter]
public EventCallback<string> TextChanged { get; set; }
}
Встроенный <InputText>
и наш пользовательский <ChildComponent>
в основном одинаковы!
Чтобы ответить на ваш другой вопрос ...
Когда я буду использовать
ValueChanged
иValueExpression
в Blazor ?? Я создаю оболочку ввода из другой библиотеки, это случай использования этой троицы?
Как объяснялось выше, ValueChanged
и ValueExpression
- это свойства, определенные во встроенных компонентах Blazor, и в большинстве случаев вам не нужно использовать их напрямую.
Посмотрите еще раз на два компонента, которые я определил выше: <ParentComponent>
и <ChildComponent>
. Измените Text
и TextChanged
на Value
и ValueChanged
, и мои компоненты по-прежнему действительны и работают правильно. Единственное различие заключается в названии. Что мне делать в <ChildComponent>
? Я определяю свойство параметра с именем Text
(обозначает Value
). Поскольку я хочу включить двустороннюю привязку данных между родительским и дочерним компонентами, мне также необходимо определить свойство параметра, называемое здесь TextChanged
(обозначает ValueChanged
). Text
переходит к TextChanged
, Value
переходит к ValueChanged
, а Year
переходит к YearChanged
. Именование носит условный характер. Суть в том, что вы должны определить свойство и EventCallback
того же типа данных, что и свойство.
Внутри родительского компонента я предоставляю свойство следующим образом:
<ChildComponent @bind-Text="NameOfAPropertyDefinedInTheParentComponent" />
or <ChildComponent @bind-Value="NameOfAPropertyDefinedInTheParentComponent" />
or <ChildComponent @bind-Year="NameOfAPropertyDefinedInTheParentComponent" />
В моих компонентах выше также есть код, как, например, в дочернем компоненте, который вызывает делегат TextChanged
, чтобы передать значение обратно родительскому компоненту; именно это делает ValueChanged
делегат в компонентах, в которых он определен. Но вам как пользователю не обязательно его использовать. Посмотрите на мои компоненты ... Они отлично работают. Не нужно трогать. Если вы, как пользователь моего компонента, хотите создать его подкласс, вам необходимо знать, что вы делаете и как правильно создать подкласс компонента Blazor. Но мои компоненты, частично представленные здесь, относительно просты.
Предположим, вы хотите создать ввод пароля на основе <InputText>
, что не только выполнимо, но и довольно просто. В этом случае вы не собираетесь ничего менять, кроме внешнего вида компонента <InputText>
, чтобы вместо обычного текста отображались символы звездочки. В остальном компонент без изменений. Вам не нужно обрабатывать события и тому подобное. Это, конечно, не означает, что автору компонента никогда не нужно будет вызывать EventCallback
откуда-то в своем коде. Тем не менее, у меня никогда не было веской причины запускать делегат ValueChanged
при использовании компонента <InputText>
. И мне только один раз пришлось указать ValueExpression
, так как компилятор не смог определить связанное значение. (Поищу, а если найду, выложу сюда ...)
ValueChanged
и ValueExpression
в Blazor ?? Я создаю оболочку ввода из другой библиотеки, это случай использования этой троицы?
- person Vencovsky; 12.03.2020
Value
и ValueChanged
. Если вы используете функциональность, которая зависит от идентификации связанного свойства (например, реализация проверки). Тогда вам также нужно использовать ValueExpression
. Например, вы можете создать FieldIdentifier
, используя FieldIdentifier.Create(ValueExpression)
. - Надеюсь это поможет.
- person LuckyLikey; 16.02.2021
ValueChanged
- это событие двунаправленной привязки данных,ValueExpression
- это выражение Linq для привязанного свойства. - person agua from mars   schedule 12.03.2020