diff --git a/src/BootstrapBlazor/Components/Layout/Layout.razor b/src/BootstrapBlazor/Components/Layout/Layout.razor index c3593ca37ea..e21e9a40bd1 100644 --- a/src/BootstrapBlazor/Components/Layout/Layout.razor +++ b/src/BootstrapBlazor/Components/Layout/Layout.razor @@ -148,7 +148,7 @@ RefreshToolbarTooltipText="@RefreshToolbarTooltipText" FullscreenToolbarTooltipText="@FullscreenToolbarTooltipText" OnToolbarRefreshCallback="OnToolbarRefreshCallback" TabHeader="TabHeader" Body="@Main" NotAuthorized="NotAuthorized!" NotFound="NotFound!" NotFoundTabText="@NotFoundTabText" - EnableErrorLogger="EnableErrorLogger" ShowErrorLoggerToast="ShowErrorLoggerToast" + EnableErrorLogger="@_enableErrorLogger" ShowErrorLoggerToast="@_showToast" ErrorLoggerToastTitle="@ErrorLoggerToastTitle"> ; diff --git a/src/BootstrapBlazor/Components/Tab/TabItemContent.cs b/src/BootstrapBlazor/Components/Tab/TabItemContent.cs index a9e7662be0b..cc0af4b8653 100644 --- a/src/BootstrapBlazor/Components/Tab/TabItemContent.cs +++ b/src/BootstrapBlazor/Components/Tab/TabItemContent.cs @@ -7,7 +7,7 @@ namespace BootstrapBlazor.Components; -class TabItemContent : IComponent +class TabItemContent : IComponent, IHandlerException, IDisposable { /// /// Gets or sets the component content. Default is null @@ -18,6 +18,15 @@ class TabItemContent : IComponent [CascadingParameter, NotNull] private Tab? TabSet { get; set; } + [Inject, NotNull] + private DialogService? DialogService { get; set; } + + [Inject] + [NotNull] + private IOptionsMonitor? Options { get; set; } + + private ErrorLogger? _logger; + private RenderHandle _renderHandle; void IComponent.Attach(RenderHandle renderHandle) @@ -55,11 +64,17 @@ private RenderFragment RenderItemContent(RenderFragment? content) => builder => builder.OpenComponent(0); builder.AddAttribute(1, nameof(ErrorLogger.ChildContent), content); - var enableErrorLogger = TabSet.EnableErrorLogger; - var showToast = TabSet.ShowErrorLoggerToast; + var enableErrorLogger = TabSet.EnableErrorLogger ?? Options.CurrentValue.EnableErrorLogger; + var showToast = TabSet.ShowErrorLoggerToast ?? Options.CurrentValue.ShowErrorLoggerToast; builder.AddAttribute(2, nameof(ErrorLogger.EnableErrorLogger), enableErrorLogger); builder.AddAttribute(3, nameof(ErrorLogger.ShowToast), showToast); builder.AddAttribute(4, nameof(ErrorLogger.ToastTitle), TabSet.ErrorLoggerToastTitle); + builder.AddAttribute(5, nameof(ErrorLogger.OnInitializedCallback), new Func(logger => + { + _logger = logger; + _logger.Register(this); + return Task.CompletedTask; + })); builder.CloseComponent(); }; @@ -75,4 +90,20 @@ public void Render() _key = new object(); RenderContent(); } + + /// + /// HandlerException 错误处理方法 + /// + /// + /// + public Task HandlerException(Exception ex, RenderFragment errorContent) => DialogService.ShowErrorHandlerDialog(errorContent(ex)); + + /// + /// IDispose 方法用于释放资源 + /// + public void Dispose() + { + _logger?.UnRegister(this); + GC.SuppressFinalize(this); + } } diff --git a/test/UnitTest/Components/ErrorLoggerTest.cs b/test/UnitTest/Components/ErrorLoggerTest.cs index dffeeec1bb2..14e744cf933 100644 --- a/test/UnitTest/Components/ErrorLoggerTest.cs +++ b/test/UnitTest/Components/ErrorLoggerTest.cs @@ -185,4 +185,46 @@ public void ErrorContent_Ok() cut.InvokeAsync(() => button.Click()); cut.Contains("Attempted to divide by zero.error_content_template"); } + + [Fact] + public async Task TabItem_Error() + { + var cut = Context.RenderComponent(pb => + { + pb.AddChildContent(pb => + { + pb.AddChildContent(pb => + { + pb.Add(a => a.Text, "Text1"); + pb.Add(a => a.ChildContent, builder => builder.AddContent(0, RenderButton())); + }); + }); + }); + + var button = cut.Find("button"); + await cut.InvokeAsync(() => button.Click()); + + // 页面不崩溃,由弹窗显示异常信息 + cut.Contains("
TimeStamp:"); + + // 单元测试覆盖 TabItemContent Dispose 方法 + var handler = Activator.CreateInstance("BootstrapBlazor", "BootstrapBlazor.Components.TabItemContent"); + Assert.NotNull(handler); + var content = handler.Unwrap(); + Assert.NotNull(content); + + Assert.IsType(content, exactMatch: false); + ((IDisposable)content).Dispose(); + } + + private RenderFragment RenderButton() => builder => + { + builder.OpenComponent