首次渲染时Blazor组件参考为空 [英] Blazor Component Reference Null on First Render

查看:81
本文介绍了首次渲染时Blazor组件参考为空的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个自定义组件,带有一个名为TabChanged的事件Action.在我的Razor页面中,我将对它的引用设置如下:

 < TabSet @ ref ="tabSet">...</TabSet>@代码 {私有TabSet tabSet;...} 

OnAfterRenderAsync 方法中,我为事件分配处理程序:

 受保护的重写异步任务OnAfterRenderAsync(bool firstRender){如果(firstRender){tabSet.TabChanged + = TabChanged;}} 

页面第一次呈现时,出现 System.NullReferenceException:对象引用未设置为对象实例的错误.

如果我切换为使用后续渲染,则效果很好:

 受保护的重写异步任务OnAfterRenderAsync(bool firstRender){如果(!firstRender){tabSet.TabChanged + = TabChanged;}} 

但是,这当然是草率的,我将在渲染期间触发多个事件处理程序,以触发它们.

如何在第一次渲染时一次分配参考?我正在按照

在其他渲染器上不为空:

解决方案

Dani Herrera 所述评论这可能是由于组件带有if/else语句而确实如此.以前,如果对象为null,我会将组件隐藏起来:

  @if(帐户!=空){< TabSet @ ref =" tabSet">...</TabSet>} 

为简洁起见,我忽略了这一点,并错误地认为该问题不是有条件的.我非常错误,因为在第一次渲染时该对象为null,因此该组件不存在!所以要小心.我通过将条件转移到组件中的各个部分来解决了该问题:

 < TabSet @ ref =" tabSet">@if(帐户!=空){< Tab>...</Tab>< Tab>...</Tab>}</TabSet> 

I have a custom component with an event Action called TabChanged. In my Razor page I set the reference to it up like so:

<TabSet @ref="tabSet">
 ...
</TabSet>

@code {
    private TabSet tabSet;   
    ...
}

In the OnAfterRenderAsync method I assign a handler to the event:

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if(firstRender)
    {
        tabSet.TabChanged += TabChanged;
    }       
}

The first time the page renders I get a System.NullReferenceException: Object reference not set to an instance of an object error.

If I switch to use subsequent renders it works fine:

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if(!firstRender)
    {
        tabSet.TabChanged += TabChanged;
    }       
}

But of course this is sloppy and I will be firing multiple event handlers as they stack up during renders.

How can I assign the reference one time and on first render? I am following the docs as outlined here

EDIT

Here is the TabSet.razor file:

@using Components.Tabs

<!-- Display the tab headers -->
<CascadingValue Value="this">
    <ul class="nav nav-tabs">
        @ChildContent
    </ul>
</CascadingValue>

<!-- Display body for only the active tab -->
<div class="nav-tabs-body" style="padding:15px; padding-top:30px;">
    @ActiveTab?.ChildContent
</div>

@code {

    [Parameter]
    public RenderFragment ChildContent { get; set; }

    public ITab ActiveTab { get; private set; }

    public event Action TabChanged;


    public void AddTab(ITab tab)
    {
        if (ActiveTab == null)
        {
            SetActiveTab(tab);
        }
    }

    public void RemoveTab(ITab tab)
    {
        if (ActiveTab == tab)
        {
            SetActiveTab(null);
        }
    }

    public void SetActiveTab(ITab tab)
    {
        if (ActiveTab != tab)
        {
            ActiveTab = tab;
            NotifyStateChanged();
            StateHasChanged();
        }
    }

    private void NotifyStateChanged() => TabChanged?.Invoke();

}

TabSet also uses Tab.razor:

@using Components.Tabs
@implements ITab

<li>
    <a @onclick="Activate" class="nav-link @TitleCssClass" role="button">
        @Title
    </a>
</li>

@code {
    [CascadingParameter]
    public TabSet ContainerTabSet { get; set; }

    [Parameter]
    public string Title { get; set; }

    [Parameter]
    public RenderFragment ChildContent { get; set; }


    private string TitleCssClass => ContainerTabSet.ActiveTab == this ? "active" : null;

    protected override void OnInitialized()
    {
        ContainerTabSet.AddTab(this);
    }

    private void Activate()
    {
        ContainerTabSet.SetActiveTab(this);
    }
}

And ITab.cs Interface

using Microsoft.AspNetCore.Components;

namespace PlatformAdmin.Components.Tabs
{
    public interface ITab
    {
        RenderFragment ChildContent { get;  }

        public string Title { get; }
    }
}

It's taken from a Steve Sanderson example found here

EDIT 2

Here is the debugger showing tabSet is null on first render:

And not null on additional renders:

解决方案

As Dani Herrera pointed out in the comments this may be due to the component being withing an if/else statement and indeed it was. Previously I had the component hidden if an object was null:

@if(Account != null)
{
    <TabSet @ref="tabSet">
     ...
    </TabSet>
}

I left this out for brevity and made the incorrect assumption that the issue was not the conditional. I was very wrong as on first render the object is null and therefore the component does not exist! So be careful out there. I resolved it by moving my conditionals to the sections within the component:

<TabSet @ref="tabSet">
    @if(Account != null)
    {
        <Tab>
         ...
        </Tab>
        <Tab>
         ...
        </Tab>
    }
</TabSet>

这篇关于首次渲染时Blazor组件参考为空的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆