diff --git a/src/Components/src/Microsoft.AspNetCore.Components/BindMethods.cs b/src/Components/src/Microsoft.AspNetCore.Components/BindMethods.cs
index fc74f6c693..52affaa6a1 100644
--- a/src/Components/src/Microsoft.AspNetCore.Components/BindMethods.cs
+++ b/src/Components/src/Microsoft.AspNetCore.Components/BindMethods.cs
@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
@@ -85,6 +85,14 @@ namespace Microsoft.AspNetCore.Components
return _ => setter((bool)((UIChangeEventArgs)_).Value);
}
+ ///
+ /// Not intended to be used directly.
+ ///
+ public static Action SetValueHandler(Action setter, bool? existingValue)
+ {
+ return _ => setter((bool?)((UIChangeEventArgs)_).Value);
+ }
+
///
/// Not intended to be used directly.
///
@@ -93,6 +101,16 @@ namespace Microsoft.AspNetCore.Components
return _ => setter(int.Parse((string)((UIChangeEventArgs)_).Value));
}
+ ///
+ /// Not intended to be used directly.
+ ///
+ public static Action SetValueHandler(Action setter, int? existingValue)
+ {
+ return _ => setter(int.TryParse((string)((UIChangeEventArgs)_).Value, out var tmpvalue)
+ ? tmpvalue
+ : (int?)null);
+ }
+
///
/// Not intended to be used directly.
///
@@ -101,6 +119,16 @@ namespace Microsoft.AspNetCore.Components
return _ => setter(long.Parse((string)((UIChangeEventArgs)_).Value));
}
+ ///
+ /// Not intended to be used directly.
+ ///
+ public static Action SetValueHandler(Action setter, long? existingValue)
+ {
+ return _ => setter(long.TryParse((string)((UIChangeEventArgs)_).Value, out var tmpvalue)
+ ? tmpvalue
+ : (long?)null);
+ }
+
///
/// Not intended to be used directly.
///
@@ -109,6 +137,16 @@ namespace Microsoft.AspNetCore.Components
return _ => setter(float.Parse((string)((UIChangeEventArgs)_).Value));
}
+ ///
+ /// Not intended to be used directly.
+ ///
+ public static Action SetValueHandler(Action setter, float? existingValue)
+ {
+ return _ => setter(float.TryParse((string)((UIChangeEventArgs)_).Value, out var tmpvalue)
+ ? tmpvalue
+ : (float?)null);
+ }
+
///
/// Not intended to be used directly.
///
@@ -117,6 +155,16 @@ namespace Microsoft.AspNetCore.Components
return _ => setter(double.Parse((string)((UIChangeEventArgs)_).Value));
}
+ ///
+ /// Not intended to be used directly.
+ ///
+ public static Action SetValueHandler(Action setter, double? existingValue)
+ {
+ return _ => setter(double.TryParse((string)((UIChangeEventArgs)_).Value, out var tmpvalue)
+ ? tmpvalue
+ : (double?)null);
+ }
+
///
/// Not intended to be used directly.
///
@@ -125,6 +173,16 @@ namespace Microsoft.AspNetCore.Components
return _ => setter(decimal.Parse((string)((UIChangeEventArgs)_).Value));
}
+ ///
+ /// Not intended to be used directly.
+ ///
+ public static Action SetValueHandler(Action setter, decimal? existingValue)
+ {
+ return _ => setter(decimal.TryParse((string)((UIChangeEventArgs)_).Value, out var tmpvalue)
+ ? tmpvalue
+ : (decimal?)null);
+ }
+
///
/// Not intended to be used directly.
///
diff --git a/src/Components/test/Microsoft.AspNetCore.Components.E2ETest/Tests/BindTest.cs b/src/Components/test/Microsoft.AspNetCore.Components.E2ETest/Tests/BindTest.cs
index a0d7b65845..eb75e3cffc 100644
--- a/src/Components/test/Microsoft.AspNetCore.Components.E2ETest/Tests/BindTest.cs
+++ b/src/Components/test/Microsoft.AspNetCore.Components.E2ETest/Tests/BindTest.cs
@@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
Navigate(ServerPathBase, noReload: !serverFixture.UsingAspNetHost);
MountTestComponent();
}
-
+
[Fact]
public void CanBindTextbox_InitiallyBlank()
{
@@ -73,7 +73,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
Assert.Equal(string.Empty, boundValue.Text);
Assert.Equal(string.Empty, mirrorValue.GetAttribute("value"));
}
-
+
[Fact]
public void CanBindTextArea_InitiallyBlank()
{
@@ -103,6 +103,26 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
WaitAssert.Equal("Changed value", () => boundValue.Text);
}
+ [Fact]
+ public void CanBindCheckbox_InitiallyNull()
+ {
+ var target = Browser.FindElement(By.Id("checkbox-initially-null"));
+ var boundValue = Browser.FindElement(By.Id("checkbox-initially-null-value"));
+ var invertButton = Browser.FindElement(By.Id("checkbox-initially-null-invert"));
+ Assert.False(target.Selected);
+ Assert.Equal(string.Empty, boundValue.Text);
+
+ // Modify target; verify value is updated
+ target.Click();
+ WaitAssert.True(() => target.Selected);
+ WaitAssert.Equal("True", () => boundValue.Text);
+
+ // Modify data; verify checkbox is updated
+ invertButton.Click();
+ WaitAssert.False(() => target.Selected);
+ WaitAssert.Equal("False", () => boundValue.Text);
+ }
+
[Fact]
public void CanBindCheckbox_InitiallyUnchecked()
{
@@ -180,6 +200,35 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
Assert.Equal("42", mirrorValue.GetAttribute("value"));
}
+ [Fact]
+ public void CanBindTextboxNullableInt()
+ {
+ var target = Browser.FindElement(By.Id("textbox-nullable-int"));
+ var boundValue = Browser.FindElement(By.Id("textbox-nullable-int-value"));
+ var mirrorValue = Browser.FindElement(By.Id("textbox-nullable-int-mirror"));
+ Assert.Equal(string.Empty, target.GetAttribute("value"));
+ Assert.Equal(string.Empty, boundValue.Text);
+ Assert.Equal(string.Empty, mirrorValue.GetAttribute("value"));
+
+ // Modify target; verify value is updated and that textboxes linked to the same data are updated
+ target.Clear();
+ target.SendKeys("-42\t");
+ WaitAssert.Equal("-42", () => boundValue.Text);
+ Assert.Equal("-42", mirrorValue.GetAttribute("value"));
+
+ // Modify target; verify value is updated and that textboxes linked to the same data are updated
+ target.Clear();
+ target.SendKeys("42\t");
+ WaitAssert.Equal("42", () => boundValue.Text);
+ Assert.Equal("42", mirrorValue.GetAttribute("value"));
+
+ // Modify target; verify value is updated and that textboxes linked to the same data are updated
+ target.Clear();
+ target.SendKeys("\t");
+ WaitAssert.Equal(string.Empty, () => boundValue.Text);
+ Assert.Equal(string.Empty, mirrorValue.GetAttribute("value"));
+ }
+
[Fact]
public void CanBindTextboxLong()
{
@@ -197,6 +246,35 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
Assert.Equal("-3000000000", mirrorValue.GetAttribute("value"));
}
+ [Fact]
+ public void CanBindTextboxNullableLong()
+ {
+ var target = Browser.FindElement(By.Id("textbox-nullable-long"));
+ var boundValue = Browser.FindElement(By.Id("textbox-nullable-long-value"));
+ var mirrorValue = Browser.FindElement(By.Id("textbox-nullable-long-mirror"));
+ Assert.Equal(string.Empty, target.GetAttribute("value"));
+ Assert.Equal(string.Empty, boundValue.Text);
+ Assert.Equal(string.Empty, mirrorValue.GetAttribute("value"));
+
+ // Modify target; verify value is updated and that textboxes linked to the same data are updated
+ target.Clear();
+ target.SendKeys("3000000000\t");
+ WaitAssert.Equal("3000000000", () => boundValue.Text);
+ Assert.Equal("3000000000", mirrorValue.GetAttribute("value"));
+
+ // Modify target; verify value is updated and that textboxes linked to the same data are updated
+ target.Clear();
+ target.SendKeys("-3000000000\t");
+ WaitAssert.Equal("-3000000000", () => boundValue.Text);
+ Assert.Equal("-3000000000", mirrorValue.GetAttribute("value"));
+
+ // Modify target; verify value is updated and that textboxes linked to the same data are updated
+ target.Clear();
+ target.SendKeys("\t");
+ WaitAssert.Equal(string.Empty, () => boundValue.Text);
+ Assert.Equal(string.Empty, mirrorValue.GetAttribute("value"));
+ }
+
[Fact]
public void CanBindTextboxFloat()
{
@@ -214,6 +292,35 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
Assert.Equal("-3.141", mirrorValue.GetAttribute("value"));
}
+ [Fact]
+ public void CanBindTextboxNullableFloat()
+ {
+ var target = Browser.FindElement(By.Id("textbox-nullable-float"));
+ var boundValue = Browser.FindElement(By.Id("textbox-nullable-float-value"));
+ var mirrorValue = Browser.FindElement(By.Id("textbox-nullable-float-mirror"));
+ Assert.Equal(string.Empty, target.GetAttribute("value"));
+ Assert.Equal(string.Empty, boundValue.Text);
+ Assert.Equal(string.Empty, mirrorValue.GetAttribute("value"));
+
+ // Modify target; verify value is updated and that textboxes linked to the same data are updated
+ target.Clear();
+ target.SendKeys("3.141\t");
+ WaitAssert.Equal("3.141", () => boundValue.Text);
+ Assert.Equal("3.141", mirrorValue.GetAttribute("value"));
+
+ // Modify target; verify value is updated and that textboxes linked to the same data are updated
+ target.Clear();
+ target.SendKeys("-3.141\t");
+ WaitAssert.Equal("-3.141", () => boundValue.Text);
+ Assert.Equal("-3.141", mirrorValue.GetAttribute("value"));
+
+ // Modify target; verify value is updated and that textboxes linked to the same data are updated
+ target.Clear();
+ target.SendKeys("\t");
+ WaitAssert.Equal(string.Empty, () => boundValue.Text);
+ Assert.Equal(string.Empty, mirrorValue.GetAttribute("value"));
+ }
+
[Fact]
public void CanBindTextboxDouble()
{
@@ -238,6 +345,42 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
Assert.Equal("0.01", mirrorValue.GetAttribute("value"));
}
+ [Fact]
+ public void CanBindTextboxNullableDouble()
+ {
+ var target = Browser.FindElement(By.Id("textbox-nullable-double"));
+ var boundValue = Browser.FindElement(By.Id("textbox-nullable-double-value"));
+ var mirrorValue = Browser.FindElement(By.Id("textbox-nullable-double-mirror"));
+ Assert.Equal(string.Empty, target.GetAttribute("value"));
+ Assert.Equal(string.Empty, boundValue.Text);
+ Assert.Equal(string.Empty, mirrorValue.GetAttribute("value"));
+
+ // Modify target; verify value is updated and that textboxes linked to the same data are updated
+ target.Clear();
+ target.SendKeys("3.14159265359\t");
+ WaitAssert.Equal("3.14159265359", () => boundValue.Text);
+ Assert.Equal("3.14159265359", mirrorValue.GetAttribute("value"));
+
+ // Modify target; verify value is updated and that textboxes linked to the same data are updated
+ target.Clear();
+ target.SendKeys("-3.14159265359\t");
+ WaitAssert.Equal("-3.14159265359", () => boundValue.Text);
+ Assert.Equal("-3.14159265359", mirrorValue.GetAttribute("value"));
+
+ // Modify target; verify value is updated and that textboxes linked to the same data are updated
+ // Double shouldn't preserve trailing zeros
+ target.Clear();
+ target.SendKeys("0.010\t");
+ WaitAssert.Equal("0.01", () => boundValue.Text);
+ Assert.Equal("0.01", mirrorValue.GetAttribute("value"));
+
+ // Modify target; verify value is updated and that textboxes linked to the same data are updated
+ target.Clear();
+ target.SendKeys("\t");
+ WaitAssert.Equal(string.Empty, () => boundValue.Text);
+ Assert.Equal(string.Empty, mirrorValue.GetAttribute("value"));
+ }
+
[Fact]
public void CanBindTextboxDecimal()
{
@@ -255,5 +398,35 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
WaitAssert.Equal("0.010", () => boundValue.Text);
Assert.Equal("0.010", mirrorValue.GetAttribute("value"));
}
+
+ [Fact]
+ public void CanBindTextboxNullableDecimal()
+ {
+ var target = Browser.FindElement(By.Id("textbox-nullable-decimal"));
+ var boundValue = Browser.FindElement(By.Id("textbox-nullable-decimal-value"));
+ var mirrorValue = Browser.FindElement(By.Id("textbox-nullable-decimal-mirror"));
+ Assert.Equal(string.Empty, target.GetAttribute("value"));
+ Assert.Equal(string.Empty, boundValue.Text);
+ Assert.Equal(string.Empty, mirrorValue.GetAttribute("value"));
+
+ // Modify target; verify value is updated and that textboxes linked to the same data are updated
+ target.Clear();
+ target.SendKeys("0.0000000000000000000000000001\t");
+ WaitAssert.Equal("0.0000000000000000000000000001", () => boundValue.Text);
+ Assert.Equal("0.0000000000000000000000000001", mirrorValue.GetAttribute("value"));
+
+ // Modify target; verify value is updated and that textboxes linked to the same data are updated
+ // Decimal should preserve trailing zeros
+ target.Clear();
+ target.SendKeys("0.010\t");
+ WaitAssert.Equal("0.010", () => boundValue.Text);
+ Assert.Equal("0.010", mirrorValue.GetAttribute("value"));
+
+ // Modify target; verify value is updated and that textboxes linked to the same data are updated
+ target.Clear();
+ target.SendKeys("\t");
+ WaitAssert.Equal(string.Empty, () => boundValue.Text);
+ Assert.Equal(string.Empty, mirrorValue.GetAttribute("value"));
+ }
}
}
diff --git a/src/Components/test/testapps/BasicTestApp/BindCasesComponent.cshtml b/src/Components/test/testapps/BasicTestApp/BindCasesComponent.cshtml
index 91dd1fc786..696e24568e 100644
--- a/src/Components/test/testapps/BasicTestApp/BindCasesComponent.cshtml
+++ b/src/Components/test/testapps/BasicTestApp/BindCasesComponent.cshtml
@@ -21,30 +21,60 @@
@textboxIntValue
+
+ Nullable int:
+
+ @textboxNullableIntValue
+
+
long:
@textboxLongValue
+
+ Nullable long:
+
+ @textboxNullableLongValue
+
+
float:
@textboxFloatValue
+
+ Nullable float:
+
+ @textboxNullableFloatValue
+
+
double:
@textboxDoubleValue
+
+ Nullable double:
+
+ @textboxNullableDoubleValue
+
+
decimal:
@textboxDecimalValue
+
+ Nullable decimal:
+
+ @textboxNullableDecimalValue
+
+
Text Area
@@ -59,6 +89,12 @@
Checkbox
+
+ Initially null:
+
+ @checkboxInitiallyNullValue
+
+
Initially unchecked:
@@ -80,7 +116,7 @@
@if (includeFourthOption)
{
-
+
}
@selectValue
@@ -94,14 +130,20 @@
string textAreaInitiallyBlankValue = null;
string textAreaInitiallyPopulatedValue = "Hello";
+ bool? checkboxInitiallyNullValue = null;
bool checkboxInitiallyUncheckedValue = false;
bool checkboxInitiallyCheckedValue = true;
int textboxIntValue = -42;
+ int? textboxNullableIntValue = null;
long textboxLongValue = 3_000_000_000;
+ long? textboxNullableLongValue = null;
float textboxFloatValue = 3.141f;
+ float? textboxNullableFloatValue = null;
double textboxDoubleValue = 3.14159265359d;
+ double? textboxNullableDoubleValue = null;
decimal textboxDecimalValue = 0.0000000000000000000000000001M;
+ decimal? textboxNullableDecimalValue = null;
bool includeFourthOption = false;
enum SelectableValue { First, Second, Third, Fourth }