diff --git a/src/BootstrapBlazor/Components/Table/Table.razor b/src/BootstrapBlazor/Components/Table/Table.razor index 88533b388a9..e464b841182 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor +++ b/src/BootstrapBlazor/Components/Table/Table.razor @@ -787,19 +787,7 @@ { @RenderRowExtendButtons(item) } - @if (RowContentTemplate != null) - { - var columns = GetVisibleColumns(); - @RowContentTemplate(new(item, columns, ActiveRenderMode)) - } - else - { - @RenderContentRow(item) - } - @if (ShowExtendButtons && !IsExtendButtonsInRowHeader) - { - @RenderRowExtendButtons(item) - } + @RenderRowCell(item) ; RenderFragment> RenderContentCell => context => @@ -1152,4 +1140,9 @@ OnValueChanged="visible => OnToggleColumnVisible(item.Name, visible)"> ; + + RenderFragment RenderRowContentWithValidateForm => item => + @ + @RenderRowContent(item) + ; } diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.Edit.cs b/src/BootstrapBlazor/Components/Table/Table.razor.Edit.cs index b5637b6bdf6..f4136cd20ee 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.Edit.cs +++ b/src/BootstrapBlazor/Components/Table/Table.razor.Edit.cs @@ -703,6 +703,12 @@ private async Task ClickEditButton(TItem item) private async Task ClickUpdateButtonCallback() { + // 验证 InCell 模式下的表单 + if (!_inCellValidateForm.Validate()) + { + return; + } + var context = new EditContext(EditModel); await SaveAsync(context, AddInCell ? ItemChangedType.Add : ItemChangedType.Update); } diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.Toolbar.cs b/src/BootstrapBlazor/Components/Table/Table.razor.Toolbar.cs index e8b4f1699ab..f17a817dc23 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.Toolbar.cs +++ b/src/BootstrapBlazor/Components/Table/Table.razor.Toolbar.cs @@ -499,6 +499,11 @@ private bool GetColumnsListState(ColumnVisibleItem item) private bool InCellMode => AddInCell || EditInCell; + /// + /// 获得 InCell 模式下的 ValidateForm 实例 + /// + private ValidateForm _inCellValidateForm = default!; + /// /// 新建按钮方法 /// diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.cs b/src/BootstrapBlazor/Components/Table/Table.razor.cs index 7b85ff48ce4..cbcec66299a 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.cs +++ b/src/BootstrapBlazor/Components/Table/Table.razor.cs @@ -1501,7 +1501,7 @@ RenderFragment RenderTemplate() => col.Template == null : col.Template(item); RenderFragment RenderEditTemplate() => col.EditTemplate == null - ? new RenderFragment(builder => builder.CreateComponentByFieldType(this, col, item, changedType, isSearch: false, col.GetLookupService(InjectLookupService), skipValidate: true)) + ? new RenderFragment(builder => builder.CreateComponentByFieldType(this, col, item, changedType, isSearch: false, col.GetLookupService(InjectLookupService), skipValidate: false)) : col.EditTemplate(item); } @@ -1840,6 +1840,36 @@ private void OnTouchEnd() private object? GetKeyByITem(TItem item) => SortableList != null ? item : null; //OnGetRowKey?.Invoke(item); + private RenderFragment RenderRowCell(TItem item) => builder => + { + var isInCellEditRow = InCellMode && SelectedRows.FirstOrDefault() == item; + if (isInCellEditRow) + { + builder.AddContent(0, RenderRowContentWithValidateForm(item)); + } + else + { + builder.AddContent(1, RenderRowContent(item)); + } + }; + + private RenderFragment RenderRowContent(TItem item) => builder => + { + if (RowContentTemplate != null) + { + var columns = GetVisibleColumns(); + builder.AddContent(0, RowContentTemplate(new(item, columns, ActiveRenderMode))); + } + else + { + builder.AddContent(1, RenderContentRow(item)); + } + if (ShowExtendButtons && !IsExtendButtonsInRowHeader) + { + builder.AddContent(2, RenderRowExtendButtons(item)); + } + }; + /// /// Dispose 方法 /// diff --git a/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor b/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor index 9b5ae3d6e45..a4dd260505d 100644 --- a/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor +++ b/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor @@ -1,16 +1,26 @@ -@namespace BootstrapBlazor.Components +@namespace BootstrapBlazor.Components @inherits BootstrapModuleComponentBase @attribute [BootstrapModuleAutoLoader] @if (Model != null) { - - - @ChildContent - + @if (IsFormless) + { + + + @ChildContent + + } + else + { + + + @ChildContent + + } } diff --git a/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs b/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs index 4cd3093e639..271577b7ebe 100644 --- a/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs +++ b/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs @@ -83,6 +83,13 @@ public partial class ValidateForm [Parameter] public bool? ShowLabelTooltip { get; set; } + /// + /// 获得/设置 是否为无表单模式 默认 false + /// + /// 设置为 true 时不渲染 form 元素,仅级联 EditContext 用于 Table InCell 编辑模式 + [Parameter] + public bool IsFormless { get; set; } + /// /// 获得/设置 是否禁用表单内回车自动提交功能 默认 null 未设置 /// @@ -146,6 +153,12 @@ protected override void OnParametersSet() { DisableAutoSubmitFormByEnter = BootstrapBlazorOptions.CurrentValue.DisableAutoSubmitFormByEnter.Value; } + + // 无表单模式下创建/更新 EditContext + if (IsFormless) + { + _formlessEditContext ??= new EditContext(Model); + } } /// @@ -330,6 +343,13 @@ internal async Task ValidateObject(ValidationContext context, List 0; + } } } @@ -636,6 +656,11 @@ private async Task OnInvalidSubmitForm(EditContext context) [NotNull] private BootstrapBlazorDataAnnotationsValidator? Validator { get; set; } + /// + /// 获得/设置 无表单模式下的 EditContext 实例 + /// + private EditContext? _formlessEditContext; + /// /// 验证方法 用于代码调用触发表单验证 /// diff --git a/test/UnitTest/Components/TableTest.cs b/test/UnitTest/Components/TableTest.cs index 6c9fb74cc91..602cc7aff1d 100644 --- a/test/UnitTest/Components/TableTest.cs +++ b/test/UnitTest/Components/TableTest.cs @@ -3887,6 +3887,19 @@ public async Task InCell_Ok() }); var button = cut.Find("tbody tr td button"); await cut.InvokeAsync(() => button.Click()); + + // 增加 Validate 测试 + // 设置姓名为 null 保存按钮不成功 + var nameField = cut.Find("tbody tr td input"); + Assert.Equal("张三 0001", nameField.GetAttribute("value")); + + await cut.InvokeAsync(() => nameField.Change("")); + Assert.Contains("is-invalid", nameField.ToMarkup()); + await cut.InvokeAsync(() => button.Click()); + + await cut.InvokeAsync(() => nameField.Change("张三 0001")); + Assert.Contains("is-valid", nameField.ToMarkup()); + await cut.InvokeAsync(() => button.Click()); } [Fact]