Skip to content

Prerender select elements with value; move HtmlRenderer into Mvc.ViewFeatures #12996

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

Merged
merged 7 commits into from
Aug 13, 2019

Conversation

SteveSandersonMS
Copy link
Member

This deals with #12667 in the case where the <option> element isn't embedded into a markup frame.

The remainder of the fix for #12667 would be to change the Razor compiler's markup pass to avoid recursing into <select> elements, as per @rynowak's description. @ajaybhargavb, would it be possible for you to handle the remaining piece inside the Razor compiler? This is because I'm trying to maximise the amount of API-review changes I can get done in my remaining few days on preview 9 - not sure what your backlog currently looks like, @ajaybhargavb.

Further API review thought: if there's any reasonable way we could move HtmlRenderer out of .Components and into .Web, that would be much better. This whole concept is completely web-specific. I understand the limit there is that the MVC prerendering feature depends on this. @javiercn / @rynowak, do you know if anything stops us from making MVC prerendering depend on M.A.Components.Web?

@SteveSandersonMS SteveSandersonMS added area-blazor Includes: Blazor, Razor Components tell-mode Indicates a PR which is being merged during tell-mode labels Aug 9, 2019
@SteveSandersonMS SteveSandersonMS added this to the 3.0.0-preview9 milestone Aug 9, 2019
case RenderTreeFrameType.Attribute:
return RenderAttributes(result, frames, position, 1);
throw new InvalidOperationException($"Attributes should only be encountered within {nameof(RenderElement)}");
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 think this is true, and all the tests do still pass. @javiercn, do you have any concerns about this?

Copy link
Member

Choose a reason for hiding this comment

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

Do we have a test with more than 1 attribute? I thought we iterated over the attributes that way, but I might be wrong.

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 new tests for prerendering <select> put multiple attributes on a single element and work OK.

@javiercn
Copy link
Member

javiercn commented Aug 9, 2019

Components.Web depends on JSInterop, doesn't it? Not sure about how others feels about it. On one side, it's all the shared framework. On the other, we would have to explain customers why MVC has a reference to something they can't use.

@SteveSandersonMS
Copy link
Member Author

Components.Web depends on JSInterop, doesn't it?

M.A.Components itself already has a reference to JSInterop (though we do plan to move that out to .Web later). People haven't found it troublesome yet.

On the other, we would have to explain customers why MVC has a reference to something they can't use.

Whether they can use it depends on what parts of ASP.NET Core they have configured for use in their application. We have a lot of things like that, so it doesn't seem too problematic to me, but maybe we can cover this in our discussions about JSInterop.

@SteveSandersonMS
Copy link
Member Author

Update We agreed offline that we will move HtmlRenderer into .Web. So I'll add that behavior either here or in a different PR.

@ajaybhargavb
Copy link
Contributor

PR with the compiler changes dotnet/razor#952

@SteveSandersonMS SteveSandersonMS force-pushed the stevesa/fix-prerendering-select branch from 4e1477f to 3df6664 Compare August 12, 2019 10:18
@SteveSandersonMS
Copy link
Member Author

OK @javiercn @rynowak:

I've updated this quite a bit and expanded the scope of the PR to untangling HtmlRenderer from the rest of the system:

  • RemoteRenderer no longer inherits from HtmlRenderer. The fact that it did before was a strange implementation quirk of stateful prerendering which is no longer required.
  • HtmlRenderer itself moves all the way into Mvc.ViewFeatures and becomes purely internal, so this and ComponentRenderedText disappear from the public API surface entirely

The one new bit of API we have to expose to enable this is changing Renderer's GetCurrentRenderTreeFrames from private protected to protected. I think this is the right accessibility modifier, as it really is necessary for subclasses to access this info for some reasonable scenarios (HTML rendering, Blutter). The fact that it only shows up for people subclassing Renderer means this doesn't change anything for building normal apps.

@SteveSandersonMS SteveSandersonMS changed the title Prerender select elements with value Prerender select elements with value; move HtmlRenderer into Mvc.ViewFeatures Aug 12, 2019
@javiercn
Copy link
Member

  • HtmlRenderer itself moves all the way into Mvc.ViewFeatures and becomes purely internal, so this and ComponentRenderedText disappear from the public API surface entirely

This sounds great. I didn't like the shape of ComponentRenderedText and was planning to change it if we had time. It's better now as we can improve perf without causing breaking changes.

@@ -247,6 +259,13 @@ private async Task<(int, ArrayRange<RenderTreeFrame>)> CreateInitialRenderAsync(

return (componentId, GetCurrentRenderTreeFrames(componentId));
}

private class HtmlRenderingContext
Copy link
Member

Choose a reason for hiding this comment

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

struct?

Copy link
Member Author

Choose a reason for hiding this comment

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

It's only one per render, and if we make it a mutable struct we have to be careful about passing by ref everywhere. I'd be happier keeping it as class for simplicity.

{
// There's no concept of nested <select> elements, so as soon as we're exiting one of them,
// we can safely say there is no longer any value for this
context.ClosestSelectValueAsString = null;
Copy link
Member

Choose a reason for hiding this comment

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

Should we try and detect this case and throw in that scenario? I'm concerned between the difference between prerender and non-prerender here.

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 don't think there's any difference between prerender and non-prerender in any valid scenario. It's never valid to nest <select> elements.

Copy link
Member

@javiercn javiercn left a comment

Choose a reason for hiding this comment

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

Great stuff!

I only had a small comment about detecting nested selects, I'll leave it up to you.

@@ -93,7 +93,7 @@ protected internal int AssignRootComponentId(IComponent component)
/// </summary>
/// <param name="componentId">The id for the component.</param>
/// <returns>The <see cref="RenderTreeBuilder"/> representing the current render tree.</returns>
private protected ArrayRange<RenderTreeFrame> GetCurrentRenderTreeFrames(int componentId) => GetRequiredComponentState(componentId).CurrentRenderTree.GetFrames();
protected ArrayRange<RenderTreeFrame> GetCurrentRenderTreeFrames(int componentId) => GetRequiredComponentState(componentId).CurrentRenderTree.GetFrames();
Copy link
Member

Choose a reason for hiding this comment

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

Is the expectation that if you get the current frames you can or can't hold on to them? Does this make a copy?

Copy link
Member Author

Choose a reason for hiding this comment

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

It doesn't make a copy. The expectation is that you only access them synchronously.

Copy link
Member Author

Choose a reason for hiding this comment

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

We could rename to “ViewCurrentRenderTreeFrames” to clarify this. Longer term we could use the same technique we plan for ParameterView to throw if you access after the underlying store is mutated further.

Copy link
Member Author

Choose a reason for hiding this comment

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

ViewCurrentRenderTreeFrames

I'm not actually going to rename it to that, because it's a strange name. The existing name GetCurrentRenderTreeFrames is more conventional. We can add the "don't read after mutation" check in the future if we want.

@SteveSandersonMS SteveSandersonMS merged commit 6b2d9f2 into release/3.0 Aug 13, 2019
@ghost ghost deleted the stevesa/fix-prerendering-select branch August 13, 2019 08:34
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 tell-mode Indicates a PR which is being merged during tell-mode
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants