-
Notifications
You must be signed in to change notification settings - Fork 10.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add an API to tell you the current render mode #49401
Comments
We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process. |
There should be APIs to determine at least what context a component is rendering in. I have created my own https://github.com/marinasundstrom/Blazor8Test/blob/main/src/Shared/RenderingContext.cs Besides this, I'm also looking for a way to determine whether or not an interactive component has been mounted and activated in the browser so that I know when to execute the Javascript. |
Sounds good! Glad to hear this is a generally useful concept.
Hopefully |
Another approach could be something like <RenderModeSelector>
<PreRender>
<MyPlaceholderComponentA>
</PreRender>
<WebAssembly>
<MyComponentA>
</WebAssembly>
</RenderModeSelector> where PreRender == SSR. |
@ladeak In my own experience, I don't see a use for this type of component. To me, it just makes the markup harder to read when in for instance a page component. It is just a glorified switch statement. This kind of component pattern is OK for the router, and perhaps I'd rather see that people learn about how pre-rendering works, than for Blazor to to hide the details for them in some React-like pattern from the start. I just think we should wait and see whether this is a good idea or not. |
Sure, although I agree @marinasundstrom I wanted to offer an alternative, where the custom components can remain agnostic to render mode. I think the original proposal is very useful: with ASP.NET Core 8 when I have a component tree in SSR or server rendered and one component with rendermode: webassembly, I get a refresh in the browser that moves components around as soon as that component gets rendered eventually on the browser. It looks very undesired. |
This seems to work. public enum RenderingEnvironment
{
Unknown,
Server,
WebAssembly,
SSRServer,
SSRWebAssembly,
WebView
} RenderingEnvironment renderingEnvironment;
var servicesProvider = builder.Services.BuildServiceProvider();
var isSSR = servicesProvider.GetService<IComponentPrerenderer>() is not null;
var jsRuntime = servicesProvider.GetRequiredService<IJSRuntime>();
if (isSSR)
{
renderingEnvironment = jsRuntime is IJSInProcessRuntime
? RenderingEnvironment.SSRWebAssembly
: RenderingEnvironment.SSRServer;
}
else if (jsRuntime is JSInProcessRuntime)
{
renderingEnvironment = RenderingEnvironment.WebAssembly;
}
else if (jsRuntime.GetType().Name == "RemoteJSRuntime")
{
renderingEnvironment = RenderingEnvironment.Server;
}
else if (jsRuntime.GetType().Name == "WebViewJSRuntime")
{
renderingEnvironment = RenderingEnvironment.WebView;
}
else
{
renderingEnvironment = RenderingEnvironment.Unknown;
} |
The above suggested solution does not seem to work when I navigate with links on the page. |
Until this ability is available, prerendering offers a poor user experience. Even in the Counter.razor component that ships with the .NET 8 Blazor Web App template, the "Click me" button does nothing when initially prerendered, for a second or two until Web Assembly interactivity kicks in. Is there no recommended way to mitigate it for the time being, other than disabling prerendering on the component? By using reflection on the IJSRuntime, it seems to be possible to detect prerendering.
EDIT - please check out this post on issue #51468 for a cleaner RenderContext implementation by @bcheung4589. Having separate RenderContext implementations for client and server, and detecting prerendering using HttpContext.Response avoids any need for reflection. |
Just wanna say my RenderContext was heavily inspired by marinasundstrom. Some months ago I went through multiple public repo's to look for inspiration on how people work and to learn about Blazor 8. So credit where credit is due, @marinasundstrom <3 |
Thanks for contacting us. We're moving this issue to the |
Do we really have to wait a whole year just to determine the RenderMode that we are using in our code? |
Sample protected override void OnInitialized()
{
if (GetRenderMode() is InteractiveServerRenderMode)
{
_message = $"Host Render Mode: Interactive Server";
}
else if (GetRenderMode() is InteractiveWebAssemblyRenderMode)
{
_message = $"Host Render Mode: Interactive Webassembly";
}
else
{
_message = "Host Render Mode: Static";
}
}
} APIsnamespace Microsoft.AspNetCore.Components;
public class ComponentBase
{
+ public IComponentRenderMode GetRenderMode();
+ public ComponentPlatform Platform { get; }
}
public struct RenderHandle
{
+ public IComponentRenderMode GetRenderMode();
+ public ComponentPlatform Platform { get; }
}
+public class ComponentPlatform(string platformName, bool isInteractive, IComponentRenderMode? renderMode)
+{
+ public string PlatformName { get; }
+ public bool IsInteractive { get; }
+} |
Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:
|
[API Review]
|
namespace Microsoft.AspNetCore.Components;
public class ComponentBase
{
+ public IComponentRenderMode RenderMode { get; } // Or AssignedRenderMode
+ public ComponentPlatform Platform { get; }
}
public struct RenderHandle
{
+ public IComponentRenderMode RenderMode { readonly get; } // Or AssignedRenderMode
+ public ComponentPlatform Platform { readonly get; }
}
+public sealed class ComponentPlatform(string name, bool isInteractive)
+{
+ public string Name { get; }
+ public bool IsInteractive { get; }
+} (Feel free to drop the API approved! |
There are multiple rendering platforms/environments:
We've resisted having an API that literally just tells you which one is currently in effect, reasoning that components should be agnostic to this and only vary their behavior indirectly, e.g.:
serviceProvider.GetService<IHttpContextAccessor>()
OnAfterRenderAsync
, and then it won't execute if not.What's changing?
With .NET 8, people will be writing a lot more code that's specialized to SSR. The actual UX of a component may frequently vary based on this. For example, you might choose to render some buttons in a greyed-out way for SSR/prerendering, and then light up if the component becomes interactive. Or you might render completely different UI given the restrictions of noninteractivity (reducing something to a basic HTML form that can be posted). There isn't currently a sensible way to vary UX based on whether interactivity is available; you just have to somehow know.
In the framework we also have reasons to distinguish. For example,
InputBase
wants to emit field names in SSR cases and not otherwise. Currently we distinguish based on the presence of aFormMappingContext
, but that's a weak approximation and sometimes incorrect, since aFormMappingContext
could also be present in interactive components if the developer has put one there to make a form also work in SSR mode.Proposal
I'd recommend something modelled on how .NET reports the current OS platform as a string (making it extensible) and has helpful extension methods to identify specific ones in a strongly-typed way. The main difference though is we can't have a
static
API for this since it may vary within a single process. So:class M.A.Components.ComponentPlatform(string PlatformName, bool Interactive)
Renderer
would haveComponentPlatform Platform { get; }
configured during the hosting environment startupRenderHandle
would haveComponentPlatform Platform => _renderer.Platform
ComponentBase
would haveComponentPlatform Platform => _renderHandle.Platform
Platform.IsStaticServer()
,Platform.IsInteractiveServer()
,Platform.IsWebAssembly()
,Platform.IsWebView()
,Platform.IsInteractive()
, ....M.A.C.WebView
, likewise for any custom hosting platforms).I specifically do not propose we solve this with some kind of cascading parameter. We have to be careful about overusing them because they impose a cost of
O(component depth)
to every[CascadingParameter]
declaration. In particular, adding these toInputBase
is not good given how apps may have UI with many hundreds of input fields at once (e.g., in an editable grid). We should try to reserve use of[CascadingParameter]
for application code and only use it in the framework in situations where we think only a small number of instances of the consumer will make sense to be used.Likewise, I don't propose we solve this with a DI service. That again comes at a per-consumption cost. It's likely somewhat cheaper than
CascadingParameter
(should be pretty much a single dictionary lookup) but still is a lot more machinery than we need for this.Update: Also offer a way to find the rendermode
Besides knowing the current environment, it may be desirable to know the rendermode. That way even if you're currently prerendering (e.g.,
Platform.IsStaticServer()
), you can find out whether the component is scheduled to become interactive later, because it has a nonnull rendermode or is inheriting one from further up the hierarchy. You might want to change the actual shape of the render output based on whether you expect interactive buttons to light up in a moment.Alternatives
Haven't really thought through the pros/cons, but instead of the platform being defined through a string name with extension methods to identify it, it could potentially be a type hierarchy. That would allow subclasses to provide extra custom information, while still adding extension methods on the root type that would try downcasting to the subclass they know about.
Not sure whether this is desirable or would even practically be useful. We don't consider "custom hosting models" to be a common use case, and the hosting model would be the thing constructing the instance, so it wouldn't really be very extensible anyway.
I was just trying to imagine scenarios like if M.A.C.WebView wants to be able to have
platform.HasWebViewFlag(FlagName ...)
which it could do by subclassingComponentPlatform
and then defining its own extension methods that read info from the subclass. Another possible approach would be putting a loosely-typed property bag onComponentPlatform
.The text was updated successfully, but these errors were encountered: