如何从 blazor 页面修改布局? [英] How can I modify the layout from a blazor page?

查看:69
本文介绍了如何从 blazor 页面修改布局?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在 Blazor 中实现面包屑.似乎没有任何机制可以将数据上游传递到布局级别,所以我想知道人们是如何管理它的.

这是我的布局的简化版本:

<工具栏/><面包屑></面包屑></标题><主类=mt-5">@身体</main>

这个想法是,在任何给定的 Blazor 页面上,我都希望能够设置面包屑.但是我的页面无法与布局或面包屑组件交互.

我考虑过/尝试过的事情:

  • 将我的布局包装在 CascadingParameter 中,并在面包屑组件和我的页面中实现该参数.这不起作用,因为显然无法在子组件中更改级联值.
  • 一个巧妙的范围服务,可以同时实现页面和面包屑组件.但是没有当前页面"的范围.级别,所以如果打开多个浏览器选项卡等,我会担心奇怪的工件.在我写这篇文章时,我突然想到两个选项卡可能会产生两个电路,这是值得探索的,因为这是可行的.
  • 放弃并使用 JS 互操作 解决此问题.这是核选项.这是可行的,只是跛脚.:) 必须有更好的方法,减少生命损失.

在这个古老的 GitHub 问题中,Steve Sanderson 回答了类似的需求,但他的解决方法是侵入性的.我希望现在有更好的东西:https://github.com/dotnet/aspnetcore/issues/16009

这很有趣,但最终没有帮助:Blazor:从布局访问参数

解决方案

状态容器模式

我最终使用状态容器模式解决了这个问题.

如果您不知道那是什么,这些链接很有帮助:

https://chrissainty.com/3-blazor 中的组件间通信方式/(最后一部分)https://www.youtube.com/watch?v=BB4lK2kfKf0&feature=youtu.be(使用其他选项进行更深入的探索)

概述

我做了一个 Scoped 服务只是为了处理导航.它被注入到我的 Navbar 组件中,用于管理面包屑.导航服务还有一个事件,可在面包屑导航发生变化时刷新导航栏 UI.

可以逐页配置导航选项.

为了方便我自己,我还创建了一个从 ComponentBase 继承的基本页面.

我已经稍微简化了我的代码.在我的实际项目中,我在这里管理的不仅仅是面包屑.

主布局

注意标题中的 Navbar 组件.

<导航栏/></标题><主要>@身体</main>

导航栏组件

这使用 NavState 组件来构建我们的面包屑并处理可见性.本示例使用 mdbootstrap 4.在最后的代码块中,我们注册了 OnChange 事件,并使用它来重新渲染组件.我们还实现了 Dispose 来删除该事件绑定,否则我们可能会出现内存泄漏.

@inject NavState Nav@implements IDisposable<div class="subnav clearfix @(Nav.Visible ?"": "invisible")">@*面包屑*@<div class="float-left"><ol类=面包屑">@foreach(Nav.Breadcrumbs 中的 var 项目){如果(项目.活动){<li class="breadcrumb-item active">@item.Text</li>}别的{<li class="breadcrumb-item"><a href=@item.Link">@item.Text</a></li>}}</ol>

@代码 {受保护的覆盖 void OnInitialized(){Nav.OnChange += StateHasChanged;}公共无效处置(){Nav.OnChange -= StateHasChanged;}}

NavState 服务

作为范围服务注入.在服务器端 Blazor 中,范围服务存在于 Blazor 连接的生命周期内,因此我们必须在加载新页面时小心地重置它.

另外值得注意的是:如果您打开多个选项卡,每个选项卡都有自己的连接,因此不会因为一个用户打开多个选项卡而损坏.

公共类 NavState : IScopedService{公共列表<面包屑>面包屑{得到;放;} = new List();公共布尔可见{得到;放;} = 假;公共事件 Action OnChange;public void SetVisible(bool isVisible){可见 = isVisible;NotifyStateChanged();}公共无效重置(){Breadcrumbs = new List();可见 = 假;NotifyStateChanged();}public void SetBreadcrumbs(Listbreadcrumbs){面包屑 = 面包屑;可见 = 真;NotifyStateChanged();}private void NotifyStateChanged() =>OnChange?.Invoke();}}

面包屑本身很简单:

 公共类面包屑{公共字符串文本 { 获取;放;}公共字符串链接{获取;放;}公共布尔活动{得到;放;}}

基本页面

 公共类 MyPageBase : ComponentBase{[注入] 受保护的 NavState Nav { get;放;}受保护的覆盖 void OnInitialized(){//NavState(面包屑等)是 Scoped,所以只要我们的连接存在,它就会存在.//所以当访问新页面时,我们需要清除导航以防止面包屑溢出等.//这也使导航栏默认不可见.导航重置();}}

页面

在您的页面上,如果您什么都不做,导航栏将不可见.或者您可以添加面包屑:

 protected override async Task OnInitializedAsync(){...Nav.SetBreadcrumbs(new List(){ 新面包屑(文本:测试",链接:https://google.com"});...}

在我的实际实现中,我还创建了一个流利的构建器,使处理面包屑的工作不那么笨拙,但我不想让人们不知所措.

I'm trying to implement breadcrumbs in Blazor. There doesn't seem to be any mechanism for passing data upstream to the layout level, so I'm wondering how people are managing this.

This is a simplified version of my layout:

<header>
    <Toolbar />
    <Breadcrumbs></Breadcrumbs>
</header>

<main class="mt-5">
     @Body
</main>

The idea is that on any given Blazor page, I want to be able to set breadcrumbs. But my pages cannot interact with the layout or the breadcrumb component.

Things I've considered/tried:

  • Wrapped my layout in a CascadingParameter and implemented the parameter inside the breadcrumb component and my page. This didn't work because evidently cascading values cannot be changed in a child component.
  • A cleverly scoped service that could be implemented both the page and the breadcrumb component. But there is no scoping that for the "current page" level, so I'm concerned about weird artifacts if there were multiple browser tabs open, etc. It occurs to me as I write this that two tabs might yield two circuits, which is something to explore as that would be workable.
  • Give up and solve this with JS interop. This is the nuclear option. It's doable, just lame. :) There must be a better way, where fewer lives are lost.

In this ancient GitHub issue, Steve Sanderson replies to a similar need, but his workaround is invasive. I'm hoping there's something better now: https://github.com/dotnet/aspnetcore/issues/16009

This was interesting but ultimately not helpful: Blazor: Access parameter from layout

解决方案

State Container Pattern

I ended up solving this using the State Container pattern.

If you don't know what that is, these links are helpful:

https://chrissainty.com/3-ways-to-communicate-between-components-in-blazor/ (last section) https://www.youtube.com/watch?v=BB4lK2kfKf0&feature=youtu.be (deeper dive with additional options)

Overview

I made a Scoped service just for handling nav. It's injected into my Navbar component, where it is used to manage breadcrumbs. The nav service also has an event that refreshes the navbar UI whenever the breadcrumbs change.

Nav options can be configured on a page-by-page basis.

To make things easier for myself, I also created a base page that inherits from ComponentBase.

I've simplified my code somewhat. In my actual project I am managing more than just breadcrumbs here.

MainLayout

Note the Navbar component in the header.

<header>
    <Navbar />
</header>

<main>
     @Body
</main>

Navbar Component

This uses the NavState component to build our breadcrumbs and handle visibility. This example uses mdbootstrap 4. In the code block at the end, we sign up for the OnChange event, and we use it to re-render the component. We also implement Dispose to drop that event binding, otherwise we might have memory leaks.

@inject NavState Nav
@implements IDisposable

<div class="subnav clearfix @(Nav.Visible ? "" : "invisible")">
    @*BREADCRUMBS*@
    <div class="float-left">
        <ol class="breadcrumb">
            @foreach (var item in Nav.Breadcrumbs)
            {
                if (item.Active)
                {
                    <li class="breadcrumb-item active">@item.Text</li>
                }
                else
                {
                    <li class="breadcrumb-item"><a href="@item.Link">@item.Text</a></li>
                }
            }
        </ol>
    </div>
</div>


@code {

    protected override void OnInitialized()
    {
        Nav.OnChange += StateHasChanged; 
    }

    public void Dispose()
    {
        Nav.OnChange -= StateHasChanged;
    }
}

NavState Service

Injected as a scoped service. In server-side Blazor, scoped services exist for the lifespan of the Blazor connection, so we have to be careful to reset this when a new page loads.

Also worth noting: if you open multiple tabs, each tab has its own connection, so there is no chance of corruption due to one user having multiple tabs open.

public class NavState : IScopedService
    {
        public List<Breadcrumb> Breadcrumbs { get; set; } = new List<Breadcrumb>(); 
        public bool Visible { get; set; } = false;

        public event Action OnChange;

        public void SetVisible(bool isVisible)
        {
            Visible = isVisible;
            NotifyStateChanged();
        }

        public void Reset()
        {
            Breadcrumbs = new List<Breadcrumb>();
            Visible = false;

            NotifyStateChanged();
        }

        public void SetBreadcrumbs(List<Breadcrumb> breadcrumbs)
        {
            Breadcrumbs = breadcrumbs;
            Visible = true;

            NotifyStateChanged();
        }

        private void NotifyStateChanged() => OnChange?.Invoke();
    }
}

The breadcrumb itself is simple:

    public class Breadcrumb 
    {
        public string Text { get; set; }
        public string Link { get; set; }
        public bool Active { get; set; }

    }

Base Page

    public class MyPageBase : ComponentBase
    {
        [Inject] protected NavState Nav { get; set; }

        protected override void OnInitialized()
        {
            // NavState (breadcrumbs, etc) is Scoped, so it lives as long as our connection lives. 
            // So when a new page is visited, we need to clear navigation to prevent breadcrumbs from bleed-over, etc. 
            // This also makes the navbar invisible by default.
            Nav.Reset();
        }
    }

Pages

With all that out of your way, on your pages, if you do nothing at all the navbar will be invisible. Or you can add breadcrumbs:

        protected override async Task OnInitializedAsync()
        {
            ...

            Nav.SetBreadcrumbs(new List<Breadcrumb>() 
            { new Breadcrumb(Text: "Test", Link: "https://google.com" }
            );
            
            ...
        }

In my real-world implementation, I also created a fluent builder to make working with breadcrumbs a bit less clunky, but I don't want to overwhelm people.

这篇关于如何从 blazor 页面修改布局?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
C#/.NET最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆