Skip to content

Commit fdd3902

Browse files
committed
refactor: Outlet/Content 组件
1 parent fae04df commit fdd3902

File tree

2 files changed

+217
-4
lines changed

2 files changed

+217
-4
lines changed

src/BootstrapBlazor/Components/BootstrapBlazorRootOutlet/BootstrapBlazorRootContent.cs

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,91 @@
66
namespace BootstrapBlazor.Components;
77

88
/// <summary>
9-
/// BootstrapBlazorRootContent 组件
9+
/// BootstrapBlazorRootContent Component
1010
/// </summary>
11-
public class BootstrapBlazorRootContent : ComponentBase
11+
public class BootstrapBlazorRootContent : IComponent, IDisposable
1212
{
13+
private object? _registeredIdentifier;
14+
15+
/// <summary>
16+
/// Gets or sets the <see cref="string"/> ID that determines which <see cref="BootstrapBlazorRootOutlet"/> instance will render
17+
/// the content of this instance.
18+
/// </summary>
19+
[Parameter] public string? RootName { get; set; }
20+
21+
/// <summary>
22+
/// Gets or sets the <see cref="object"/> ID that determines which <see cref="BootstrapBlazorRootOutlet"/> instance will render
23+
/// the content of this instance.
24+
/// </summary>
25+
[Parameter] public object? RootId { get; set; }
26+
27+
/// <summary>
28+
/// Gets or sets the content.
29+
/// </summary>
30+
[Parameter]
31+
public RenderFragment? ChildContent { get; set; }
32+
33+
[Inject]
34+
private BootstrapBlazorRootRegisterService RootRegisterService { get; set; } = default!;
35+
36+
/// <summary>
37+
/// <inheritdoc/>
38+
/// </summary>
39+
/// <param name="renderHandle"></param>
40+
void IComponent.Attach(RenderHandle renderHandle)
41+
{
42+
43+
}
44+
45+
/// <summary>
46+
/// <inheritdoc/>
47+
/// </summary>
48+
/// <param name="parameters"></param>
49+
/// <returns></returns>
50+
Task IComponent.SetParametersAsync(ParameterView parameters)
51+
{
52+
parameters.SetParameterProperties(this);
53+
54+
object? identifier = null;
55+
56+
if (RootName is not null && RootId is not null)
57+
{
58+
throw new InvalidOperationException($"{nameof(BootstrapBlazorRootContent)} requires that '{nameof(RootName)}' and '{nameof(RootId)}' cannot both have non-null values.");
59+
}
60+
else if (RootName is not null)
61+
{
62+
identifier = RootName;
63+
}
64+
else if (RootId is not null)
65+
{
66+
identifier = RootId;
67+
}
68+
identifier ??= BootstrapBlazorRootOutlet.DefaultIdentifier;
69+
70+
if (!Equals(identifier, _registeredIdentifier))
71+
{
72+
if (_registeredIdentifier is not null)
73+
{
74+
RootRegisterService.RemoveProvider(_registeredIdentifier, this);
75+
}
76+
77+
RootRegisterService.AddProvider(identifier, this);
78+
_registeredIdentifier = identifier;
79+
}
80+
81+
RootRegisterService.NotifyContentProviderChanged(identifier, this);
82+
return Task.CompletedTask;
83+
}
84+
85+
/// <summary>
86+
/// <inheritdoc/>
87+
/// </summary>
88+
public void Dispose()
89+
{
90+
if (_registeredIdentifier is not null)
91+
{
92+
RootRegisterService.RemoveProvider(_registeredIdentifier, this);
93+
}
94+
GC.SuppressFinalize(this);
95+
}
1396
}

src/BootstrapBlazor/Components/BootstrapBlazorRootOutlet/BootstrapBlazorRootOutlet.cs

Lines changed: 132 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,142 @@
33
// See the LICENSE file in the project root for more information.
44
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone
55

6+
using Microsoft.AspNetCore.Components.Rendering;
7+
68
namespace BootstrapBlazor.Components;
79

810
/// <summary>
9-
/// BootstrapBlazorRootOutlet 组件
11+
/// BootstrapBlazorRootOutlet Component
1012
/// </summary>
11-
public partial class BootstrapBlazorRootOutlet : ComponentBase
13+
public class BootstrapBlazorRootOutlet : IComponent, IDisposable
1214
{
15+
private static readonly RenderFragment _emptyRenderFragment = _ => { };
16+
private object? _subscribedIdentifier;
17+
private RenderHandle _renderHandle;
18+
19+
/// <summary>
20+
/// Gets the default identifier that can be used to subscribe to all <see cref="BootstrapBlazorRootContent"/> instances.
21+
/// </summary>
22+
public static readonly object DefaultIdentifier = new();
23+
24+
[Inject]
25+
private BootstrapBlazorRootRegisterService RootRegisterService { get; set; } = default!;
26+
27+
/// <summary>
28+
/// Gets or sets the <see cref="string"/> ID that determines which <see cref="BootstrapBlazorRootContent"/> instances will provide
29+
/// content to this instance.
30+
/// </summary>
31+
[Parameter]
32+
public string? RootName { get; set; }
33+
34+
/// <summary>
35+
/// Gets or sets the <see cref="object"/> ID that determines which <see cref="BootstrapBlazorRootContent"/> instances will provide
36+
/// content to this instance.
37+
/// </summary>
38+
[Parameter]
39+
public object? RootId { get; set; }
40+
41+
void IComponent.Attach(RenderHandle renderHandle)
42+
{
43+
_renderHandle = renderHandle;
44+
}
45+
46+
/// <summary>
47+
/// <inheritdoc/>
48+
/// </summary>
49+
/// <param name="parameters"></param>
50+
/// <returns></returns>
51+
Task IComponent.SetParametersAsync(ParameterView parameters)
52+
{
53+
parameters.SetParameterProperties(this);
54+
55+
object? identifier = null;
56+
57+
if (RootName is not null && RootId is not null)
58+
{
59+
throw new InvalidOperationException($"{nameof(BootstrapBlazorRootOutlet)} requires that '{nameof(RootName)}' and '{nameof(RootId)}' cannot both have non-null values.");
60+
}
61+
else if (RootName is not null)
62+
{
63+
identifier = RootName;
64+
}
65+
else if (RootId is not null)
66+
{
67+
identifier = RootId;
68+
}
69+
identifier ??= DefaultIdentifier;
70+
71+
if (!Equals(identifier, _subscribedIdentifier))
72+
{
73+
if (_subscribedIdentifier is not null)
74+
{
75+
RootRegisterService.Unsubscribe(_subscribedIdentifier);
76+
}
77+
78+
RootRegisterService.Subscribe(identifier, this);
79+
_subscribedIdentifier = identifier;
80+
}
81+
82+
RenderContent();
83+
return Task.CompletedTask;
84+
}
85+
86+
internal void ContentUpdated(BootstrapBlazorRootContent? provider)
87+
{
88+
RenderContent();
89+
}
90+
91+
private void RenderContent()
92+
{
93+
_renderHandle.Render(BuildRenderTree);
94+
}
95+
96+
/// <summary>
97+
/// <inheritdoc/>
98+
/// </summary>
99+
/// <param name="builder"></param>
100+
private void BuildRenderTree(RenderTreeBuilder builder)
101+
{
102+
if (_subscribedIdentifier is not null)
103+
{
104+
foreach (var content in RootRegisterService.GetProviders(_subscribedIdentifier))
105+
{
106+
builder.OpenComponent<BootstrapBlazorRootOutletContentRenderer>(0);
107+
builder.SetKey(content);
108+
builder.AddAttribute(1, BootstrapBlazorRootOutletContentRenderer.ContentParameterName, content.ChildContent ?? _emptyRenderFragment);
109+
builder.CloseComponent();
110+
}
111+
}
112+
}
113+
114+
/// <summary>
115+
/// <inheritdoc/>
116+
/// </summary>
117+
public void Dispose()
118+
{
119+
if (_subscribedIdentifier is not null)
120+
{
121+
RootRegisterService.Unsubscribe(_subscribedIdentifier);
122+
}
123+
GC.SuppressFinalize(this);
124+
}
125+
126+
internal sealed class BootstrapBlazorRootOutletContentRenderer : IComponent
127+
{
128+
public const string ContentParameterName = "content";
129+
130+
private RenderHandle _renderHandle;
131+
132+
public void Attach(RenderHandle renderHandle)
133+
{
134+
_renderHandle = renderHandle;
135+
}
13136

137+
public Task SetParametersAsync(ParameterView parameters)
138+
{
139+
var fragment = parameters.GetValueOrDefault<RenderFragment>(ContentParameterName)!;
140+
_renderHandle.Render(fragment);
141+
return Task.CompletedTask;
142+
}
143+
}
14144
}

0 commit comments

Comments
 (0)