Skip to content

Conversation

@ArgoZhang
Copy link
Member

@ArgoZhang ArgoZhang commented Nov 17, 2025

Link issues

fixes #7137

Summary By Copilot

Regression?

  • Yes
  • No

Risk

  • High
  • Medium
  • Low

Verification

  • Manual (required)
  • Automated

Packaging changes reviewed?

  • Yes
  • No
  • N/A

☑️ Self Check before Merge

⚠️ Please check all items below before review. ⚠️

  • Doc is updated/provided or not needed
  • Demo is updated/provided or not needed
  • Merge the latest code from the main branch

Summary by Sourcery

Introduce optional automatic re-rendering in EditorForm and update samples to showcase inline editing templates while cleaning up obsolete demo components

New Features:

  • Add IsRenderWhenValueChanged parameter to EditorForm to control automatic re-render on field value changes
  • Enable IsRenderWhenValueChanged by default in the EditDialog component

Enhancements:

  • Implement IDisposable in EditorForm to unsubscribe from OnFieldChanged events for proper cleanup
  • Update table editing sample (TablesEdit) with inline Select editor templates and display logic

Chores:

  • Remove obsolete DemoTableEditTemplate component files

Copilot AI review requested due to automatic review settings November 17, 2025 02:03
@bb-auto bb-auto bot added the enhancement New feature or request label Nov 17, 2025
@bb-auto bb-auto bot added this to the 10.0.0 milestone Nov 17, 2025
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Nov 17, 2025

Reviewer's Guide

This PR extends EditorForm to support optional live re-rendering on field value changes by subscribing to EditContext.OnFieldChanged, ensures proper disposal of subscriptions, enables the feature in EditDialog, and updates the TablesEdit sample with a custom EditTemplate while removing obsolete components.

Sequence diagram for EditorForm live re-rendering on field value change

sequenceDiagram
    participant EditContext
    participant EditorForm
    EditContext->>EditorForm: OnFieldChanged event
    EditorForm->>EditorForm: NotifyValueChanged()
    alt IsRenderWhenValueChanged == true
        EditorForm->>EditorForm: StateHasChanged() (re-render)
    end
Loading

Class diagram for updated EditorForm with IsRenderWhenValueChanged and IDisposable

classDiagram
    class EditorForm_TModel {
        +bool IsRenderWhenValueChanged
        +void Dispose()
        -void NotifyValueChanged(object? sender, FieldChangedEventArgs args)
    }
    EditorForm_TModel --|> IDisposable
    EditorForm_TModel --|> IShowLabel
Loading

Class diagram for removal of DemoTableEditTemplate component

classDiagram
    class DemoTableEditTemplate {
        <<removed>>
    }
Loading

Class diagram for TablesEdit sample with custom EditTemplate and helper method

classDiagram
    class TablesEdit {
        -string? GetEducationDesc(EnumEducation? item)
    }
Loading

File-Level Changes

Change Details Files
Support live re-rendering of EditorForm on value changes
  • Add IsRenderWhenValueChanged parameter to control re-render behavior
  • Subscribe to EditContext.OnFieldChanged in OnInitialized
  • Implement NotifyValueChanged method to conditionally call StateHasChanged
  • Implement IDisposable to unsubscribe and suppress finalization in Dispose
src/BootstrapBlazor/Components/EditorForm/EditorForm.razor.cs
Enable live updates in EditDialog
  • Set IsRenderWhenValueChanged="true" on the EditorForm instance
src/BootstrapBlazor/Components/Dialog/EditDialog.razor
Update TablesEdit sample to demonstrate live editing and cleanup obsolete components
  • Add custom EditTemplate for the Education field with Select and Display components
  • Introduce GetEducationDesc helper for field description rendering
  • Remove the deprecated DemoTableEditTemplate component and related files
src/BootstrapBlazor.Server/Components/Samples/Table/TablesEdit.razor
src/BootstrapBlazor.Server/Components/Samples/Table/TablesEdit.razor.cs
src/BootstrapBlazor.Server/Components/Components/DemoTableEditTemplate.razor
src/BootstrapBlazor.Server/Components/Components/DemoTableEditTemplate.razor.cs

Assessment against linked issues

Issue Objective Addressed Explanation
#7137 Add IsRenderWhenValueChanged parameter to EditorForm component.
#7137 Implement logic in EditorForm to trigger re-rendering when field values change if IsRenderWhenValueChanged is true.
#7137 Update related components and samples to demonstrate and utilize the new IsRenderWhenValueChanged parameter.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

sourcery-ai[bot]
sourcery-ai bot previously approved these changes Nov 17, 2025
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location> `src/BootstrapBlazor/Components/EditorForm/EditorForm.razor.cs:15` </location>
<code_context>
 /// </summary>
 [CascadingTypeParameter(nameof(TModel))]
-public partial class EditorForm<TModel> : IShowLabel
+public partial class EditorForm<TModel> : IShowLabel, IEditorFormValueChanged
 {
     private string? ClassString => CssBuilder.Default("bb-editor")
</code_context>

<issue_to_address>
**issue (complexity):** Consider replacing the custom value change interface and method with a direct EditContext.OnFieldChanged subscription for automatic re-rendering.

```markdown
You can drop the new interface + `NotifyValueChanged()` and just hook into the existing `EditContext.OnFieldChanged` to trigger `StateHasChanged()`. This removes the indirection and keeps all functionality.

1. Remove
   - `IEditorFormValueChanged` from the class signature
   - the `IsRenderWhenValueChanged` parameter
   - the `NotifyValueChanged()` method

2. Add an `OnInitialized` (or `OnParametersSet`) subscription:

```csharp
public partial class EditorForm<TModel> : IShowLabel, IDisposable
{
    [Parameter]
    public bool AutoRefreshOnFieldChanged { get; set; }

    [CascadingParameter] private EditContext? CascadedEditContext { get; set; }

    private EventHandler<FieldChangedEventArgs>? _fieldChangedHandler;

    protected override void OnInitialized()
    {
        if (AutoRefreshOnFieldChanged && CascadedEditContext != null)
        {
            _fieldChangedHandler = (_, __) => InvokeAsync(StateHasChanged);
            CascadedEditContext.OnFieldChanged += _fieldChangedHandler;
        }
    }

    public void Dispose()
    {
        if (_fieldChangedHandler != null && CascadedEditContext != null)
        {
            CascadedEditContext.OnFieldChanged -= _fieldChangedHandler;
        }
    }
}
```

3. Update your consumers to use `AutoRefreshOnFieldChanged` instead of `IsRenderWhenValueChanged`.

This way you get the same re-render-on-value-change behavior without a custom interface or manual `NotifyValueChanged()` calls.
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@codecov
Copy link

codecov bot commented Nov 17, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (b7e3028) to head (ff7135e).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #7138   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          745       745           
  Lines        32548     32566   +18     
  Branches      4510      4513    +3     
=========================================
+ Hits         32548     32566   +18     
Flag Coverage Δ
BB 100.00% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds the IsRenderWhenValueChanged parameter to the EditorForm component, enabling automatic re-rendering when child component values change. This addresses issue #7137 by allowing form components to update reactively without requiring manual component encapsulation.

Key Changes:

  • Introduces IEditorFormValueChanged interface with a NotifyValueChanged() method for value change notifications
  • Adds IsRenderWhenValueChanged parameter to EditorForm (default: false) to control re-rendering behavior
  • Updates ValidateBase to notify parent EditorForm when values change through cascading parameters
  • Simplifies demo code by removing the need for separate component encapsulation (DemoTableEditTemplate) to achieve reactive updates

Reviewed Changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/BootstrapBlazor/Components/EditorForm/IEditorFormValueChanged.cs New interface defining the value change notification contract
src/BootstrapBlazor/Components/Validate/ValidateBase.cs Adds cascading parameter to receive EditorForm instance and notify on value changes
src/BootstrapBlazor/Components/EditorForm/EditorForm.razor.cs Implements IEditorFormValueChanged interface and adds IsRenderWhenValueChanged parameter
src/BootstrapBlazor/Components/Dialog/EditDialog.razor Sets IsRenderWhenValueChanged="true" to enable reactive updates in edit dialogs
src/BootstrapBlazor.Server/Locales/zh-CN.json Updates documentation text to reflect version 10.0.1 supporting reactive functionality
src/BootstrapBlazor.Server/Components/Samples/Table/TablesEdit.razor Refactors example to use inline EditTemplate instead of separate component
src/BootstrapBlazor.Server/Components/Samples/Table/TablesEdit.razor.cs Moves education description logic from separate component to main page
src/BootstrapBlazor.Server/Components/Components/DemoTableEditTemplate.razor.cs Removes obsolete separate component (no longer needed)
src/BootstrapBlazor.Server/Components/Components/DemoTableEditTemplate.razor Removes obsolete separate component template

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@ArgoZhang
Copy link
Member Author

@sourcery-ai review

sourcery-ai[bot]
sourcery-ai bot previously approved these changes Nov 17, 2025
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location> `src/BootstrapBlazor.Server/Components/Samples/Table/TablesEdit.razor:61-70` </location>
<code_context>
            ShowToolbar="true" ShowExtendButtons="true" ShowSkeleton="true">
         <TableColumns>
             <TableColumn @bind-Field="@context.Complete" Align="Alignment.Center"></TableColumn>
+            <TableColumn @bind-Field="@context.Education">
+                <EditTemplate Context="v">
+                    <div class="col-12 col-sm-6">
+                    </div>
+                    <div class="col-12 col-sm-6">
+                        <Select @bind-Value="@v.Education"></Select>
+                    </div>
+                    <div class="col-12 col-sm-6">
+                        <Display Value="@GetEducationDesc(v.Education)" ShowLabel="true" DisplayText="@Localizer["TablesEditTemplateDisplayLabel"]"></Display>
+                    </div>
+                </EditTemplate>
</code_context>

<issue_to_address>
**suggestion:** EditTemplate for Education column contains empty divs.

Please remove or merge the empty grid divs to simplify the markup and enhance code clarity.

```suggestion
                <EditTemplate Context="v">
                    <div class="col-12 col-sm-6">
                        <Select @bind-Value="@v.Education"></Select>
                    </div>
                    <div class="col-12 col-sm-6">
                        <Display Value="@GetEducationDesc(v.Education)" ShowLabel="true" DisplayText="@Localizer["TablesEditTemplateDisplayLabel"]"></Display>
                    </div>
                </EditTemplate>
```
</issue_to_address>

### Comment 2
<location> `src/BootstrapBlazor/Components/EditorForm/EditorForm.razor.cs:162` </location>
<code_context>
-
-namespace BootstrapBlazor.Server.Components.Components;
-
-/// <summary>
-/// DemoTableEditTemplate component
-/// </summary>
</code_context>

<issue_to_address>
**issue (complexity):** Consider simplifying the Dispose implementation by removing the virtual Dispose(bool) method and finalizer suppression, and only unsubscribing in IDisposable.Dispose.

```csharp
// Drop the full Dispose pattern (no finalizer, no virtual Dispose(bool))
// and implement IDisposable.Dispose directly to just unsubscribe:
public partial class EditorForm<TModel> : IShowLabel, IDisposable
{
    // ...

    protected override void OnInitialized()
    {
        base.OnInitialized();
        if (CascadedEditContext != null)
        {
            CascadedEditContext.OnFieldChanged += NotifyValueChanged;
        }
    }

    private void NotifyValueChanged(object? sender, FieldChangedEventArgs args)
    {
        if (IsRenderWhenValueChanged)
        {
            StateHasChanged();
        }
    }

    public void Dispose()
    {
        // only managed cleanup needed
        if (CascadedEditContext != null)
        {
            CascadedEditContext.OnFieldChanged -= NotifyValueChanged;
        }
    }
}
```

Steps:
1. Remove the `protected virtual Dispose(bool)` method.
2. Remove the `GC.SuppressFinalize(this)` call.
3. Keep only the `public void Dispose()` method that unsubscribes from `OnFieldChanged`.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@ArgoZhang
Copy link
Member Author

@sourcery-ai review

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location> `src/BootstrapBlazor/Components/EditorForm/EditorForm.razor.cs:330-333` </location>
<code_context>
+        {
+            CascadedEditContext.OnFieldChanged -= NotifyValueChanged;
+        }
+        GC.SuppressFinalize(this);
+    }
 }
</code_context>

<issue_to_address>
**suggestion:** Suppressing finalization may be unnecessary if no finalizer is defined.

Consider removing GC.SuppressFinalize(this) to improve code clarity, as EditorForm lacks a finalizer.

```suggestion
        }
    }
 }
```
</issue_to_address>

### Comment 2
<location> `src/BootstrapBlazor/Components/EditorForm/EditorForm.razor.cs:15` </location>
<code_context>
 /// </summary>
 [CascadingTypeParameter(nameof(TModel))]
-public partial class EditorForm<TModel> : IShowLabel
+public partial class EditorForm<TModel> : IShowLabel, IDisposable
 {
     private string? ClassString => CssBuilder.Default("bb-editor")
</code_context>

<issue_to_address>
**issue (complexity):** Consider subscribing and unsubscribing to the event only when IsRenderWhenValueChanged is true, and remove unnecessary finalizer suppression.

```csharp
// 1) Only hook/unhook the event when IsRenderWhenValueChanged is true
// 2) Drop GC.SuppressFinalize (no finalizer)
// 3) Keep Dispose light-weight

public partial class EditorForm<TModel> : IShowLabel, IDisposable
{
    protected override void OnInitialized()
    {
        base.OnInitialized();

        if (CascadedEditContext != null && IsRenderWhenValueChanged)
        {
            CascadedEditContext.OnFieldChanged += NotifyValueChanged;
        }

        // ...existing validation/mapping logic...
    }

    private void NotifyValueChanged(object? sender, FieldChangedEventArgs args)
    {
        // perf: only re-render when feature is enabled
        StateHasChanged();
    }

    public void Dispose()
    {
        if (CascadedEditContext != null && IsRenderWhenValueChanged)
        {
            CascadedEditContext.OnFieldChanged -= NotifyValueChanged;
        }
    }
}
```

- This removes the unconditional subscription/Dispose ceremony when the feature is off.  
- We also drop `GC.SuppressFinalize(this)` since there’s no finalizer.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(EditorForm): add IsRenderWhenValueChanged parameter

1 participant