diff --git a/src/MudBlazor.Docs/Pages/Components/Autocomplete/Examples/AutocompleteValidationExample.razor b/src/MudBlazor.Docs/Pages/Components/Autocomplete/Examples/AutocompleteValidationExample.razor index 49ea2774d6f..563ab4207ad 100644 --- a/src/MudBlazor.Docs/Pages/Components/Autocomplete/Examples/AutocompleteValidationExample.razor +++ b/src/MudBlazor.Docs/Pages/Components/Autocomplete/Examples/AutocompleteValidationExample.razor @@ -40,7 +40,7 @@ CloseIcon="@Icons.Material.Filled.Search" AdornmentColor="Color.Tertiary" Validation="@(new Func>(Validate))" /> Validate + OnClick="@(()=>form.ValidateAsync())">Validate @if (form.IsTouched && form.IsValid) { Success diff --git a/src/MudBlazor.Docs/Pages/Components/FileUpload/Examples/FileUploadValidationExample.razor b/src/MudBlazor.Docs/Pages/Components/FileUpload/Examples/FileUploadValidationExample.razor index 0f30de0f767..0f33b4830ac 100644 --- a/src/MudBlazor.Docs/Pages/Components/FileUpload/Examples/FileUploadValidationExample.razor +++ b/src/MudBlazor.Docs/Pages/Components/FileUpload/Examples/FileUploadValidationExample.razor @@ -67,7 +67,7 @@ private async Task Submit() { - await form.Validate(); + await form.ValidateAsync(); if (form.IsValid) { diff --git a/src/MudBlazor.Docs/Pages/Components/Form/Examples/FluentValidationComplexExample.razor b/src/MudBlazor.Docs/Pages/Components/Form/Examples/FluentValidationComplexExample.razor index 18b1820b7b4..29e5fe93b00 100644 --- a/src/MudBlazor.Docs/Pages/Components/Form/Examples/FluentValidationComplexExample.razor +++ b/src/MudBlazor.Docs/Pages/Components/Form/Examples/FluentValidationComplexExample.razor @@ -110,7 +110,7 @@ private async Task Submit() { - await form.Validate(); + await form.ValidateAsync(); if (form.IsValid) { diff --git a/src/MudBlazor.Docs/Pages/Components/Form/Examples/MudFormExample.razor b/src/MudBlazor.Docs/Pages/Components/Form/Examples/MudFormExample.razor index 773befb1ba0..e51ad819d6c 100644 --- a/src/MudBlazor.Docs/Pages/Components/Form/Examples/MudFormExample.razor +++ b/src/MudBlazor.Docs/Pages/Components/Form/Examples/MudFormExample.razor @@ -29,9 +29,9 @@ - Validate + Validate Reset - Reset Validation + Reset Validation diff --git a/src/MudBlazor.UnitTests.Viewer/TestComponents/Autocomplete/AutocompleteValidationTest.razor b/src/MudBlazor.UnitTests.Viewer/TestComponents/Autocomplete/AutocompleteValidationTest.razor index 5a54a5d4f3e..07c59e521cc 100644 --- a/src/MudBlazor.UnitTests.Viewer/TestComponents/Autocomplete/AutocompleteValidationTest.razor +++ b/src/MudBlazor.UnitTests.Viewer/TestComponents/Autocomplete/AutocompleteValidationTest.razor @@ -9,7 +9,7 @@ RequiredError="Very long error message. Very long error message. Very long error message. Very long error message. Very long error message. Very long error message. Very long error message. Very long error message. Very long error message. Very long error message. Very long error message. Very long error message. Very long error message." ResetValueOnEmptyText="true" /> - Reset Validation + Reset Validation @code { diff --git a/src/MudBlazor.UnitTests.Viewer/TestComponents/DatePicker/DateRangePickerValidationTest.razor b/src/MudBlazor.UnitTests.Viewer/TestComponents/DatePicker/DateRangePickerValidationTest.razor index 8d457578ae2..4150e1a5454 100644 --- a/src/MudBlazor.UnitTests.Viewer/TestComponents/DatePicker/DateRangePickerValidationTest.razor +++ b/src/MudBlazor.UnitTests.Viewer/TestComponents/DatePicker/DateRangePickerValidationTest.razor @@ -14,7 +14,7 @@ - Validate + Validate @code { diff --git a/src/MudBlazor.UnitTests.Viewer/TestComponents/FileUpload/FileUploadFormValidationTest.razor b/src/MudBlazor.UnitTests.Viewer/TestComponents/FileUpload/FileUploadFormValidationTest.razor index ef38ee976d9..5e07c9d0ddf 100644 --- a/src/MudBlazor.UnitTests.Viewer/TestComponents/FileUpload/FileUploadFormValidationTest.razor +++ b/src/MudBlazor.UnitTests.Viewer/TestComponents/FileUpload/FileUploadFormValidationTest.razor @@ -6,7 +6,7 @@ -Submit +Submit @code { public MudForm Form; diff --git a/src/MudBlazor.UnitTests.Viewer/TestComponents/Form/FormIsValidTest3.razor b/src/MudBlazor.UnitTests.Viewer/TestComponents/Form/FormIsValidTest3.razor index e4dc9135b8b..e9930303ea2 100644 --- a/src/MudBlazor.UnitTests.Viewer/TestComponents/Form/FormIsValidTest3.razor +++ b/src/MudBlazor.UnitTests.Viewer/TestComponents/Form/FormIsValidTest3.razor @@ -4,9 +4,9 @@
- Validate + Validate Reset - Reset Validation + Reset Validation
diff --git a/src/MudBlazor.UnitTests/Components/AutocompleteTests.cs b/src/MudBlazor.UnitTests/Components/AutocompleteTests.cs index 9b6823a0702..1be1c1bc5b9 100644 --- a/src/MudBlazor.UnitTests/Components/AutocompleteTests.cs +++ b/src/MudBlazor.UnitTests/Components/AutocompleteTests.cs @@ -374,7 +374,7 @@ public async Task Autocomplete_Should_Validate_Data_Attribute_Fail() autocomplete.Value.Should().Be("Quux"); autocomplete.Text.Should().Be("Quux"); // check validity - await comp.InvokeAsync(autocomplete.Validate); + await comp.InvokeAsync(autocomplete.ValidateAsync); autocomplete.ValidationErrors.Should().NotBeEmpty(); autocomplete.ValidationErrors.Should().HaveCount(1); autocomplete.ValidationErrors[0].Should().Be("Should not be longer than 3"); @@ -393,7 +393,7 @@ public async Task Autocomplete_Should_Validate_Data_Attribute_Success() autocomplete.Value.Should().Be("Qux"); autocomplete.Text.Should().Be("Qux"); // check validity - await comp.InvokeAsync(autocomplete.Validate); + await comp.InvokeAsync(autocomplete.ValidateAsync); autocomplete.ValidationErrors.Should().BeEmpty(); } @@ -409,7 +409,7 @@ public async Task Autocomplete_Should_SetRequiredTrue() autocomplete.Required.Should().BeTrue(); - await comp.InvokeAsync(autocomplete.Validate); + await comp.InvokeAsync(autocomplete.ValidateAsync); autocomplete.ValidationErrors.First().Should().Be("Required"); } diff --git a/src/MudBlazor.UnitTests/Components/DateRangePickerTests.cs b/src/MudBlazor.UnitTests/Components/DateRangePickerTests.cs index f82c46ce5b2..f5176a1b5f6 100644 --- a/src/MudBlazor.UnitTests/Components/DateRangePickerTests.cs +++ b/src/MudBlazor.UnitTests/Components/DateRangePickerTests.cs @@ -616,7 +616,7 @@ public async Task DateRangePicker_RequiredValidation() dateRangePickerInstance.DateRange.Should().Be(null); // validated the picker - await dateRangePickerComponent.InvokeAsync(() => dateRangePickerInstance.Validate()); + await dateRangePickerComponent.InvokeAsync(() => dateRangePickerInstance.ValidateAsync()); dateRangePickerInstance.Error.Should().BeTrue("Value is required and should be handled as invalid"); dateRangePickerInstance.ErrorText.Should().Be(errorMessage); diff --git a/src/MudBlazor.UnitTests/Components/FileUploadTests.cs b/src/MudBlazor.UnitTests/Components/FileUploadTests.cs index 1d820a6588f..4c8e35c73e4 100644 --- a/src/MudBlazor.UnitTests/Components/FileUploadTests.cs +++ b/src/MudBlazor.UnitTests/Components/FileUploadTests.cs @@ -232,7 +232,7 @@ public async Task FileUpload_ValidationTest() var comp = Context.RenderComponent(); var form = comp.Instance.Form; - await comp.InvokeAsync(() => form.Validate()); + await comp.InvokeAsync(() => form.ValidateAsync()); form.IsValid.Should().BeFalse(); //form is invalid to start @@ -247,7 +247,7 @@ public async Task FileUpload_ValidationTest() var singleInput = single.FindComponent(); singleInput.UploadFiles(fileContent[0]); //upload first file - await comp.InvokeAsync(() => form.Validate()); + await comp.InvokeAsync(() => form.ValidateAsync()); single.Instance.ErrorText.Should().Be(null); //first input is now valid single.Markup.Should().NotContain("'File' must not be empty."); @@ -257,7 +257,7 @@ public async Task FileUpload_ValidationTest() var multipleInput = multiple.FindComponent(); multipleInput.UploadFiles(fileContent); //upload second files - await comp.InvokeAsync(() => form.Validate()); + await comp.InvokeAsync(() => form.ValidateAsync()); single.Instance.ErrorText.Should().Be(null); //second input is now valid single.Markup.Should().NotContain("'Files' must not be empty."); diff --git a/src/MudBlazor.UnitTests/Components/FormTests.cs b/src/MudBlazor.UnitTests/Components/FormTests.cs index 00a6da5ddfe..2d080637c0b 100644 --- a/src/MudBlazor.UnitTests/Components/FormTests.cs +++ b/src/MudBlazor.UnitTests/Components/FormTests.cs @@ -139,7 +139,7 @@ public async Task FormIsTouchedTest() form.IsTouched.Should().Be(true); //reset validation should not reset touched state - await comp.InvokeAsync(() => form.ResetValidation()); + await comp.InvokeAsync(() => form.ResetValidationAsync()); form.IsTouched.Should().Be(true); } @@ -174,7 +174,7 @@ public async Task FormIsTouchedAndNestedFormIsNotTouchedWhenParentFormFieldIsTou nestedForm.IsTouched.Should().Be(false); //reset validation should not reset touched state - await comp.InvokeAsync(() => form.ResetValidation()); + await comp.InvokeAsync(() => form.ResetValidationAsync()); form.IsTouched.Should().Be(true); nestedForm.IsTouched.Should().Be(false); } @@ -212,7 +212,7 @@ public async Task FormIsUnTouchedWhenNestedFormTouchedTest() nestedForm.IsTouched.Should().Be(true); //reset validation should not reset touched state - await comp.InvokeAsync(() => nestedFormDateField.ResetValidation()); + await comp.InvokeAsync(() => nestedFormDateField.ResetValidationAsync()); form.IsTouched.Should().Be(false); nestedForm.IsTouched.Should().Be(true); } @@ -1197,7 +1197,7 @@ public async Task MudFormExample_FillInValuesRootForm() comp.FindAll("input")[5].Blur(); var form = comp.FindComponent().Instance; - await comp.InvokeAsync(() => form.Validate()); + await comp.InvokeAsync(() => form.ValidateAsync()); form.IsValid.Should().BeFalse(); var textfields = comp.FindComponents>(); @@ -1242,7 +1242,7 @@ public async Task MudFormExample_FillInValuesNestedForm() comp.FindAll("input")[9].Blur(); var form = comp.FindComponent().Instance; - await comp.InvokeAsync(() => form.Validate()); + await comp.InvokeAsync(() => form.ValidateAsync()); form.IsValid.Should().BeFalse(); var textfields = comp.FindComponents>(); @@ -1297,7 +1297,7 @@ public async Task MudFormExample_FillInValues() comp.FindAll("input")[9].Blur(); var form = comp.FindComponent().Instance; - await comp.InvokeAsync(() => form.Validate()); + await comp.InvokeAsync(() => form.ValidateAsync()); form.IsValid.Should().BeTrue(); var textfields = comp.FindComponents>(); @@ -1347,7 +1347,7 @@ public async Task MudFormComponent_ValidationWithModel_UnexpectedErrorInValidati tf.SetParam(nameof(MudTextField.Validation), validationFunc); Expression> expression = () => model.data; tf.SetParam(nameof(MudTextField.For), expression); - await comp.InvokeAsync(tf.Instance.Validate); + await comp.InvokeAsync(tf.Instance.ValidateAsync); tf.Instance.Error.Should().Be(true); tf.Instance.ErrorText.Should().Be("Error in validation func: User error"); } @@ -1369,7 +1369,7 @@ public async Task MudFormComponent_ValidationWithModelWithNoFor_ShouldShow_Expec throw new InvalidOperationException("User error"); }); tf.SetParam(nameof(MudTextField.Validation), validationFunc); - await comp.InvokeAsync(tf.Instance.Validate); + await comp.InvokeAsync(tf.Instance.ValidateAsync); tf.Instance.Error.Should().Be(true); tf.Instance.ErrorText.Should().Be("For is null, please set parameter For on the form input component of type MudTextField`1"); } @@ -1391,7 +1391,7 @@ public async Task MudFormComponent_AsyncValidationWithModelWithNoFor_ShouldShow_ throw new InvalidOperationException("User error"); }); tf.SetParam(nameof(MudTextField.Validation), validationFunc); - await comp.InvokeAsync(tf.Instance.Validate); + await comp.InvokeAsync(tf.Instance.ValidateAsync); tf.Instance.Error.Should().Be(true); tf.Instance.ErrorText.Should().Be("For is null, please set parameter For on the form input component of type MudTextField`1"); } @@ -1416,7 +1416,7 @@ public async Task MudFormComponent_ValidationWithModel_UnexpectedErrorInValidati tf.SetParam(nameof(MudTextField.Validation), validationFunc); Expression> expression = () => model.data; tf.SetParam(nameof(MudTextField.For), expression); - await comp.InvokeAsync(tf.Instance.Validate); + await comp.InvokeAsync(tf.Instance.ValidateAsync); tf.Instance.Error.Should().Be(true); tf.Instance.ErrorText.Should().Be("Error1"); } diff --git a/src/MudBlazor.UnitTests/Components/NumericFieldTests.cs b/src/MudBlazor.UnitTests/Components/NumericFieldTests.cs index af5e22a782f..d5db3403fda 100644 --- a/src/MudBlazor.UnitTests/Components/NumericFieldTests.cs +++ b/src/MudBlazor.UnitTests/Components/NumericFieldTests.cs @@ -620,7 +620,7 @@ public async Task NumericField_Validation(T value) numericField.Value.Should().Be(value); await comp.InvokeAsync(() => { - numericField.Validate().Wait(); + numericField.ValidateAsync().Wait(); }); numericField.Value.Should().Be(value); } diff --git a/src/MudBlazor.UnitTests/Components/SelectTests.cs b/src/MudBlazor.UnitTests/Components/SelectTests.cs index a7c2fc31773..45e57017417 100644 --- a/src/MudBlazor.UnitTests/Components/SelectTests.cs +++ b/src/MudBlazor.UnitTests/Components/SelectTests.cs @@ -724,7 +724,7 @@ public async Task TextField_Should_Validate_Data_Attribute_Fail() select.Value.Should().Be("Quux"); select.Text.Should().Be("Quux"); // check validity - await comp.InvokeAsync(() => select.Validate()); + await comp.InvokeAsync(() => select.ValidateAsync()); select.ValidationErrors.Should().NotBeEmpty(); select.ValidationErrors.Should().HaveCount(1); select.ValidationErrors[0].Should().Be("Should not be longer than 3"); @@ -742,7 +742,7 @@ public async Task TextField_Should_Validate_Data_Attribute_Success() select.Value.Should().Be("Qux"); select.Text.Should().Be("Qux"); // check validity - await comp.InvokeAsync(() => select.Validate()); + await comp.InvokeAsync(() => select.ValidateAsync()); select.ValidationErrors.Should().BeEmpty(); } #endregion @@ -756,7 +756,7 @@ public async Task Select_Should_SetRequiredTrue() var comp = Context.RenderComponent(); var select = comp.FindComponent>().Instance; select.Required.Should().BeTrue(); - await comp.InvokeAsync(() => select.Validate()); + await comp.InvokeAsync(() => select.ValidateAsync()); select.ValidationErrors.First().Should().Be("Required"); } @@ -1136,13 +1136,13 @@ public async Task MultiSelectWithRequiredValue() var comp = Context.RenderComponent(); var select = comp.FindComponent>().Instance; select.Required.Should().BeTrue(); - await comp.InvokeAsync(() => select.Validate()); + await comp.InvokeAsync(() => select.ValidateAsync()); select.ValidationErrors.First().Should().Be("Required"); //1b. Check on T type - MultiSelect of T(e.g. class object) var selectWithT = comp.FindComponent>().Instance; selectWithT.Required.Should().BeTrue(); - await comp.InvokeAsync(() => selectWithT.Validate()); + await comp.InvokeAsync(() => selectWithT.ValidateAsync()); selectWithT.ValidationErrors.First().Should().Be("Required"); //2a. Now check when SelectedItems is greater than one - Validation Should Pass @@ -1150,7 +1150,7 @@ public async Task MultiSelectWithRequiredValue() inputs[0].Click();//The 2nd one is the var items = comp.FindAll("div.mud-list-item").ToArray(); items[1].Click(); - await comp.InvokeAsync(() => select.Validate()); + await comp.InvokeAsync(() => select.ValidateAsync()); select.ValidationErrors.Count.Should().Be(0); //2b. @@ -1159,7 +1159,7 @@ public async Task MultiSelectWithRequiredValue() comp.WaitForState(() => comp.FindAll("div.mud-list-item").Count == 5); items = comp.FindAll("div.mud-list-item").ToArray(); items[3].Click(); - await comp.InvokeAsync(() => selectWithT.Validate()); + await comp.InvokeAsync(() => selectWithT.ValidateAsync()); selectWithT.ValidationErrors.Count.Should().Be(0); } diff --git a/src/MudBlazor.UnitTests/Components/TextFieldTests.cs b/src/MudBlazor.UnitTests/Components/TextFieldTests.cs index 1701f1e8114..feee348e654 100644 --- a/src/MudBlazor.UnitTests/Components/TextFieldTests.cs +++ b/src/MudBlazor.UnitTests/Components/TextFieldTests.cs @@ -468,7 +468,7 @@ public async Task TextField_Should_Validate_Data_Attribute_Fail() textfield.Value.Should().Be("Quux"); textfield.Text.Should().Be("Quux"); // check validity - await comp.InvokeAsync(() => textfield.Validate()); + await comp.InvokeAsync(() => textfield.ValidateAsync()); textfield.ValidationErrors.Should().NotBeEmpty(); textfield.ValidationErrors.Should().HaveCount(1); textfield.ValidationErrors[0].Should().Be("Should not be longer than 3"); @@ -487,7 +487,7 @@ public async Task TextField_Should_Validate_Data_Attribute_Success() textfield.Value.Should().Be("Qux"); textfield.Text.Should().Be("Qux"); // check validity - await comp.InvokeAsync(() => textfield.Validate()); + await comp.InvokeAsync(() => textfield.ValidateAsync()); textfield.ValidationErrors.Should().BeEmpty(); } @@ -510,7 +510,7 @@ public async Task TextField_Should_HaveCorrectMessageWithCustomAttr_Failing() { var model = new TestFailingModel(); var comp = Context.RenderComponent>(ComponentParameter.CreateParameter("For", (Expression>)(() => model.Foo))); - await comp.InvokeAsync(() => comp.Instance.Validate()); + await comp.InvokeAsync(() => comp.Instance.ValidateAsync()); comp.Instance.Error.Should().BeTrue(); comp.Instance.ValidationErrors.Should().HaveCount(1); comp.Instance.ValidationErrors[0].Should().Be("Foo"); @@ -533,7 +533,7 @@ public async Task TextField_Should_HaveCorrectMessageWithCustomAttr_Override_Fai ComponentParameter.CreateParameter("For", (Expression>)(() => (model as TestFailingModel2).Foo)) //ComponentParameter.CreateParameter("ForModel", typeof(TestFailingModel2)) // Explicitly set the `For` class ); - await comp.InvokeAsync(() => comp.Instance.Validate()); + await comp.InvokeAsync(() => comp.Instance.ValidateAsync()); comp.Instance.Error.Should().BeTrue(); comp.Instance.ValidationErrors.Should().HaveCount(1); comp.Instance.ValidationErrors[0].Should().Be("Bar"); @@ -559,7 +559,7 @@ public async Task TextField_Should_HaveCorrectMessageWithCustomAttr_Throwing() { var model = new TestThrowingModel(); var comp = Context.RenderComponent>(ComponentParameter.CreateParameter("For", (Expression>)(() => model.Foo))); - await comp.InvokeAsync(() => comp.Instance.Validate()); + await comp.InvokeAsync(() => comp.Instance.ValidateAsync()); comp.Instance.Error.Should().BeTrue(); comp.Instance.ValidationErrors.Should().HaveCount(1); comp.Instance.ValidationErrors[0].Should().Be("An unhandled exception occurred: This is a test exception"); @@ -710,7 +710,7 @@ public async Task TextField_Data_Annotation_Resolve_Name_Of_Field() { var model = new TestDataAnnotationModel(); var comp = Context.RenderComponent>(ComponentParameter.CreateParameter("For", (Expression>)(() => model.Foo1))); - await comp.InvokeAsync(() => comp.Instance.Validate()); + await comp.InvokeAsync(() => comp.Instance.ValidateAsync()); comp.Instance.Error.Should().BeTrue(); comp.Instance.ValidationErrors.Should().HaveCount(1); comp.Instance.ValidationErrors[0].Should().Be($"The {nameof(TestDataAnnotationModel.Foo1)} field is required."); @@ -718,7 +718,7 @@ public async Task TextField_Data_Annotation_Resolve_Name_Of_Field() await comp.InvokeAsync(() => { comp.Instance.Value = "Foo"; - comp.Instance.Validate(); + comp.Instance.ValidateAsync(); }); comp.Instance.Error.Should().BeFalse(); comp.Instance.ValidationErrors.Should().HaveCount(0); @@ -729,7 +729,7 @@ public async Task TextField_Data_Annotation_Resolve_Display_Name_Of_Field() { var model = new TestDataAnnotationModel(); var comp = Context.RenderComponent>(ComponentParameter.CreateParameter("For", (Expression>)(() => model.Foo2))); - await comp.InvokeAsync(() => comp.Instance.Validate()); + await comp.InvokeAsync(() => comp.Instance.ValidateAsync()); comp.Instance.Error.Should().BeTrue(); comp.Instance.ValidationErrors.Should().HaveCount(1); comp.Instance.ValidationErrors[0].Should().Be($"The {TestDataAnnotationModel.FooTwoDisplayName} field is required."); @@ -744,7 +744,7 @@ public async Task TextField_Data_Annotation_Compare() var comp = Context.RenderComponent>( ComponentParameter.CreateParameter("For", (Expression>)(() => model.Foo2)), ComponentParameter.CreateParameter("Value", value)); - await comp.InvokeAsync(() => comp.Instance.Validate()); + await comp.InvokeAsync(() => comp.Instance.ValidateAsync()); comp.Instance.Error.Should().BeTrue(); comp.Instance.ValidationErrors.Should().HaveCount(1); comp.Instance.ValidationErrors[0].Should().Be($"'{TestDataAnnotationModel.FooTwoDisplayName}' and '{nameof(TestDataAnnotationModel.Foo1)}' do not match."); @@ -752,7 +752,7 @@ public async Task TextField_Data_Annotation_Compare() model.Foo1 = value; await comp.InvokeAsync(() => { - comp.Instance.Validate(); + comp.Instance.ValidateAsync(); }); comp.Instance.Error.Should().BeFalse(); comp.Instance.ValidationErrors.Should().HaveCount(0); diff --git a/src/MudBlazor/Base/MudFormComponent.cs b/src/MudBlazor/Base/MudFormComponent.cs index d4a8e598192..006d20df064 100644 --- a/src/MudBlazor/Base/MudFormComponent.cs +++ b/src/MudBlazor/Base/MudFormComponent.cs @@ -292,7 +292,7 @@ protected Task BeginValidateAsync() /// /// When using a , the input is validated via the function set in the property. /// - public Task Validate() + public Task ValidateAsync() { // when a validation is forced, we must set Touched to true, because for untouched fields with // no value, validation does nothing due to the way forms are expected to work (display errors @@ -610,7 +610,7 @@ protected void FieldChanged(object? newValue) public async Task ResetAsync() { await ResetValueAsync(); - ResetValidation(); + await ResetValidationAsync(); } protected virtual Task ResetValueAsync() @@ -628,12 +628,13 @@ protected virtual Task ResetValueAsync() /// /// When called, the , , and properties are all reset. /// - public void ResetValidation() + public Task ResetValidationAsync() { Error = false; ValidationErrors.Clear(); ErrorText = null; StateHasChanged(); + return Task.CompletedTask; } #endregion @@ -674,7 +675,7 @@ internal void EditFormValidate() /// Whether the property is null. /// [MemberNotNullWhen(false, nameof(For))] - public bool IsForNull => For is null; + public bool ForIsNull => For is null; /// /// Stores the list of validation attributes attached to the property targeted by . If is null, this property is null too. diff --git a/src/MudBlazor/Components/DataGrid/DataGridRowValidator.cs b/src/MudBlazor/Components/DataGrid/DataGridRowValidator.cs index 9f04175e3a4..5435a397c03 100644 --- a/src/MudBlazor/Components/DataGrid/DataGridRowValidator.cs +++ b/src/MudBlazor/Components/DataGrid/DataGridRowValidator.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Threading.Tasks; using MudBlazor.Interfaces; namespace MudBlazor @@ -15,7 +16,7 @@ public bool IsValid { get { - Validate(); + ValidateAsync().Wait(); return Errors.Length <= 0; } } @@ -56,12 +57,12 @@ void IForm.Update(IFormComponent formControl) protected HashSet _formControls = new HashSet(); [ExcludeFromCodeCoverage] - public void Validate() + public async Task ValidateAsync() { _errors.Clear(); foreach (var formControl in _formControls.ToArray()) { - formControl.Validate(); + await formControl.ValidateAsync(); foreach (var err in formControl.ValidationErrors) { _errors.Add(err); diff --git a/src/MudBlazor/Components/DataGrid/MudDataGrid.razor.cs b/src/MudBlazor/Components/DataGrid/MudDataGrid.razor.cs index 779417747c4..a1dc7b4c0b0 100644 --- a/src/MudBlazor/Components/DataGrid/MudDataGrid.razor.cs +++ b/src/MudBlazor/Components/DataGrid/MudDataGrid.razor.cs @@ -1116,7 +1116,7 @@ internal async Task CommitItemChangesAsync(T item) /// internal async Task CommitItemChangesAsync() { - await _editForm.Validate(); + await _editForm.ValidateAsync(); if (!_editForm.IsValid) { return; diff --git a/src/MudBlazor/Components/Form/MudForm.razor.cs b/src/MudBlazor/Components/Form/MudForm.razor.cs index 97d3c41b7ae..b96cd2cec43 100644 --- a/src/MudBlazor/Components/Form/MudForm.razor.cs +++ b/src/MudBlazor/Components/Form/MudForm.razor.cs @@ -14,7 +14,7 @@ public partial class MudForm : MudComponentBase, IDisposable, IForm protected string Classname => new CssBuilder("mud-form") .AddClass(Class) - .Build(); + .Build(); /// /// Child content of component. @@ -59,19 +59,25 @@ private void SetIsValid(bool value) /// [Parameter] [Category(CategoryTypes.Form.Behavior)] - public bool IsTouched { get => _touched; set {/* readonly parameter! */ } } + public bool IsTouched + { + get => _touched; + set {/* readonly parameter! */ } + } private bool _touched = false; [Parameter] [Category(CategoryTypes.Form.Behavior)] public bool Disabled { get; set; } + [CascadingParameter(Name = "ParentDisabled")] private bool ParentDisabled { get; set; } protected bool GetDisabledState() => Disabled || ParentDisabled; [Parameter] [Category(CategoryTypes.Form.Behavior)] public bool ReadOnly { get; set; } + [CascadingParameter(Name = "ParentReadOnly")] private bool ParentReadOnly { get; set; } protected bool GetReadOnlyState() => ReadOnly || ParentReadOnly; @@ -106,20 +112,24 @@ private void SetIsValid(bool value) /// /// Raised when IsValid changes. /// - [Parameter] public EventCallback IsValidChanged { get; set; } + [Parameter] + public EventCallback IsValidChanged { get; set; } /// /// Raised when IsTouched changes. /// - [Parameter] public EventCallback IsTouchedChanged { get; set; } + [Parameter] + public EventCallback IsTouchedChanged { get; set; } /// /// Raised when a contained IFormComponent changes its value /// - [Parameter] public EventCallback FieldChanged { get; set; } + [Parameter] + public EventCallback FieldChanged { get; set; } // keeps track of validation. if the input was validated at least once the value will be true protected HashSet _formControls = new(); + protected HashSet _errors = new(); /// @@ -157,7 +167,8 @@ public string[] Errors set { /* readonly */ } } - [Parameter] public EventCallback ErrorsChanged { get; set; } + [Parameter] + public EventCallback ErrorsChanged { get; set; } /// /// Specifies the top-level model object for the form. Used with Fluent Validation @@ -168,7 +179,7 @@ public string[] Errors public object? Model { get; set; } #nullable disable - private HashSet ChildForms { get; set; } = new HashSet(); + private HashSet ChildForms { get; set; } = new(); [CascadingParameter] private MudForm ParentMudForm { get; set; } @@ -198,33 +209,33 @@ void IForm.Remove(IFormComponent formControl) /// void IForm.Update(IFormComponent formControl) { - EvaluateForm(); + // Note: We don't want to make IForm.Update async because it makes no sense for the inputs to wait + // for form evaluation, especially when multiple input parameters change which results in several form + // updates which are debounced anyway via a timer. + + // Validation exceptions are observed via AndForget() which reports exceptions via MudGlobal.UnhandledExceptionHandler + EvaluateFormAsync().AndForget(); } - private void EvaluateForm(bool debounce = true) + private Task EvaluateFormAsync(bool debounce = true) { _timer?.Dispose(); if (debounce && ValidationDelay > 0) _timer = new Timer(OnTimerComplete, null, ValidationDelay, Timeout.Infinite); else - _ = OnEvaluateForm(); + return OnEvaluateFormAsync(); + return Task.CompletedTask; } private void OnTimerComplete(object stateInfo) { - try - { - InvokeAsync(OnEvaluateForm); - } - catch (Exception e) - { - Console.WriteLine($"An error occured while executing {nameof(OnEvaluateForm)}: {e.Message}"); - } + // Note: AndForget reports exceptions via MudGlobal.UnhandledExceptionHandler + InvokeAsync(OnEvaluateFormAsync).AndForget(); } private bool _shouldRender = true; // <-- default is true, we need the form children to render - protected async Task OnEvaluateForm() + protected async Task OnEvaluateFormAsync() { _errors.Clear(); foreach (var error in _formControls.SelectMany(control => control.ValidationErrors)) @@ -232,18 +243,18 @@ protected async Task OnEvaluateForm() // form can only be valid if: // - none have an error // - all required fields have been touched (and thus validated) - var no_errors = _formControls.All(x => x.HasErrors == false); - var required_all_touched = _formControls.Where(x => x.Required).All(x => x.Touched); - var valid = no_errors && required_all_touched; + var noErrors = _formControls.All(x => x.HasErrors == false); + var requiredAllTouched = _formControls.Where(x => x.Required).All(x => x.Touched); + var valid = noErrors && requiredAllTouched; - var old_touched = _touched; + var oldTouched = _touched; _touched = _formControls.Any(x => x.Touched); try { _shouldRender = false; SetIsValid(valid); await ErrorsChanged.InvokeAsync(Errors); - if (old_touched != _touched) + if (oldTouched != _touched) await IsTouchedChanged.InvokeAsync(_touched); } finally @@ -262,16 +273,16 @@ protected override bool ShouldRender() /// /// Force a validation of all form controls, even if they haven't been touched by the user yet. /// - public async Task Validate() + public async Task ValidateAsync() { - await Task.WhenAll(_formControls.Select(x => x.Validate())); + await Task.WhenAll(_formControls.Select(x => x.ValidateAsync())); if (ChildForms.Count > 0) { - await Task.WhenAll(ChildForms.Select(x => x.Validate())); + await Task.WhenAll(ChildForms.Select(x => x.ValidateAsync())); } - EvaluateForm(debounce: false); + await EvaluateFormAsync(debounce: false); } /// @@ -289,25 +300,17 @@ public async Task ResetAsync() await form.ResetAsync(); } - EvaluateForm(debounce: false); + await EvaluateFormAsync(debounce: false); } /// /// Reset the validation state but keep the values. /// - public void ResetValidation() + public async Task ResetValidationAsync() { - foreach (var control in _formControls.ToArray()) - { - control.ResetValidation(); - } - - foreach (var form in ChildForms) - { - form.ResetValidation(); - } - - EvaluateForm(debounce: false); + await Task.WhenAll(_formControls.Select(x => x.ResetValidationAsync())); + await Task.WhenAll(ChildForms.Select(x => x.ResetValidationAsync())); + await EvaluateFormAsync(debounce: false); } /// @@ -336,9 +339,10 @@ protected override Task OnAfterRenderAsync(bool firstRender) private void SetDefaultControlValidation(IFormComponent formComponent) { - if (Validation == null) return; + if (Validation == null) + return; - if (!formComponent.IsForNull && (formComponent.Validation == null || (OverrideFieldValidation ?? true))) + if (!formComponent.ForIsNull && (formComponent.Validation == null || (OverrideFieldValidation ?? true))) { formComponent.Validation = Validation; } @@ -346,10 +350,7 @@ private void SetDefaultControlValidation(IFormComponent formComponent) protected override void OnInitialized() { - if (ParentMudForm != null) - { - ParentMudForm.ChildForms.Add(this); - } + ParentMudForm?.ChildForms.Add(this); base.OnInitialized(); } @@ -357,11 +358,8 @@ protected override void OnInitialized() public void Dispose() { _timer?.Dispose(); - if (ParentMudForm != null) - { - ParentMudForm.ChildForms.Remove(this); - ParentMudForm.EvaluateForm(); // Need this to refresh the form state - } + ParentMudForm?.ChildForms.Remove(this); + ParentMudForm?.EvaluateFormAsync().AndForget(); // Need this to refresh the form state. AndForget reports errors via MudGlobal.UnhandledExceptionHandler } } } diff --git a/src/MudBlazor/Components/Table/TableRowValidator.cs b/src/MudBlazor/Components/Table/TableRowValidator.cs index dec3862ff43..7dedae39e48 100644 --- a/src/MudBlazor/Components/Table/TableRowValidator.cs +++ b/src/MudBlazor/Components/Table/TableRowValidator.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using MudBlazor.Interfaces; namespace MudBlazor @@ -10,7 +11,7 @@ public bool IsValid { get { - Validate(); + ValidateAsync().Wait(); return Errors.Length <= 0; } } @@ -48,12 +49,12 @@ void IForm.Update(IFormComponent formControl) protected HashSet _formControls = new(); - public void Validate() + public async Task ValidateAsync() { _errors.Clear(); foreach (var formControl in _formControls.ToArray()) { - formControl.Validate(); + await formControl.ValidateAsync(); foreach (var err in formControl.ValidationErrors) { _errors.Add(err); diff --git a/src/MudBlazor/Interfaces/IFormComponent.cs b/src/MudBlazor/Interfaces/IFormComponent.cs index c4f1266c48f..1ec25b3a66d 100644 --- a/src/MudBlazor/Interfaces/IFormComponent.cs +++ b/src/MudBlazor/Interfaces/IFormComponent.cs @@ -14,10 +14,10 @@ public interface IFormComponent public bool HasErrors { get; } public bool Touched { get; } public object Validation { get; set; } - public bool IsForNull { get; } + public bool ForIsNull { get; } public List ValidationErrors { get; set; } - public Task Validate(); + public Task ValidateAsync(); public Task ResetAsync(); - public void ResetValidation(); + public Task ResetValidationAsync(); } }