Skip to content
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

[Blazor] Add an API to describe the render mode (if any) a component is running in #55577

Merged
merged 12 commits into from
May 14, 2024

Conversation

javiercn
Copy link
Member

@javiercn javiercn commented May 7, 2024

Adds an API to describe the current host render mode if any a component is running in.

Fixes #49401

Sample usage:

<p id="host-render-mode">@_message</p>

@code {

    private string _message = "";

    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";
        }
    }
}

Adds a new ComponentPlatform type that the renderer provides (gets exposed through RenderHandle) to provide details on the platform the component is currently running on.

All 4 renderers provide an implementation, and they indicate the name, whether the platform is interactive, and any render mode associated with the platform (if any).

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-blazor Includes: Blazor, Razor Components label May 7, 2024
@javiercn javiercn force-pushed the javiercn/current-render-mode-api branch from 91621a7 to ac1ad1b Compare May 7, 2024 12:18
@javiercn javiercn marked this pull request as ready for review May 7, 2024 14:13
@javiercn javiercn requested a review from a team as a code owner May 7, 2024 14:13
@SteveSandersonMS
Copy link
Member

The approach here is great for finding out the current rendering platform, but doesn't address finding out the effective rendermode at least in the sense that I was proposing.

For example, even when a component is running on the Static platform, it might also have rendermode InteractiveServer. Developers may need to vary behavior based on whether the component is going to become interactive, since (for example) an RCL component might produce a different UI shape entirely based on whether interactive controls are going to be available later.

Suggestion: let's keep this PR simple as it is, and just eliminate the ComponentPlatform.RenderMode property since it would be misleading (e.g., it would report rendermode null for all components during SSR even when you've set a nonnull rendermode on them). Then later as a separate work item if we have time, we can add some further ComponentBase.RenderMode property that gets the effective per-component rendermode.

What do you think?

@javiercn
Copy link
Member Author

javiercn commented May 9, 2024

Suggestion: let's keep this PR simple as it is, and just eliminate the ComponentPlatform.RenderMode property since it would be misleading (e.g., it would report rendermode null for all components during SSR even when you've set a nonnull rendermode on them). Then later as a separate work item if we have time, we can add some further ComponentBase.RenderMode property that gets the effective per-component rendermode.

What do you think?

Sounds good, let me think for 5 minutes if what you suggest is straightforward to do, and maybe I just go ahead and do it. If I see any trouble with it, I'll just remove it for now.

@SteveSandersonMS
Copy link
Member

SteveSandersonMS commented May 9, 2024

Sounds good, let me think for 5 minutes if what you suggest is straightforward to do, and maybe I just go ahead and do it. If I see any trouble with it, I'll just remove it for now.

Renderer already has a virtual method GetComponentRenderMode.

On EndpointHtmlRenderer this is already overridden to return the info by doing a linear walk up the list component hierarchy to look for the closest boundary. As long as we're OK with this doing a linear walk up the hierarchy on each property evaluation (or I guess we could cache after the first read), we can just use that and it's pretty simple. Would just need to override the method on all the other renderers besides EndpointHtmlRenderer and make them return whatever rendermode is fixed on that renderer.

However there is quite a bit more to test, as there are more combinations (e.g., need to cover all the renderers, and each case where it's possible to set rendermodes). So I'll leave it with you whether you want to incorporate that into this PR or not!

@javiercn
Copy link
Member Author

javiercn commented May 9, 2024

However there is quite a bit more to test, as there are more combinations (e.g., need to cover all the renderers, and each case where it's possible to set rendermodes). So I'll leave it with you whether you want to incorporate that into this PR or not!

Not sure what you mean by this, I think the main change would be that EndpointHtmlRenderer walks the tree to find the rendermode, but all other renderers just provide static results as they do already (server does server, wasm does wasm, webview does null).

The main difference is that endpoints returned null, and now we are saying it'll return Auto, WebAseembly or Server based on the render mode for the root component (figuring it out with the help of the renderer), am I getting this wrong?

So, it's only about checking those non-interactive returns the render mode instead of null, isn't it?

@javiercn javiercn force-pushed the javiercn/current-render-mode-api branch from ac1ad1b to c2b67f9 Compare May 9, 2024 15:17
@SteveSandersonMS
Copy link
Member

By "a bit more to test" I just mean writing E2E tests in the PR, not things getting tested at runtime :)

The main difference is that endpoints returned null, and now we are saying it'll return Auto, WebAseembly or Server based on the render mode for the root component (figuring it out with the help of the renderer), am I getting this wrong?

Yes exactly.

@javiercn
Copy link
Member Author

javiercn commented May 9, 2024

Yes exactly.

Cool, check if c2b67f9 matches your expectations and I'll add more tests.

@SteveSandersonMS
Copy link
Member

Cool, check if c2b67f9 matches your expectations and I'll add more tests.

Yay! Other than minor details yes that looks great.

/// <summary>
/// Gets the <see cref="IComponentRenderMode"/> the component is rendering or will render in when it becomes interactive.
/// </summary>
protected IComponentRenderMode? InteractiveRenderMode => _interactiveRenderMode ??= _renderHandle.GetInteractiveRenderMode();
Copy link
Member

Choose a reason for hiding this comment

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

Could we just call it RenderMode? The return value is valid even if the component isn't interactive, plus we could add other noninteractive rendermodes in the future.

Also it would be good to change the caching rule so it caches even if the value is null.

Copy link
Member Author

Choose a reason for hiding this comment

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

The reason I wanted to add interactive in there is because I'm worried of people writing

if(RenderMode is InteractiveServer) 
{
  `JSRuntime.InvokeAsync(...)`
}

So I wanted to give it a name that gave a hint as to what this meant/covered and didn't want to call it NonPrerendered render mode.

Copy link
Member

@SteveSandersonMS SteveSandersonMS May 9, 2024

Choose a reason for hiding this comment

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

If people try to use JS interop when it's not available, it will just fail telling them it's not available, so TBH I don't think there's a problem with this. The correct code would be "if (Platform is server) { ... }" which is similarly easy to write so I think people will be OK.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fair enough, let's go with RenderMode then.

Copy link
Member Author

Choose a reason for hiding this comment

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

I had to do GetRenderMode() instead because RenderMode is a type and that will cause all sort of symbol resolution issues

@javiercn javiercn force-pushed the javiercn/current-render-mode-api branch from c2b67f9 to 1017c33 Compare May 10, 2024 21:20
@javiercn
Copy link
Member Author

🆙 📅

@javiercn
Copy link
Member Author

Applied API review feedback

? (GridRendering.RenderMode)Enum.Parse(typeof(GridRendering.RenderMode), _gridTypeOption.Value(), true)
: GridRendering.RenderMode.FastGrid;
? (GridRendering.GridRenderMode)Enum.Parse(typeof(GridRendering.GridRenderMode), _gridTypeOption.Value(), true)
: GridRendering.GridRenderMode.FastGrid;
Copy link
Member

@SteveSandersonMS SteveSandersonMS May 14, 2024

Choose a reason for hiding this comment

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

I suppose this shows it's a breaking change in a super niche sense, but we have to accept that otherwise we couldn't extend the base class at all. If there's a lot of pushback in previews we can consider options.

Copy link
Member

Choose a reason for hiding this comment

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

Since we decided to go with AssignedRenderMode instead of just RenderMode for the property, hopefully it will be less breaking.

@SteveSandersonMS SteveSandersonMS self-requested a review May 14, 2024 09:43
Copy link
Member

@SteveSandersonMS SteveSandersonMS left a comment

Choose a reason for hiding this comment

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

Looks great!

@SteveSandersonMS SteveSandersonMS enabled auto-merge (squash) May 14, 2024 09:43
@@ -48,16 +48,14 @@ public ComponentBase()
protected ComponentPlatform Platform => _renderHandle.Platform;

/// <summary>
/// Gets the target <see cref="IComponentRenderMode"/> this component will run in after prerendering.
/// Gets the <see cref="IComponentRenderMode"/> assigned to this component.
Copy link
Member

Choose a reason for hiding this comment

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

This change is to avoid implying we are currently prerendering, or that prerendering is even applicable (since on WebView or standalone WebAssembly it wouldn't be applicable as a concept).

@SteveSandersonMS SteveSandersonMS merged commit 1b454f5 into main May 14, 2024
26 checks passed
@SteveSandersonMS SteveSandersonMS deleted the javiercn/current-render-mode-api branch May 14, 2024 16:19
@dotnet-policy-service dotnet-policy-service bot added this to the 9.0-preview5 milestone May 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-blazor Includes: Blazor, Razor Components
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add an API to tell you the current render mode
4 participants