Blazor 页面刷新或直接链接时,最外层的CascadingValue丢失

Blazor 页面刷新或直接链接时,最外层的CascadingValue丢失,blazor,blazor-server-side,syncfusion,Blazor,Blazor Server Side,Syncfusion,我想在Blazor应用程序中共享几个组件。这些恰好是SyncFusion组件—一个是SfToast,另一个是SfDialog。我认为这样做的一个简单方法是将组件放在MainLayout.razor上,然后使用for each将引用传递给所有子页面和组件 只要通过元素或使用NavigationManager.NavigateTo进行页面导航,这种方法就可以正常工作。但是,如果页面被深度链接或刷新,最外层的页面将变为空。为了解决这个问题,我创建了一个额外的虚拟对象作为最外层,以确保我真正关心的值填充

我想在Blazor应用程序中共享几个组件。这些恰好是SyncFusion组件—一个是SfToast,另一个是SfDialog。我认为这样做的一个简单方法是将组件放在MainLayout.razor上,然后使用for each将引用传递给所有子页面和组件

只要通过元素或使用NavigationManager.NavigateTo进行页面导航,这种方法就可以正常工作。但是,如果页面被深度链接或刷新,最外层的页面将变为空。为了解决这个问题,我创建了一个额外的虚拟对象作为最外层,以确保我真正关心的值填充在刷新或直接链接上,但这感觉像是一个黑客。我想知道我这样做的方式是否存在固有的问题,导致最外层在刷新时变为null

下面是一些示例代码来说明这个问题。这是非常做作的,但这是我能想出的唯一办法来创建一个显示问题的

如果运行项目并单击“转到示例页面”按钮,您将看到CompOne和CompTwo组件引用都设置为预期值。但是,如果您随后刷新页面,您将看到最外层的CompOne引用现在为null

该项目的布局如下所示,是从默认Blazor服务器模板创建的,因此我只显示了我进行修改的区域:

+ BlazorSample | + Components (I added this folder) | - ComponentOne.razor | - ComponentTwo.razor | + Pages | - Index.razor | - SamplePage.razor | + Shared | - MainLayout.razor 组件2.剃刀

剃刀索引

剃须刀

原因 正如被引用的HTML元素在渲染后才会链接一样,被引用的组件也不会链接。这是因为组件是作为BuildRenderTree的一部分创建的,此时引用被链接起来,如下所示

<SurveyPrompt @ref=MySurveyPrompt Title="How is Blazor working for you?" />

@code

{
    SurveyPrompt MySurveyPrompt;
}
因此,第一次渲染(即直接导航)时,您在渲染过程中没有引用,也就是在渲染CascadingValue时,但是当您单击某个按钮或某事以导致NavigationManager.NavigateTo Blazor将自动重新渲染以检查更改。此时,您现在有了要渲染的引用

解决方案 首先,在布局中添加一个字段或属性bool hasprended。 然后,在UI中,确保具有引用之前的第一次渲染不会渲染除正在引用的组件之外的任何子内容。 附加的 由于CascadingValue组件的值在任何使用者使用时都不会更改,因此您还可以添加IsFixed=True,这将提高渲染性能。

正如被引用的HTML元素在渲染后才会链接一样,被引用的组件也不会链接。这是因为组件是作为BuildRenderTree的一部分创建的,此时引用被链接起来,如下所示

<SurveyPrompt @ref=MySurveyPrompt Title="How is Blazor working for you?" />

@code

{
    SurveyPrompt MySurveyPrompt;
}
因此,第一次渲染(即直接导航)时,您在渲染过程中没有引用,也就是在渲染CascadingValue时,但是当您单击某个按钮或某事以导致NavigationManager.NavigateTo Blazor将自动重新渲染以检查更改。此时,您现在有了要渲染的引用

解决方案 首先,在布局中添加一个字段或属性bool hasprended。 然后,在UI中,确保具有引用之前的第一次渲染不会渲染除正在引用的组件之外的任何子内容。 附加的
因为在任何使用者使用CascadingValue组件时,它们的值不会改变,所以您还可以添加IsFixed=True,这将提高渲染性能。

级联参数的执行是从下开始的,页面刷新时不考虑父组件实例,因此为空。检查以下链接以供参考


父实例:

级联参数的执行是从下开始的,页面刷新时不考虑父组件实例,因此为空。检查以下链接以供参考


父实例:

Refs在第一次渲染后已修复,因此它们将以null开头。@PeterMorris不确定我是否遵循。如果通过NavigationManager.NavigateTo/samplepage访问路由,则它们不为null-仅在刷新或直接链接上。即使如此,也只有最外层是空的。我想我的解决方案是把另一个包裹在外面,这是唯一的解决办法?这两个问题似乎都与syncfusion有关。我将添加它作为标记。我希望您与页面交互以执行NavigateTo。这种相互作用将导致重新播放。有一本书。在OnAfterRender中,如果为false,则将其设置为true并调用StateHasChanged。如果已渲染,则仅渲染级联值内的内容true@PeterMorris明亮的将此添加为答案,我很乐意接受。在第一次渲染后,Refs已修复,因此它们将开始为空。@PeterMorris不确定我是否遵循。如果通过NavigationManager.NavigateTo/samplepage访问路由,则它们不为null-仅在
刷新或直接链接。即使如此,也只有最外层是空的。我想我的解决方案是把另一个包裹在外面,这是唯一的解决办法?这两个问题似乎都与syncfusion有关。我将添加它作为标记。我希望您与页面交互以执行NavigateTo。这种相互作用将导致重新播放。有一本书。在OnAfterRender中,如果为false,则将其设置为true并调用StateHasChanged。如果已渲染,则仅渲染级联值内的内容true@PeterMorris明亮的加上这个作为回答,我会欣然接受。
@code {
    // this would normally contain something useful
    public string ThisIsNotUsefulEither = "This is just a sample";
}
@page "/"
@inject NavigationManager NavigationManager

<button class="btn btn-primary"
        @onclick="@(() => NavigationManager.NavigateTo("/samplepage"))">
    Go to Sample Page
</button>
@page "/samplepage"

<div class="row">
    <div class="col-12">
        CompOne is null: @(CompOne == null)
    </div>
</div>
<div class="row">
    <div class="col-12">
        CompTwo is null: @(CompTwo == null)
    </div>
</div>

@code{
    [CascadingParameter(Name = "CompOne")]
    public BlazorSample.Components.ComponentOne CompOne { get; set; }

    [CascadingParameter(Name = "CompTwo")]
    public BlazorSample.Components.ComponentTwo CompTwo { get; set; }
}
<SurveyPrompt @ref=MySurveyPrompt Title="How is Blazor working for you?" />

@code

{
    SurveyPrompt MySurveyPrompt;
}
__builder.OpenComponent<BlazorApp58.Shared.SurveyPrompt>(1);
__builder.AddAttribute(2, "Title", "How is Blazor working for you?");

// This is the line that captures the reference
__builder.AddComponentReferenceCapture(3, (__value) => {
      MySurveyPrompt = (BlazorApp58.Shared.SurveyPrompt)__value;
  });
  __builder.CloseComponent();
@if (HasRendered)
{
  <CascadingValue Value=@ComponentOne>
    <CascadingValue Value=@ComponentTwo>
      ... your markup here ...
    </CascadingValue>
  </CascadingValue>
}
<ComponentOne ...../>
<ComponentTwo ...../>
if (firstRender)
{
  HasRendered = true;
  StateHasChanged();
}