diff --git a/src/BootstrapBlazor/BootstrapBlazor.csproj b/src/BootstrapBlazor/BootstrapBlazor.csproj index c510f140fd0..ba0651b92a8 100644 --- a/src/BootstrapBlazor/BootstrapBlazor.csproj +++ b/src/BootstrapBlazor/BootstrapBlazor.csproj @@ -1,7 +1,7 @@  - 10.2.1-beta03 + 10.2.1-beta04 diff --git a/src/BootstrapBlazor/Components/Select/Select.razor.cs b/src/BootstrapBlazor/Components/Select/Select.razor.cs index 2682b60667c..f279c6dcac1 100644 --- a/src/BootstrapBlazor/Components/Select/Select.razor.cs +++ b/src/BootstrapBlazor/Components/Select/Select.razor.cs @@ -281,7 +281,7 @@ private bool TryParseSelectItem(string value, [MaybeNullWhen(false)] out TValue // support SelectedItem? type result = SelectedItem != null ? (TValue)(object)SelectedItem : default; - validationErrorMessage = ""; + validationErrorMessage = null; return SelectedItem != null; } diff --git a/src/BootstrapBlazor/Components/Validate/ValidateBase.cs b/src/BootstrapBlazor/Components/Validate/ValidateBase.cs index b07d93e77a4..d7ff994b2af 100644 --- a/src/BootstrapBlazor/Components/Validate/ValidateBase.cs +++ b/src/BootstrapBlazor/Components/Validate/ValidateBase.cs @@ -119,6 +119,11 @@ protected string CurrentValueAsString PreviousParsingAttemptFailed = false; CurrentValue = parsedValue; } + else if (string.IsNullOrEmpty(validationErrorMessage)) + { + // validationErrorMessage 为 null 表示转换目标值失败组件值未改变 + PreviousParsingAttemptFailed = false; + } else { PreviousParsingAttemptFailed = true; @@ -131,7 +136,7 @@ protected string CurrentValueAsString if (FieldIdentifier != null) { - _parsingValidationMessages?.Add(FieldIdentifier.Value, PreviousErrorMessage ?? ""); + _parsingValidationMessages?.Add(FieldIdentifier.Value, PreviousErrorMessage); // Since we're not writing to CurrentValue, we'll need to notify about modification from here EditContext?.NotifyFieldChanged(FieldIdentifier.Value); diff --git a/test/UnitTest/Components/DateTimePickerTest.cs b/test/UnitTest/Components/DateTimePickerTest.cs index 01278e42c64..5095a367d37 100644 --- a/test/UnitTest/Components/DateTimePickerTest.cs +++ b/test/UnitTest/Components/DateTimePickerTest.cs @@ -1255,6 +1255,41 @@ await cut.InvokeAsync(() => Assert.Equal("02/15/2024 01:00:00", cut.Instance.Value.ToString("MM/dd/yyyy HH:mm:ss")); } + [Fact] + public async Task ValidateForm_IsEditable_Ok() + { + var model = new Foo() { DateTime = new DateTime(2024, 2, 15) }; + var cut = Context.Render(pb => + { + pb.Add(a => a.Model, model); + pb.AddChildContent>(builder => + { + builder.Add(a => a.IsEditable, true); + builder.Add(a => a.ViewMode, DatePickerViewMode.Date); + builder.Add(a => a.DateFormat, "MM/dd/yyyy"); + builder.Add(a => a.Value, model.DateTime); + builder.Add(a => a.ValueChanged, EventCallback.Factory.Create(this, v => + { + model.DateTime = v; + })); + builder.Add(a => a.ValueExpression, Utility.GenerateValueExpression(model, nameof(model.DateTime), typeof(DateTime?))); + }); + }); + var input = cut.Find(".datetime-picker-input"); + + // 更改成非法数值 测试 CurrentValueAsString 赋值逻辑 + await cut.InvokeAsync(() => + { + input.Change("00/15/2024"); + }); + Assert.NotNull(model.DateTime); + Assert.Equal("02/15/2024", model.DateTime.Value.ToString("MM/dd/yyyy")); + + // 录入非法数值 Validate 通过 + var valid = cut.Instance.Validate(); + Assert.True(valid); + } + [Fact] public void MinValueToEmpty_Ok() { diff --git a/test/UnitTest/Components/InputNumberTest.cs b/test/UnitTest/Components/InputNumberTest.cs index 14362306672..78686218ed3 100644 --- a/test/UnitTest/Components/InputNumberTest.cs +++ b/test/UnitTest/Components/InputNumberTest.cs @@ -216,6 +216,88 @@ public void Type_Ok(Type t) cut.InvokeAsync(() => buttons[1].Click()); } + [Fact] + public async Task Validate_Ok() + { + var model = new Foo() { Count = 1 }; + var cut = Context.Render(pb => + { + pb.Add(a => a.Model, model); + pb.AddChildContent>(builder => + { + builder.Add(a => a.Value, model.Count); + builder.Add(a => a.ValueChanged, EventCallback.Factory.Create(this, v => + { + model.Count = v; + })); + builder.Add(a => a.ValueExpression, Utility.GenerateValueExpression(model, nameof(model.Count), typeof(int))); + }); + }); + var input = cut.Find(".form-control"); + + // 更改成非法数值 测试 CurrentValueAsString 赋值逻辑 + await cut.InvokeAsync(() => + { + input.Change("t"); + }); + Assert.Equal(1, model.Count); + + var valid = cut.Instance.Validate(); + Assert.False(valid); + + await cut.InvokeAsync(() => + { + input.Change("t2"); + }); + Assert.Equal(1, model.Count); + valid = cut.Instance.Validate(); + Assert.False(valid); + + await cut.InvokeAsync(() => + { + input.Change("2"); + }); + Assert.Equal(2, model.Count); + + valid = cut.Instance.Validate(); + Assert.True(valid); + } + + [Fact] + public async Task TryParseValueFromString_Ok() + { + var model = new Foo() { Count = 1 }; + var cut = Context.Render>(pb => + { + pb.Add(a => a.Value, 1); + pb.Add(a => a.ValueChanged, EventCallback.Factory.Create(this, v => + { + model.Count = v; + })); + pb.Add(a => a.ValueExpression, Utility.GenerateValueExpression(model, nameof(model.Count), typeof(int))); + }); + var input = cut.Find(".form-control"); + + // 更改成非法数值 测试 CurrentValueAsString 赋值逻辑 + await cut.InvokeAsync(() => + { + input.Change("t"); + }); + Assert.Equal(1, model.Count); + + await cut.InvokeAsync(() => + { + input.Change("t2"); + }); + Assert.Equal(1, model.Count); + + await cut.InvokeAsync(() => + { + input.Change("2"); + }); + Assert.Equal(2, model.Count); + } + private class Cat { [Range(1, 10)]