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

DisplayAsync to return actual pre-rendered IHtmlContent #15247

Merged
merged 12 commits into from
Feb 21, 2024
Merged

Conversation

ns8482e
Copy link
Contributor

@ns8482e ns8482e commented Feb 3, 2024

Component tag helper / Html Helper is not able to render Razor Components for nested PositionWrapper components in .NET8.

Suggested change here to have, DisplayAsync to return actual pre-rendered IHtmlContent instead of PositionWrapper

Fixes #15235
Fixes #15211

/cc @sebastienros

@@ -50,6 +50,16 @@ public async Task<IHtmlContent> ExecuteAsync(DisplayContext context)
// Check if the shape is pre-rendered.
if (shape is IHtmlContent htmlContent)
{
if (htmlContent is PositionWrapper wrapper)
Copy link
Member

Choose a reason for hiding this comment

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

I don't see how this would not break something in OC. Or the PositionWrapper would be useless.
Have you tried to check where this is called (outside of Blazor) to see what could go wrong?

Also, can you explain why this is fixing the exception? Technically this wrapper is invoking the same WriteTo as if you return the wrapped value.

Copy link
Member

Choose a reason for hiding this comment

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

Example of things to try with this change: Widgets, FlowPart, BagPart, GraphQL rendering, layouts, custom placement.

Copy link
Contributor Author

@ns8482e ns8482e Feb 9, 2024

Choose a reason for hiding this comment

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

It would not break anything in OC, rather fixes Blazor component rendering.

PositionWrapper is nothing but holds pre-rendered IHtmlContent, and it can only contain one IHtmlContent. Hence there no need of nesting PositionWrapper in PositionWrapper.

Currently In ThemeLayout we use PositionWrapper to pre-render (and useful while maintaining position on zone) before we place on Layout. This allows to have lazy placed zones to be rendered on Layout but requires us to call DisplayAsync twice 8880 on zones, that unnecessary nests PositionWrapper in PositionWrapper.

This PR returns the original pre-rendered IHtmlContent when the shape provided in DisplayAsync is PositionWrapper and avoids nesting of PositionWrapper when called multiple times.

This allows thread that Writes to the stream is the same that owns the Dispatcher that generated IHtmlContent for blazor component.

@sebastienros
Copy link
Member

there no need of nesting PositionWrapper in PositionWrapper.

I had not understood this at first look. Now I get it, I thought your code was removing all wrappers, not just the nested ones. And this is raising a good point actually, maybe we shouldn't wrap position wrappers, that is kind of a bug. Can you try to do that instead?

We could also add a static IHtmlContent TryWrap() in PositionWrapper to handle string, PositionWrapper and IHtmlContent (using pattern matching) so we don't duplicate the logic and it would return the correct results.

@ns8482e
Copy link
Contributor Author

ns8482e commented Feb 10, 2024

I'll update the code, to use static TryWrap instead of using constructor. However to avoid nested wrapper we still need to unwrap the wrapper to return underlying IHtmlContent on shape execute / DisplayAsync.

…er is now sealed and have private constructor.
@ns8482e
Copy link
Contributor Author

ns8482e commented Feb 10, 2024

maybe we shouldn't wrap position wrappers

FYI @sebastienros , this happens when the PositonWrapper is within the _items of ZoneShapes and DisplayAsync is called on Zone / ContentZone.


public static PositionWrapper TryWrap(object value, string position)
{
if (value is PositionWrapper wrapper)
Copy link
Member

Choose a reason for hiding this comment

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

is IPositioned?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I used PositionWrapper because return value must be both IPositioned and IShape

@@ -65,13 +65,13 @@ public virtual ValueTask<IShape> AddAsync(object item, string position)

_items ??= [];

if (item is IHtmlContent)
if (item is IHtmlContent htmlContent)
Copy link
Member

Choose a reason for hiding this comment

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

Can't this whole if/else/else be a single TryWrap?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated

else if (value is string stringContent)
return new PositionWrapper(stringContent, position);
else
throw new System.NotSupportedException($"Type of {nameof(value)} must be either {nameof(String)}, {nameof(IHtmlContent)} or {nameof(PositionWrapper)}.");
Copy link
Member

Choose a reason for hiding this comment

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

TryWrap should it return a new wrapper in this case instead of throwing?

Copy link
Member

Choose a reason for hiding this comment

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

Should it unwrap if the position is "" ? Is that the meaning of "" in ThemeLayout.cs?

Copy link
Contributor Author

@ns8482e ns8482e Feb 21, 2024

Choose a reason for hiding this comment

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

Should it unwrap if the position is "" ? Is that the meaning of "" in ThemeLayout.cs?

It's default, I believe when empty it will sort based on the order of items added

TryWrap should it return a new wrapper in this case instead of throwing?

Throwing NotSupportedException because value is not either string, IHtmlContent or PositionWrapper. Position wrapper doesn't support any other object - unless we return null instead of throwing exception - please suggest @sebastienros

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated to return null instead of throwing exception

@sebastienros sebastienros enabled auto-merge (squash) February 21, 2024 20:15
@sebastienros
Copy link
Member

I saw a test failing on the CI

@sebastienros sebastienros merged commit eb0511f into main Feb 21, 2024
5 checks passed
@sebastienros sebastienros deleted the ns/#15235 branch February 21, 2024 20:48
@ns8482e
Copy link
Contributor Author

ns8482e commented Feb 21, 2024

I saw a test failing on the CI

fixed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants