Update: this issue is acknowledged by Microsoft. (Github issue), and will be likely fixed/resolved in .Net 6 (github issue).
I can use a RenderFragment to pass (child) content to a component. And that can be optional. Take:
@*Baz.razor*@
@if (Bar is null)
{
<div>nobar</div>
}
else
{
<div>Render bar: @Bar</div>
}
@code {
[Parameter]
public RenderFragment Bar { get; set; }
}
I can do
@*FooPage.razor*@
@page "/foopage"
<Baz />
which renders "nobar", or
@*FooPage.razor*@
@page "/foopage"
<Baz Bar=@bar/>
@code {
private RenderFragment bar = (builder) => { builder.AddMarkupContent(0, "<div>bar</div>"); };
}
which renders "Render bar: bar" (with a newline due to the div)
So, now I have a new situation where there's a component in-between the two, called Foo.
@*Foo.razor*@
<Baz Bar=@Bar />
@code {
[Parameter]
public RenderFragment Bar { get; set; }
}
And I modify FooPage.razor to
@*FooPage.razor*@
@page "/foopage"
<Foo />
I.e. I'm forwarding the Bar parameter from Foo to Baz... this doesn't work and gives me a quite lengthy error (at the bottom of this question). (Note: it does work when I assign Bar in FooPage.)
This is very weird to me. I can check if Bar is null, and use it or not. However, I cannot explicitly set Bar to null, or pass a null reference.
I've also tried this directly by doing:
@*FooPage.razor*@
@page "/foopage"
<Foo Bar=@nullrf />
@code {
private RenderFragment nullrf;
}
which also throws the same exception.
I.e. if you leave it out, it's null, bu if you assign it null it breaks. Is this a bug?
ArgumentException: Delegate to an instance method cannot have null 'this'.
System.MulticastDelegate.ThrowNullThisInDelegateToInstance()
System.MulticastDelegate.CtorClosed(object target, IntPtr methodPtr)
BlazorServerDefault.Pages.Foo.BuildRenderTree(RenderTreeBuilder __builder)
Microsoft.AspNetCore.Components.ComponentBase.<.ctor>b__6_0(RenderTreeBuilder builder)
Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment)
Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderInExistingBatch(RenderQueueEntry renderQueueEntry)
Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue()
Microsoft.AspNetCore.Components.Rendering.HtmlRenderer.HandleException(Exception exception)
Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue()
Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessPendingRender()
Microsoft.AspNetCore.Components.RenderTree.Renderer.AddToRenderQueue(int componentId, RenderFragment renderFragment)
Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged()
Microsoft.AspNetCore.Components.ComponentBase.CallOnParametersSetAsync()
Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()
Microsoft.AspNetCore.Components.Rendering.HtmlRenderer.HandleException(Exception exception)
Microsoft.AspNetCore.Components.RenderTree.Renderer.AddToPendingTasks(Task task)
Microsoft.AspNetCore.Components.Rendering.ComponentState.SetDirectParameters(ParameterView parameters)
Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderRootComponentAsync(int componentId, ParameterView initialParameters)
Microsoft.AspNetCore.Components.Rendering.HtmlRenderer.CreateInitialRenderAsync(Type componentType, ParameterView initialParameters)
Microsoft.AspNetCore.Components.Rendering.HtmlRenderer.RenderComponentAsync(Type componentType, ParameterView initialParameters)
Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext+<>c__11<TResult>+<<InvokeAsync>b__11_0>d.MoveNext()
Microsoft.AspNetCore.Mvc.ViewFeatures.StaticComponentRenderer.PrerenderComponentAsync(ParameterView parameters, HttpContext httpContext, Type componentType)
Microsoft.AspNetCore.Mvc.ViewFeatures.ComponentRenderer.PrerenderedServerComponentAsync(HttpContext context, ServerComponentInvocationSequence invocationId, Type type, ParameterView parametersCollection)
Microsoft.AspNetCore.Mvc.ViewFeatures.ComponentRenderer.RenderComponentAsync(ViewContext viewContext, Type componentType, RenderMode renderMode, object parameters)
Microsoft.AspNetCore.Mvc.TagHelpers.ComponentTagHelper.ProcessAsync(TagHelperContext context, TagHelperOutput output)
Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner.<RunAsync>g__Awaited|0_0(Task task, TagHelperExecutionContext executionContext, int i, int count)
BlazorServerDefault.Pages.Pages__Host.<ExecuteAsync>b__14_1() in _Host.cshtml
+
<component type="typeof(App)" render-mode="ServerPrerendered" />
Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext.SetOutputContentAsync()
BlazorServerDefault.Pages.Pages__Host.ExecuteAsync() in _Host.cshtml
+
Layout = null;
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, bool invokeViewStarts)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|29_0<TFilter, TFilterAsync>(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext<TFilter, TFilterAsync>(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
This isn't really the answer but I need somewhere to put some code that's better than a comment.
The issue lies in the way the razor code is pre-compiled into the BuildRenderTree method when building the proper class for the Razor Component. Blame the component builder. You need to declare RenderFragments in the way Razor expects them.
Your coding - using the attribute approach:
<Baz Bar="@Bar"></Baz>
The Razor builder turns this into:
public partial class Foo2 : Microsoft.AspNetCore.Components.ComponentBase
{
#pragma warning disable 1998
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
{
__builder.OpenComponent<TestBlazorServer.Components.Bar>(0);
__builder.AddAttribute(1, "Bar",
#nullable restore
Bar
#line default
#line hidden
#nullable disable
);
__builder.CloseComponent();
}
#pragma warning restore 1998
#nullable restore
#line 3
[Parameter]
public RenderFragment Bar { get; set; }
Note that Bar is added as an attribute, as explained in the answer above.
Now if you declare it like this:
<Baz>
<Bar>@Bar</Bar>
</Baz>
You get this where Bar gets added correctly as a RenderFragment.
public partial class Foo : Microsoft.AspNetCore.Components.ComponentBase
{
#pragma warning disable 1998
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
{
__builder.OpenComponent<TestBlazorServer.Components.Bar>(0);
__builder.AddAttribute(1, "Baz", (Microsoft.AspNetCore.Components.RenderFragment)((__builder2) => {
__builder2.AddContent(2,
#nullable restore
#line 5
Bar
#line default
#line hidden
#nullable disable
);
}
));
__builder.CloseComponent();
}
#pragma warning restore 1998
#nullable restore
#line 9
[Parameter]
public RenderFragment Bar { get; set; }
This bit my rear end a few months ago building components in code. Anyway, I'm not sure if you can build your stuff this way, but it explains what you see.
When you are attempting to pass Bar as an attribute, the Razor compiler will attempt to render it as part of the Foo component, which is not what you want and is why it fails when Bar is null.
You could achieve your goal - if the real use case suits this - by attribute splatting.
@*Foo.razor*@
<Baz @attributes=PassThrough/>
@code {
[Parameter]
public RenderFragment Bar { get; set; }
Dictionary<string,object> PassThrough;
protected override void OnInitialized()
{
PassThrough = new Dictionary<string,object>();
PassThrough.Add(nameof(Baz.Bar),this.Bar);
}
}
Edit: Test here : Blazor Repl
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With