簡體   English   中英

將 Blazor RenderFragment 參數轉發給子組件拋出異常

[英]Forwarding Blazor RenderFragment parameter to child components throws exception

更新:微軟承認了這個問題。 Github 問題),並且可能會在.Net 6 中修復/解決( github 問題)。


我可以使用 RenderFragment 將(子)內容傳遞給組件。 這可以是可選的。 拿:

@*Baz.razor*@
@if (Bar is null)
{
    <div>nobar</div>
}
else
{
    <div>Render bar: @Bar</div>
}

@code {
    [Parameter]
    public RenderFragment Bar { get; set; }
}

我可以

@*FooPage.razor*@
@page "/foopage"
<Baz />

呈現“nobar”,或

@*FooPage.razor*@
@page "/foopage"
<Baz Bar=@bar/>

@code {
    private RenderFragment bar = (builder) => { builder.AddMarkupContent(0, "<div>bar</div>"); };
}

呈現“渲染欄:欄”(由於div而帶有換行符)

所以,現在我遇到了一個新情況,其中有一個介於兩者之間的組件,稱為Foo

@*Foo.razor*@
<Baz Bar=@Bar />

@code {
    [Parameter]
    public RenderFragment Bar { get; set; }
}

我將 FooPage.razor 修改為

@*FooPage.razor*@
@page "/foopage"
<Foo />

即,我將Bar參數從Foo轉發到Baz ...這不起作用並且給了我一個相當長的錯誤(在這個問題的底部)。 (注意:當我在 FooPage 中分配Bar時,它確實有效。)

這對我來說很奇怪。 我可以檢查Bar是否為null,並使用與否。 但是,我無法將Bar顯式設置為 null,或傳遞 null 參考。

我也通過這樣做直接嘗試過:

@*FooPage.razor*@
@page "/foopage"
<Foo Bar=@nullrf />

@code {
    private RenderFragment nullrf;
}

這也會引發相同的異常。

即,如果您將其忽略,則為 null,但是如果您將其分配為 null,則它會損壞。 這是一個錯誤嗎?

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)

當您嘗試將Bar作為屬性傳遞時,Razor 編譯器將嘗試將其呈現為 Foo 組件的一部分,這不是您想要的,也是當 Bar 為 null 時它失敗的原因。

您可以通過屬性飛濺來實現您的目標 - 如果實際用例適合這一點。

@*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);
    }
}

編輯:在這里測試: Blazor Repl

這不是真正的答案,但我需要在某個地方放置一些比評論更好的代碼。

問題在於在為 Razor 組件構建正確的 class 時,將 razor 代碼預編譯到BuildRenderTree方法中的方式。 責怪組件制造商。 您需要以 Razor 期望的方式聲明 RenderFragments。

您的編碼 - 使用屬性方法:

<Baz Bar="@Bar"></Baz>

Razor 構建器將其變為:

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

請注意, Bar是作為屬性添加的,如上面的答案中所述。

現在,如果您像這樣聲明它:

<Baz>
 <Bar>@Bar</Bar>
</Baz>

你會在Bar被正確添加為 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; }

幾個月前,這是我在代碼中構建組件的后端。 無論如何,我不確定你是否可以用這種方式構建你的東西,但它解釋了你所看到的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM