Bolero
Bolero copied to clipboard
Question: How to do parent-to-child binding
Hi.
First, thanks for this great library. Its working great for me in the most case.
However, I'm having a problem for creating a form with validation.
I've been using MatBlazor
and it supports data validation through EditForm
The following code works in Blazor. And I want to make an equivalent in Bolero.
<EditForm Model="myModel" OnValidSubmit="Success">
<DataAnnotationsValidator />
<MatTextField Label="Username" @bind-Value="myModel.Username" />
<MatTextField Label="Password" @bind-Value="myModel.Password" Type="password" />
<ValidationSummary />
</EditForm>
@code {
LoginModel myModel = new LoginModel();
public class LoginModel
{
[Required]
public string Username { get; set; }
[Required]
[MinLength(8)]
public string Password { get; set; }
}
}
What I have come up with is
type Model = {
[<Required>]
UserId: string
[<Required>]
[<MinLength(8)>]
Password: string
}
type Msg =
| UserIdInput of string
| PasswordInput of string
let init = {
UserId = ""
Password = ""
}
let update msg model =
match msg with
| UserIdInput s -> { model with UserId = s }
| PasswordInput s -> { model with Password = s }
let view model dispatch =
let editFormInner context =
comp<CascadingValue<EditContext>> ["Value" => context] [
comp<MatTextField<string>> ["Label" => "UserID"; (* How to do `@bind-Value` here? *)] []
comp<MatTextField<string>> ["Label" => "Password"; "Type" => "Password"; (* And here? *)] []
comp<ValidationSummary> [] []
]
comp<EditForm> [attr.fragmentWith "ChildContent" (fun (e: EditContext) -> editFormInner e)
"Model" => model] [
]
How should I bind one of the field of parent model value to comp<MatTextField<string>>
?
First I have tried
bind.input.string model.UserId (UserIdInput >> dispatch)
But this throws an error saying An unhandled exception was thrown by the application. System.InvalidOperationException: Microsoft.AspNetCore.Components.Forms.InputText requires a value for the 'ValueExpression' parameter. Normally this is provided automatically when using 'bind-Value'.
It seems to me the problem is that Bolero has no equivalent to @bind-{PROPERTY}
in the Blazor.
Am I missing something? If not, is there a future plan to support @bind-{PROPERTY}
(or Form Validation) in Bolero?
Indeed this type of bind
is not supported currently.
If I understand correctly, bind-Value
actually creates two attributes Value
and ValueChanged
, with a third one ValueExpression
if the component has a parameter with this name. This third one is a bit trickier to generate than the other two because it's a Linq Expression
, but it should be doable.
@Tarmil I need that to. Is it a complex enchancement? How to implement that?
@Tarmil I implemented this:
[<System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)>]
let inline binderExpression< ^T, ^B, ^O when ^B : (static member FormatValue : ^T * CultureInfo -> ^O)>
(valueAttribute: string) (valueExpression: Quotations.Expr<Func< ^T>>) (callback: ^T -> unit) cultureInfo =
let valueExpression = valueExpression |> LeafExpressionConverter.QuotationToLambdaExpression
let valueFunction = valueExpression.Compile()
Attr(fun receiver builder sequence ->
let value = valueFunction.Invoke()
builder.AddAttribute(sequence, valueAttribute, (^B : (static member FormatValue : ^T * CultureInfo -> ^O)(value, cultureInfo)))
builder.AddAttribute(sequence + 1, valueAttribute + "Changed", EventCallback.Factory.Create(receiver, Action<'T>(callback)))
builder.AddAttribute(sequence + 2, valueAttribute + "Expression", valueExpression)
sequence + 3)
[<RequireQualifiedAccess>]
module Input =
let inline bool value callback = binder<bool, BindConverter, bool> "Value" value callback null
but I have an error as below. Do you have any ideas why?
System.ArgumentNullException: Value cannot be null. (Parameter 'obj')
at System.OrdinalCaseSensitiveComparer.GetHashCode(String obj)
at Microsoft.AspNetCore.Components.Forms.FieldIdentifier.GetHashCode()
at System.Collections.Generic.Dictionary`2[[Microsoft.AspNetCore.Components.Forms.FieldIdentifier, Microsoft.AspNetCore.Components.Forms, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[Microsoft.AspNetCore.Components.Forms.FieldState, Microsoft.AspNetCore.Components.Forms, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].FindValue(FieldIdentifier key)
at System.Collections.Generic.Dictionary`2[[Microsoft.AspNetCore.Components.Forms.FieldIdentifier, Microsoft.AspNetCore.Components.Forms, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[Microsoft.AspNetCore.Components.Forms.FieldState, Microsoft.AspNetCore.Components.Forms, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].TryGetValue(FieldIdentifier key, FieldState& value)
at Microsoft.AspNetCore.Components.Forms.EditContext.GetValidationMessages(FieldIdentifier fieldIdentifier)+MoveNext()
at System.Linq.Enumerable.<Any>g__WithEnumerator|8_0[String](IEnumerable`1 source)
at System.Linq.Enumerable.Any[String](IEnumerable`1 source)
at Microsoft.FluentUI.AspNetCore.Components.FluentInputBase`1[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].UpdateAdditionalValidationAttributes() in /_/src/Core/Components/Base/FluentInputBase.cs:line 353
at Microsoft.FluentUI.AspNetCore.Components.FluentInputBase`1[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnValidateStateChanged(Object sender, ValidationStateChangedEventArgs eventArgs) in /_/src/Core/Components/Base/FluentInputBase.cs:line 340
at Microsoft.AspNetCore.Components.Forms.EditContext.NotifyValidationStateChanged()
at Microsoft.AspNetCore.Components.Forms.EditContextDataAnnotationsExtensions.DataAnnotationsEventSubscriptions.OnFieldChanged(Object sender, FieldChangedEventArgs eventArgs)
at Microsoft.AspNetCore.Components.Forms.EditContext.NotifyFieldChanged(FieldIdentifier& fieldIdentifier)
at Microsoft.FluentUI.AspNetCore.Components.FluentInputBase`1.<SetCurrentValue>d__74[[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MoveNext() in /_/src/Core/Components/Base/FluentInputBase.cs:line 150
at Microsoft.FluentUI.AspNetCore.Components.FluentInputBase`1.<ChangeHandlerAsync>d__108[[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MoveNext() in /_/src/Core/Components/Base/FluentInputBaseHandlers.cs:line 34
at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)
fixed
This is what I came to for Fluent components
/// Two-way binding for HTML input elements.
module SkillGro.Bolero.Html.Bind
open Bolero
open System
open System.Globalization
open Microsoft.AspNetCore.Components
open Microsoft.AspNetCore.Components.Forms
open Microsoft.FSharp.Linq.RuntimeHelpers
/// <exclude />
[<System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)>]
let inline binder< ^T, ^B, ^O when ^B : (static member FormatValue : ^T * CultureInfo -> ^O)>
(valueAttribute: string) (value: ^T) (callback: ^T -> unit) cultureInfo =
Attr(fun receiver builder sequence ->
builder.AddAttribute(sequence, valueAttribute, (^B : (static member FormatValue : ^T * CultureInfo -> ^O)(value, cultureInfo)))
builder.AddAttribute(sequence + 1, valueAttribute + "Changed", EventCallback.Factory.Create(receiver, Action<'T>(callback)))
sequence + 2)
[<System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)>]
let inline binderThreeState< ^T, ^B, ^O when ^B : (static member FormatValue : ^T * CultureInfo -> ^O)>
(valueAttribute: string) (value: ^T) (callback: ^T -> unit) cultureInfo =
Attr(fun receiver builder sequence ->
builder.AddAttribute(sequence, "ThreeState", true)
builder.AddAttribute(sequence + 1, "CheckState", (^B : (static member FormatValue : ^T * CultureInfo -> ^O)(value, cultureInfo)))
builder.AddAttribute(sequence + 2, "CheckStateChanged", EventCallback.Factory.Create(receiver, Action<'T>(callback)))
sequence + 3)
[<System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)>]
let inline binderFieldId< ^T, ^B, ^O when ^B : (static member FormatValue : ^T * CultureInfo -> ^O)>
(valueAttribute: string) (value: ^T) (callback: ^T -> unit) (fieldIdentifier: FieldIdentifier) cultureInfo =
Attr(fun receiver builder sequence ->
builder.AddAttribute(sequence, "FieldIdentifier", fieldIdentifier)
builder.AddAttribute(sequence + 1, valueAttribute, (^B : (static member FormatValue : ^T * CultureInfo -> ^O)(value, cultureInfo)))
builder.AddAttribute(sequence + 2, valueAttribute + "Changed", EventCallback.Factory.Create(receiver, Action<'T>(callback)))
sequence + 3)
[<System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)>]
let inline binderThreeStateFieldId< ^T, ^B, ^O when ^B : (static member FormatValue : ^T * CultureInfo -> ^O)>
(valueAttribute: string) (value: ^T) (callback: ^T -> unit) (fieldIdentifier: FieldIdentifier) cultureInfo =
Attr(fun receiver builder sequence ->
builder.AddAttribute(sequence, "ThreeState", true)
builder.AddAttribute(sequence + 1, "FieldIdentifier", fieldIdentifier)
builder.AddAttribute(sequence + 2, "CheckState", (^B : (static member FormatValue : ^T * CultureInfo -> ^O)(value, cultureInfo)))
builder.AddAttribute(sequence + 3, "CheckStateChanged", EventCallback.Factory.Create(receiver, Action<'T>(callback)))
sequence + 4)
[<System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)>]
let inline binderExpression< ^T, ^B, ^O when ^B : (static member FormatValue : ^T * CultureInfo -> ^O)>
(valueAttribute: string) (valueExpression: Quotations.Expr<Func< ^T>>) (callback: ^T -> unit) cultureInfo =
let valueExpression = valueExpression |> LeafExpressionConverter.QuotationToLambdaExpression
let valueFunction = valueExpression.Compile()
Attr(fun receiver builder sequence ->
let value = valueFunction.Invoke()
builder.AddAttribute(sequence, valueAttribute, (^B : (static member FormatValue : ^T * CultureInfo -> ^O)(value, cultureInfo)))
builder.AddAttribute(sequence + 1, valueAttribute + "Changed", EventCallback.Factory.Create(receiver, Action<'T>(callback)))
builder.AddAttribute(sequence + 2, valueAttribute + "Expression", valueExpression)
sequence + 3)
[<System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)>]
let inline binderThreeStateExpression< ^T, ^B, ^O when ^B : (static member FormatValue : ^T * CultureInfo -> ^O)>
(valueAttribute: string) (valueExpression: Quotations.Expr<Func< ^T>>) (callback: ^T -> unit) (fieldIdentifier: FieldIdentifier) cultureInfo =
let valueExpression = valueExpression |> LeafExpressionConverter.QuotationToLambdaExpression
let valueFunction = valueExpression.Compile()
Attr(fun receiver builder sequence ->
let value = valueFunction.Invoke()
builder.AddAttribute(sequence, "ThreeState", true)
builder.AddAttribute(sequence + 1, "CheckState", (^B : (static member FormatValue : ^T * CultureInfo -> ^O)(value, cultureInfo)))
builder.AddAttribute(sequence + 2, "CheckStateChanged", EventCallback.Factory.Create(receiver, Action<'T>(callback)))
builder.AddAttribute(sequence + 2, valueAttribute + "Expression", valueExpression)
sequence + 4)
/// <summary>Bind a boolean to the value of a checkbox with 3 states.</summary>
/// <param name="value">The current checked state.</param>
/// <param name="callback">The function called when the checked state changes.</param>
let inline CheckState value callback = binderThreeState<bool Nullable, BindConverter, bool Nullable> "Value" value callback null
/// <summary>Bind a boolean to the value of a checkbox with 3 states.</summary>
/// <param name="value">The current checked state.</param>
/// <param name="callback">The function called when the checked state changes.</param>
let inline CheckStateFieldId value callback fieldIdentifier = binderThreeStateFieldId<bool Nullable, BindConverter, bool Nullable> "Value" value callback fieldIdentifier null
/// <summary>Bind a boolean to the value of a checkbox with 3 states.</summary>
/// <param name="value">The current checked state.</param>
/// <param name="callback">The function called when the checked state changes.</param>
let inline CheckStateExpressoin value callback fieldIdentifier = binderThreeStateExpression<bool Nullable, BindConverter, bool Nullable> "Value" value callback fieldIdentifier null
/// <summary>
/// Bind to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
[<RequireQualifiedAccess>]
module Input =
/// <summary>
/// Bind a string to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline bool value callback = binder<bool, BindConverter, bool> "Value" value callback null
/// <summary>
/// Bind a string to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline string value callback = binder<string, BindConverter, string> "Value" value callback null
/// <summary>
/// Bind an integer to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline int value callback = binder<int, BindConverter, string> "Value" value callback null
/// <summary>
/// Bind an int64 to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline int64 value callback = binder<int64, BindConverter, string> "Value" value callback null
/// <summary>
/// Bind a float to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline float value callback = binder<float, BindConverter, string> "Value" value callback null
/// <summary>
/// Bind a float32 to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline float32 value callback = binder<float32, BindConverter, string> "Value" value callback null
/// <summary>
/// Bind a decimal to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline decimal value callback = binder<decimal, BindConverter, string> "Value" value callback null
/// <summary>
/// Bind a DateTime to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline dateTime value callback = binder<DateTime, BindConverter, string> "Value" value callback null
/// <summary>
/// Bind a DateTimeOffset to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline dateTimeOffset value callback = binder<DateTimeOffset, BindConverter, string> "Value" value callback null
/// <summary>
/// Bind to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
[<RequireQualifiedAccess>]
module InputFieldId =
/// <summary>
/// Bind a string to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline bool value callback fieldIdentifier = binderFieldId<bool, BindConverter, bool> "Value" value callback fieldIdentifier null
/// <summary>
/// Bind a string to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline string value callback fieldIdentifier = binderFieldId<string, BindConverter, string> "Value" value callback fieldIdentifier null
/// <summary>
/// Bind an integer to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline int value callback fieldIdentifier = binderFieldId<int, BindConverter, string> "Value" value callback fieldIdentifier null
/// <summary>
/// Bind an int64 to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline int64 value callback fieldIdentifier = binderFieldId<int64, BindConverter, string> "Value" value callback fieldIdentifier null
/// <summary>
/// Bind a float to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline float value callback fieldIdentifier = binderFieldId<float, BindConverter, string> "Value" value callback fieldIdentifier null
/// <summary>
/// Bind a float32 to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline float32 value callback fieldIdentifier = binderFieldId<float32, BindConverter, string> "Value" value callback fieldIdentifier null
/// <summary>
/// Bind a decimal to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline decimal value callback fieldIdentifier = binderFieldId<decimal, BindConverter, string> "Value" value callback fieldIdentifier null
/// <summary>
/// Bind a DateTime to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline dateTime value callback fieldIdentifier = binderFieldId<DateTime, BindConverter, string> "Value" value callback fieldIdentifier null
/// <summary>
/// Bind a DateTimeOffset to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline dateTimeOffset value callback fieldIdentifier = binderFieldId<DateTimeOffset, BindConverter, string> "Value" value callback fieldIdentifier null
/// <summary>
/// Bind to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
module InputExpression =
/// <summary>
/// Bind a string to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline bool value callback = binderExpression<bool, BindConverter, bool> "Value" value callback null
/// <summary>
/// Bind a string to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline string value callback = binderExpression<string, BindConverter, string> "Value" value callback null
/// <summary>
/// Bind an integer to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline int value callback = binderExpression<int, BindConverter, string> "Value" value callback null
/// <summary>
/// Bind an int64 to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline int64 value callback = binderExpression<int64, BindConverter, string> "Value" value callback null
/// <summary>
/// Bind a float to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline float value callback = binderExpression<float, BindConverter, string> "Value" value callback null
/// <summary>
/// Bind a float32 to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline float32 value callback = binderExpression<float32, BindConverter, string> "Value" value callback null
/// <summary>
/// Bind a decimal to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline decimal value callback = binderExpression<decimal, BindConverter, string> "Value" value callback null
/// <summary>
/// Bind a DateTime to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline dateTime value callback = binderExpression<DateTime, BindConverter, string> "Value" value callback null
/// <summary>
/// Bind a DateTimeOffset to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline dateTimeOffset value callback = binderExpression<DateTimeOffset, BindConverter, string> "Value" value callback null
/// <summary>
/// Bind to the Value of an input and convert using the given <see cref="T:System.Globalization.CultureInfo" />.
/// </summary>
module withCulture =
/// <summary>Bind a boolean to the value of a checkbox with 3 states.</summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="value">The current checked state.</param>
/// <param name="callback">The function called when the checked state changes.</param>
let inline CheckState culture value callback = binderThreeState<bool Nullable, BindConverter, bool Nullable> "Value" value callback culture
/// <summary>Bind a boolean to the value of a checkbox with 3 states.</summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="value">The current checked state.</param>
/// <param name="callback">The function called when the checked state changes.</param>
let inline CheckStateFiedlId culture value callback fieldIdentifier = binderThreeStateFieldId<bool Nullable, BindConverter, bool Nullable> "Value" value callback fieldIdentifier culture
/// <summary>Bind a boolean to the value of a checkbox with 3 states.</summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="value">The current checked state.</param>
/// <param name="callback">The function called when the checked state changes.</param>
let inline CheckStateExpression culture value callback fieldIdentifier = binderThreeStateExpression<bool Nullable, BindConverter, bool Nullable> "Value" value callback fieldIdentifier culture
/// <summary>
/// Bind to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
[<RequireQualifiedAccess>]
module Input =
/// <summary>
/// Bind a string to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline bool culture value callback = binder<bool, BindConverter, bool> "Value" value callback culture
/// <summary>
/// Bind a string to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline string culture value callback = binder<string, BindConverter, string> "Value" value callback culture
/// <summary>
/// Bind an integer to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline int culture value callback = binder<int, BindConverter, string> "Value" value callback culture
/// <summary>
/// Bind an int64 to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline int64 culture value callback = binder<int64, BindConverter, string> "Value" value callback culture
/// <summary>
/// Bind a float to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline float culture value callback = binder<float, BindConverter, string> "Value" value callback culture
/// <summary>
/// Bind a float32 to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline float32 culture value callback = binder<float32, BindConverter, string> "Value" value callback culture
/// <summary>
/// Bind a decimal to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline decimal culture value callback = binder<decimal, BindConverter, string> "Value" value callback culture
/// <summary>
/// Bind a DateTime to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline dateTime culture value callback = binder<DateTime, BindConverter, string> "Value" value callback culture
/// <summary>
/// Bind a DateTimeOffset to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline dateTimeOffset culture value callback = binder<DateTimeOffset, BindConverter, string> "Value" value callback culture
/// <summary>
/// Bind to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
[<RequireQualifiedAccess>]
module InputFieldId =
/// <summary>
/// Bind a string to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline bool culture value callback fieldIdentifier = binderFieldId<bool, BindConverter, bool> "Value" value callback fieldIdentifier culture
/// <summary>
/// Bind a string to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline string culture value callback fieldIdentifier = binderFieldId<string, BindConverter, string> "Value" value callback fieldIdentifier culture
/// <summary>
/// Bind an integer to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline int culture value callback fieldIdentifier = binderFieldId<int, BindConverter, string> "Value" value callback fieldIdentifier culture
/// <summary>
/// Bind an int64 to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline int64 culture value callback fieldIdentifier = binderFieldId<int64, BindConverter, string> "Value" value callback fieldIdentifier culture
/// <summary>
/// Bind a float to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline float culture value callback fieldIdentifier = binderFieldId<float, BindConverter, string> "Value" value callback fieldIdentifier culture
/// <summary>
/// Bind a float32 to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline float32 culture value callback fieldIdentifier = binderFieldId<float32, BindConverter, string> "Value" value callback fieldIdentifier culture
/// <summary>
/// Bind a decimal to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline decimal culture value callback fieldIdentifier = binderFieldId<decimal, BindConverter, string> "Value" value callback fieldIdentifier culture
/// <summary>
/// Bind a DateTime to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline dateTime culture value callback fieldIdentifier = binderFieldId<DateTime, BindConverter, string> "Value" value callback fieldIdentifier culture
/// <summary>
/// Bind a DateTimeOffset to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline dateTimeOffset culture value callback fieldIdentifier = binderFieldId<DateTimeOffset, BindConverter, string> "Value" value callback fieldIdentifier culture
/// <summary>
/// Bind to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
module InputExpression =
/// <summary>
/// Bind a string to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline string culture value callback = binderExpression<string, BindConverter, string> "Value" value callback culture
/// <summary>
/// Bind an integer to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline int culture value callback = binderExpression<int, BindConverter, string> "Value" value callback culture
/// <summary>
/// Bind an int64 to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline int64 culture value callback = binderExpression<int64, BindConverter, string> "Value" value callback culture
/// <summary>
/// Bind a float to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline float culture value callback = binderExpression<float, BindConverter, string> "Value" value callback culture
/// <summary>
/// Bind a float32 to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline float32 culture value callback = binderExpression<float32, BindConverter, string> "Value" value callback culture
/// <summary>
/// Bind a decimal to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline decimal culture value callback = binderExpression<decimal, BindConverter, string> "Value" value callback culture
/// <summary>
/// Bind a DateTime to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline dateTime culture value callback = binderExpression<DateTime, BindConverter, string> "Value" value callback culture
/// <summary>
/// Bind a DateTimeOffset to the Value of an input. The Value is updated on the <c>oninput</c> event.
/// </summary>
/// <param name="culture">The culture to use to parse the Value.</param>
/// <param name="Value">The current input state.</param>
/// <param name="callback">The function called when the input state changes.</param>
let inline dateTimeOffset culture value callback = binderExpression<DateTimeOffset, BindConverter, string> "Value" value callback culture
@xperiandri I just tested this with MatBlazor, and indeed the following works:
type Model = { x: string }
type Message = SetX of string
let update message model =
match message with
| SetX x -> { model with x = x }
let view model dispatch =
div {
p {
comp<MatStringField> {
"Label" => "What's your name?"
Bind.InputExpression.string <@ System.Func<_>(fun () -> model.x) @> (fun x -> dispatch (SetX x))
}
}
p { $"Hello, {model.x}!" }
}
Great work!
It should be possible to make it easier to call this using F#'s support for Linq expressions. When a method (unfortunately, it must be a method, not a function) takes an Expression<T>
, then it can be called by simply passing an expression of type T
and the compiler will implicitly quote it and do the LeafExpressionConverter
thing.
Expression is used to extract field name. I'm not sure that is possible with method