从页面或组件向布局模板中的Blazor RenderFragment填充内容

6

我希望在Blazor中的布局页面中拥有可自定义的内容。我尝试将RenderFragment参数添加到布局中,并在我的组件中添加相同名称的标记,但是它没有被渲染在我的布局中。

layout.razor

<header>
    @HeaderContent
</header>

@body

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

component.razor

<HeaderContent>
    <p>my page specific content</p>
</HeaderContnt>

但是HeaderContent在页面体中呈现。是否有人能够解释如何做到这一点?或者为什么布局中不可能实现?我找到的唯一有效的方法是此方法,但是如果我在RenderFragment内更改绑定参数,它将无法刷新内容。


你不能直接更新属性。你必须像我一样调用一个函数来设置片段,然后调用状态已更改。实际上,直接更新属性应该会给你一个警告,我的 IDE 也是如此。 - Brian Parker
嗨,感谢您的帮助。是的,上面的伪代码不起作用。我希望找到一种方法,在布局设置器中更改内容时触发头部重新渲染。目前,在布局中呈现的内容是静态的,因此如果我更改属性,它只会更新内容而不是布局。 - Paul
我在那个页面上添加了另一个解决方案。它有点更复杂,但我对结果感到满意。 - Brian Parker
4个回答

7
  • 灵活的内容
  • 在导航时清空
  • 无需任何连线即可更新

MainLayout.razor.cs

@inherits LayoutComponentBase

...
    <div class="top-row px-4">
        @headerContent
    </div>

    <div class="content px-4">
        <CascadingValue Value="this">
            @Body
        </CascadingValue>
    </div>
...
@code {
    RenderFragment headerContent => setHeader?.ChildContent;
    SetHeader setHeader;

    public void SetHeader(SetHeader setHeader)
    {
        this.setHeader = setHeader;
        Update();
    }

    public void Update() => StateHasChanged();
}

Setter

public class SetHeader : ComponentBase, IDisposable
{
    [CascadingParameter]
    public MainLayout MainLayout { get; set; }

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

    protected override void OnInitialized()
    {
        MainLayout.SetHeader(this);
        base.OnInitialized();
    }

    protected override bool ShouldRender()
    {
        var shouldRender = base.ShouldRender();
        if (shouldRender)
        {
            MainLayout.Update();
        }
        return base.ShouldRender();
    }

    public void Dispose()
    {
        MainLayout?.SetHeader(null);
    }
}

用法

<SetHeader>
    <p>Current count: @currentCount</p>
</SetHeader>

enter image description here


2
之前的答案非常接近,但对我没用。 以下是我修复它的方式:
MainLayout:
<div class="main">
    <div class="top-row px-4">
        @headerContent
        @*<a href="https://learn.microsoft.com/aspnet/" target="_blank">About</a>*@
    </div>
    <div class="content px-4">
        <CascadingValue Value="this">
            @Body
        </CascadingValue>
    </div>
</div>
@code {
    RenderFragment headerContent => setHeader?.ChildContent;
    SetHeader setHeader;

    public void SetHeader(SetHeader setHeader)
    {
        if (setHeader == null) return;

        this.setHeader = setHeader;
        this.setHeader.OnChange += StateHasChanged;
    }
}

SetHeader:

public class SetHeader : ComponentBase, IDisposable
    {
        [CascadingParameter] public MainLayout MainLayout { get; set; }
        [Parameter] public RenderFragment ChildContent { get; set; }
        
        public event Action OnChange;
        protected override void OnInitialized()
        {
            MainLayout.SetHeader(this);
            Update();
            base.OnInitialized();
        }

        public void Update()
        {
            OnChange?.Invoke();
            StateHasChanged();
        }

        
        public void Dispose()
        {
            MainLayout?.SetHeader(null);
        }

    }

在任何剃刀页面中使用:

<SetHeader @ref="@setHeader">
    <p>Current count: @currentCount</p>

    <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

</SetHeader>

进入该页面的代码:

SetHeader setHeader;

    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
        setHeader.Update();
    }

现在只剩下一个问题...当您更改页面时,您必须在该页面中放入一个空的 以清除它。

0

Brian Parker的回答对我非常有效。然而,我想要两个标题片段,所以我不能使用默认的ChildContent命名。

MainLayout:

  <div class="main">
    <div class="top-row px-4">
        @FirstHeader
      
    </div>
   <div class="top-row px-4">
        @SecondHeader
       
    </div>
    <div class="content px-4">
        <CascadingValue Value="this">
            @Body
        </CascadingValue>
    </div>
</div>
@code 
{
   RenderFragment FirstHeader => _setHeader?.FirstHeaderTemplate;
   RenderFragment SecondHeader => _setHeader?.SecondHeaderTemplate;

   SetHeader _setHeader;

   public void SetHeader(SetHeader setHeader)
   {
       _setHeader = setHeader;
       Update();
   }

   public void Update() => StateHasChanged();
}

SetHeader类:

public class SetHeader : ComponentBase, IDisposable
    {
        [CascadingParameter]
        public MainLayout MainLayout { get; set; }

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

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

        protected override void OnInitialized()
        {
            MainLayout.SetHeader(this);
            base.OnInitialized();
        }

        protected override bool ShouldRender()
        {
            var shouldRender = base.ShouldRender();
            if (shouldRender)
            {
                MainLayout.Update();
            }
            return base.ShouldRender();
        }

        public void Dispose()
        {
            MainLayout?.SetHeader(null);
        }
    }

用法:

<SetHeader>
    <FirstHeaderTemplate>
         <LoginDisplay />
    </FirstHeaderTemplate>
    <SecondHeaderTemplate>
        <p>Current count: @currentCount</p>
    </SecondHeaderTemplate>
</SetHeader>

0

.NET 7 Blazor Server 解决方案

在 .NET 7 中,关于此主题的所有帖子中的解决方案都没有对我起作用。我需要一种方法,使布局在页面修改 <TitleHeader><ActionsHeader> 时更新其标题。当头部部分的 ChildContent 中引用另一个自定义组件时,我不断遇到无限渲染循环。

经过广泛搜索,我从 GitHub 找到了这个解决方案:https://github.com/dotnet/aspnetcore/issues/28182#issuecomment-1378804578。它可以按预期工作,而不依赖于任何服务或事件处理程序。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接