Description
Summary
We don't support rendering generic components as root components due to limitations in the way we serialize the type definition for the component (a similar thing happens for component parameters). We should consider lifting this limitation to support some more advanced scenarios.
Motivation and goals
Turns out that rendering generic components is useful if you want to write a "functional" component to wrap around another component, an example of this is when you are rendering multiple components at different points on a page and you want to, for example, require authentication for those components.
At that point, application developers are forced to write an individual wrapper for each of those components they want to wrap.
In scope
Support rendering closed generic components from Blazor applications.
Out of scope
TBD
Risks / unknowns
None so far
Examples
<CascadingAuthenticationState>
<AuthorizeView>
<Authorized>
<TComponent @attributes="Params" />
</Authorized>
</AuthorizeView>
</CascadingAuthenticationState>
@code{
[Parameter(CaptureUnmatchedValues = true)] public IDictionary<string,object> Params { get; set; }
}
Having a language syntax for writing these types of components is also desirable, otherwise it's cumbersome and likely error prone to author them:
public class ProtectedWidget<T> : IComponent where T : IComponent
{
private RenderHandle _handle;
private ParameterView _parameters;
static RenderFragment _authorizeFragment;
public ProtectedWidget()
{
_authorizeFragment = new RenderFragment(AuthorizedFragment);
}
public void Attach(RenderHandle renderHandle)
{
_handle = renderHandle;
}
public Task SetParametersAsync(ParameterView parameters)
{
_parameters = parameters;
_handle.Render(BuildRenderTree);
return Task.CompletedTask;
}
private void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenComponent<CascadingAuthenticationState>(0);
builder.OpenComponent<AuthorizeView>(1);
builder.AddAttribute(1, "Authorized", _authorizeFragment);
builder.CloseComponent();
builder.CloseComponent();
}
void AuthorizedFragment(RenderTreeBuilder child)
{
child.OpenComponent<T>(0);
child.AddMultipleAttributes(1, _parameters.ToDictionary());
child.CloseComponent();
}
}