From 8fbb9fb0e67969c46f5a39c3797261665e076822 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 14 Dec 2018 17:05:22 +0000 Subject: [PATCH] Add support for binding decimal? double? float? long? int? bool? (imported from Blazor PR 1658) (#4600) --- .../BindMethods.cs | 60 +++++- .../Tests/BindTest.cs | 177 +++++++++++++++++- .../BasicTestApp/BindCasesComponent.cshtml | 44 ++++- 3 files changed, 277 insertions(+), 4 deletions(-) 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 }