Skip to content

Commit 1b454f5

Browse files
authored
[Blazor] Add an API to describe the render mode (if any) a component is running in (#55577)
1 parent 3307bf6 commit 1b454f5

File tree

16 files changed

+252
-11
lines changed

16 files changed

+252
-11
lines changed

src/Components/Components/src/ComponentBase.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ namespace Microsoft.AspNetCore.Components;
2222
public abstract class ComponentBase : IComponent, IHandleEvent, IHandleAfterRender
2323
{
2424
private readonly RenderFragment _renderFragment;
25+
private (IComponentRenderMode? mode, bool cached) _renderMode;
2526
private RenderHandle _renderHandle;
2627
private bool _initialized;
2728
private bool _hasNeverRendered = true;
@@ -41,6 +42,27 @@ public ComponentBase()
4142
};
4243
}
4344

45+
/// <summary>
46+
/// Gets the <see cref="ComponentPlatform"/> the component is running on.
47+
/// </summary>
48+
protected ComponentPlatform Platform => _renderHandle.Platform;
49+
50+
/// <summary>
51+
/// Gets the <see cref="IComponentRenderMode"/> assigned to this component.
52+
/// </summary>
53+
protected IComponentRenderMode? AssignedRenderMode
54+
{
55+
get
56+
{
57+
if (!_renderMode.cached)
58+
{
59+
_renderMode = (_renderHandle.RenderMode, true);
60+
}
61+
62+
return _renderMode.mode;
63+
}
64+
}
65+
4466
/// <summary>
4567
/// Renders the component to the supplied <see cref="RenderTreeBuilder"/>.
4668
/// </summary>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
11
#nullable enable
2+
Microsoft.AspNetCore.Components.ComponentBase.AssignedRenderMode.get -> Microsoft.AspNetCore.Components.IComponentRenderMode?
3+
Microsoft.AspNetCore.Components.ComponentBase.Platform.get -> Microsoft.AspNetCore.Components.ComponentPlatform!
4+
Microsoft.AspNetCore.Components.ComponentPlatform
5+
Microsoft.AspNetCore.Components.ComponentPlatform.ComponentPlatform(string! platformName, bool isInteractive) -> void
6+
Microsoft.AspNetCore.Components.ComponentPlatform.IsInteractive.get -> bool
7+
Microsoft.AspNetCore.Components.ComponentPlatform.Name.get -> string!
28
Microsoft.AspNetCore.Components.ExcludeFromInteractiveRoutingAttribute
39
Microsoft.AspNetCore.Components.ExcludeFromInteractiveRoutingAttribute.ExcludeFromInteractiveRoutingAttribute() -> void
10+
Microsoft.AspNetCore.Components.RenderHandle.Platform.get -> Microsoft.AspNetCore.Components.ComponentPlatform!
11+
Microsoft.AspNetCore.Components.RenderHandle.RenderMode.get -> Microsoft.AspNetCore.Components.IComponentRenderMode?
12+
virtual Microsoft.AspNetCore.Components.RenderTree.Renderer.ComponentPlatform.get -> Microsoft.AspNetCore.Components.ComponentPlatform!

src/Components/Components/src/RenderHandle.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,28 @@ public Dispatcher Dispatcher
5151
internal bool IsRendererDisposed => _renderer?.Disposed
5252
?? throw new InvalidOperationException("No renderer has been initialized.");
5353

54+
/// <summary>
55+
/// Gets the <see cref="ComponentPlatform"/> the component is running on.
56+
/// </summary>
57+
public ComponentPlatform Platform => _renderer?.ComponentPlatform ?? throw new InvalidOperationException("No renderer has been initialized.");
58+
59+
/// <summary>
60+
/// Retrieves the <see cref="IComponentRenderMode"/> assigned to the component.
61+
/// </summary>
62+
/// <returns>The <see cref="IComponentRenderMode"/> assigned to the component.</returns>
63+
public IComponentRenderMode? RenderMode
64+
{
65+
get
66+
{
67+
if (_renderer == null)
68+
{
69+
throw new InvalidOperationException("No renderer has been initialized.");
70+
}
71+
72+
return _renderer.GetComponentRenderMode(_componentId);
73+
}
74+
}
75+
5476
/// <summary>
5577
/// Notifies the renderer that the component should be rendered.
5678
/// </summary>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.AspNetCore.Components;
5+
6+
/// <summary>
7+
/// Provides information about the platform that the component is running on.
8+
/// </summary>
9+
public sealed class ComponentPlatform
10+
{
11+
/// <summary>
12+
/// Constructs a new instance of <see cref="ComponentPlatform"/>.
13+
/// </summary>
14+
/// <param name="platformName">The name of the platform.</param>
15+
/// <param name="isInteractive">A flag to indicate if the platform is interactive.</param>
16+
public ComponentPlatform(string platformName, bool isInteractive)
17+
{
18+
Name = platformName;
19+
IsInteractive = isInteractive;
20+
}
21+
22+
/// <summary>
23+
/// Gets the name of the platform.
24+
/// </summary>
25+
public string Name { get; }
26+
27+
/// <summary>
28+
/// Gets a flag to indicate if the platform is interactive.
29+
/// </summary>
30+
public bool IsInteractive { get; }
31+
}

src/Components/Components/src/RenderTree/Renderer.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,9 @@ protected ComponentState GetComponentState(int componentId)
142142
protected internal virtual IComponentRenderMode? GetComponentRenderMode(IComponent component)
143143
=> null;
144144

145+
internal IComponentRenderMode? GetComponentRenderMode(int componentId)
146+
=> GetComponentRenderMode(GetRequiredComponentState(componentId).Component);
147+
145148
/// <summary>
146149
/// Resolves the component state for a given <see cref="IComponent"/> instance.
147150
/// </summary>
@@ -150,6 +153,11 @@ protected ComponentState GetComponentState(int componentId)
150153
protected internal ComponentState GetComponentState(IComponent component)
151154
=> _componentStateByComponent.GetValueOrDefault(component);
152155

156+
/// <summary>
157+
/// Gets the <see cref="ComponentPlatform"/> associated with this <see cref="Renderer"/>.
158+
/// </summary>
159+
protected internal virtual ComponentPlatform ComponentPlatform { get; }
160+
153161
private async void RenderRootComponentsOnHotReload()
154162
{
155163
// Before re-rendering the root component, also clear any well-known caches in the framework

src/Components/Server/src/Circuits/RemoteRenderer.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ internal partial class RemoteRenderer : WebRenderer
1818
#pragma warning restore CA1852 // Seal internal types
1919
{
2020
private static readonly Task CanceledTask = Task.FromCanceled(new CancellationToken(canceled: true));
21+
private static readonly ComponentPlatform _componentPlatform = new("Server", isInteractive: true);
2122

2223
private readonly CircuitClientProxy _client;
2324
private readonly CircuitOptions _options;
@@ -56,6 +57,10 @@ public RemoteRenderer(
5657

5758
public override Dispatcher Dispatcher { get; } = Dispatcher.CreateDefault();
5859

60+
protected override ComponentPlatform ComponentPlatform => _componentPlatform;
61+
62+
protected override IComponentRenderMode? GetComponentRenderMode(IComponent component) => RenderMode.InteractiveServer;
63+
5964
public Task AddComponentAsync(Type componentType, ParameterView parameters, string domElementSelector)
6065
{
6166
var componentId = AddRootComponent(componentType, domElementSelector);

src/Components/Web/src/HtmlRendering/StaticHtmlRenderer.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ namespace Microsoft.AspNetCore.Components.HtmlRendering.Infrastructure;
1919
/// </summary>
2020
public partial class StaticHtmlRenderer : Renderer
2121
{
22+
private static readonly ComponentPlatform _componentPlatform = new ComponentPlatform("Static", isInteractive: false);
23+
2224
private static readonly Task CanceledRenderTask = Task.FromCanceled(new CancellationToken(canceled: true));
2325
private readonly NavigationManager? _navigationManager;
2426

@@ -38,6 +40,9 @@ public StaticHtmlRenderer(IServiceProvider serviceProvider, ILoggerFactory logge
3840
/// <inheritdoc/>
3941
public override Dispatcher Dispatcher { get; } = Dispatcher.CreateDefault();
4042

43+
/// <inheritdoc/>
44+
protected internal override ComponentPlatform ComponentPlatform => _componentPlatform;
45+
4146
/// <summary>
4247
/// Adds a root component of the specified type and begins rendering it.
4348
/// </summary>

src/Components/Web/src/PublicAPI.Unshipped.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ Microsoft.AspNetCore.Components.Web.Internal.IInternalWebJSInProcessRuntime
33
Microsoft.AspNetCore.Components.Web.Internal.IInternalWebJSInProcessRuntime.InvokeJS(string! identifier, string? argsJson, Microsoft.JSInterop.JSCallResultType resultType, long targetInstanceId) -> string!
44
Microsoft.AspNetCore.Components.Web.KeyboardEventArgs.IsComposing.get -> bool
55
Microsoft.AspNetCore.Components.Web.KeyboardEventArgs.IsComposing.set -> void
6+
override Microsoft.AspNetCore.Components.HtmlRendering.Infrastructure.StaticHtmlRenderer.ComponentPlatform.get -> Microsoft.AspNetCore.Components.ComponentPlatform!

src/Components/WebAssembly/WebAssembly/src/Rendering/WebAssemblyRenderer.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ internal sealed partial class WebAssemblyRenderer : WebRenderer
2424
private readonly ILogger _logger;
2525
private readonly Dispatcher _dispatcher;
2626
private readonly IInternalJSImportMethods _jsMethods;
27+
private static readonly ComponentPlatform _componentPlatform = new("WebAssembly", isInteractive: true);
2728

2829
public WebAssemblyRenderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, JSComponentInterop jsComponentInterop)
2930
: base(serviceProvider, loggerFactory, DefaultWebAssemblyJSRuntime.Instance.ReadJsonSerializerOptions(), jsComponentInterop)
@@ -72,11 +73,15 @@ private void OnUpdateRootComponents(RootComponentOperationBatch batch)
7273
NotifyEndUpdateRootComponents(batch.BatchId);
7374
}
7475

76+
protected override IComponentRenderMode? GetComponentRenderMode(IComponent component) => RenderMode.InteractiveWebAssembly;
77+
7578
public void NotifyEndUpdateRootComponents(long batchId)
7679
{
7780
_jsMethods.EndUpdateRootComponents(batchId);
7881
}
7982

83+
protected override ComponentPlatform ComponentPlatform => _componentPlatform;
84+
8085
public override Dispatcher Dispatcher => _dispatcher;
8186

8287
public Task AddComponentAsync([DynamicallyAccessedMembers(Component)] Type componentType, ParameterView parameters, string domElementSelector)

src/Components/WebView/WebView/src/Services/WebViewRenderer.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace Microsoft.AspNetCore.Components.WebView.Services;
99

1010
internal sealed class WebViewRenderer : WebRenderer
1111
{
12+
private static readonly ComponentPlatform _componentPlatform = new("WebView", isInteractive: true);
1213
private readonly Queue<UnacknowledgedRenderBatch> _unacknowledgedRenderBatches = new();
1314
private readonly Dispatcher _dispatcher;
1415
private readonly IpcSender _ipcSender;
@@ -31,6 +32,8 @@ public WebViewRenderer(
3132

3233
public override Dispatcher Dispatcher => _dispatcher;
3334

35+
protected override ComponentPlatform ComponentPlatform => _componentPlatform;
36+
3437
protected override int GetWebRendererId() => (int)WebRendererId.WebView;
3538

3639
protected override void HandleException(Exception exception)
@@ -81,7 +84,7 @@ public void NotifyRenderCompleted(long batchId)
8184
private sealed class UnacknowledgedRenderBatch
8285
{
8386
public long BatchId { get; init; }
84-
87+
8588
public TaskCompletionSource CompletionSource { get; init; }
8689
}
8790
}

0 commit comments

Comments
 (0)