George Korolev
George Korolev

Reputation: 81

Blazor MainLayout as a CascadingParameter always null

so...Im working on a Blazor Server app and having troubles with anderstanding of how a cascading parameters work and why my MainLayout returns always null.

Im doing it like this: MainLayout.razor:

<CascadingValue Value="this">
    <PageTitle>Global</PageTitle>
    <div id="wrapper">
        <SfToolbar CssClass="dockToolbar">
            <ToolbarEvents Clicked="@Toggle"></ToolbarEvents>
            <ToolbarItems>
                <ToolbarItem PrefixIcon="e-icons e-menu" TooltipText="Menu"></ToolbarItem>
                <ToolbarItem>
                    <Template>
                        <div class="e-folder">
                            <div class="e-folder-name">Global</div>
                        </div>
                    </Template>
                </ToolbarItem>
            </ToolbarItems>
        </SfToolbar>
        <div id="main-content container-fluid col-md-12" class="maincontent">
            <div>
                <div class="content">@Body</div>
            </div>
        </div>
    </div>

and Pages that behaves

SignIn.razor:

@page "/sign-in"

<ComponentLibrary.Components.AuthPage backUrl="/"></ComponentLibrary.Components.AuthPage>

@code {
    [CascadingParameter]
    public MainLayout? Layout { get; set; }

    protected override void OnInitialized()
    {
        Layout.userModel = null;
        Layout.RefreshSideBar();
        base.OnInitialized();
    }

}

But when im getting on SignIn page my Layout for some reason is null, any advices where need to look at?

i tried to just create a new example of Layout in other pages but that is tottaly not what i needed

Upvotes: 1

Views: 1244

Answers (2)

MrC aka Shaun Curtis
MrC aka Shaun Curtis

Reputation: 30001

So let's try and recreate your problem.

Start with a standard Blazor server project.

@inherits LayoutComponentBase

<PageTitle>SO74799583</PageTitle>
<CascadingValue Value=this>
    <div class="page">
        <div class="sidebar">
            <NavMenu />

        </div>

        <main>
            <div class="top-row px-4">
                <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
            </div>

            <article class="content px-4">
                @Body
            </article>
        </main>
    </div>
</CascadingValue>

@code {
    public string RenderTime = DateTime.Now.ToLongTimeString();

    protected override bool ShouldRender()
    {
        RenderTime = DateTime.Now.ToLongTimeString();
        return true;
    }
}

And Index:

@page "/"

<PageTitle>Index</PageTitle>

<h1>Hello, world!</h1>

Welcome to your new app.

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

<div class="bg-dark text-white p-2 m-2">
    Main Component last rendered at: @(this.mainLayout?.RenderTime ?? "Not Rendered")
</div>

@code {
    [CascadingParameter] private MainLayout? mainLayout { get; set; }
}

Which works: mainLayout is not null. So your not showing us everything.

Note that it's not normally a good idea to cascade or pass around a reference to a Component.

  1. You are not in control of it's lifecycle: the Renderer is. It will "Dispose" it when it no longer needs it.
  2. There's functionality that you should never attempt to use from another component.

If you want to communicate between components create a state class and either:

  1. Register it as a service and use it to raise and register for events.
  2. Cascade it.

Here's a simple example.

public class MyState
{
    public string LastUpdateTime { get; private set; } = "Not Set";

    public event EventHandler<string>? Updated;

    public void SetTime()
    {
        this.LastUpdateTime = DateTime.Now.ToLongTimeString();
        this.Updated?.Invoke(this, this.LastUpdateTime);
    }
}

Layout:

@inherits LayoutComponentBase

<PageTitle>SO74799583</PageTitle>
    <CascadingValue Value=myState>
        <div class="page">
            <div class="sidebar">
                <NavMenu />
            </div>

            <main>
                <div class="top-row px-4">
                    <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
                </div>

                <article class="content px-4">
                    <div class="alert alert-info">@RenderTime</div>
                    @Body
                </article>
            </main>
        </div>
    </CascadingValue>

@code {
    public string RenderTime = DateTime.Now.ToLongTimeString();
    private MyState myState = new();

    protected override void OnInitialized()
        => myState.Updated += OnUpdated;

    private void OnUpdated(object? sender, string value)
        => StateHasChanged();

    public void Dispose()
        => myState.Updated -= OnUpdated;
}

And Index:

@page "/"

<PageTitle>Index</PageTitle>

<h1>Hello, world!</h1>

Welcome to your new app.

<div class="p-2 m-2">
    <button class="btn btn-primary" @onclick=Update>Update Time</button>
</div>

@code {
    [CascadingParameter] private MyState? myState { get; set; }

    private Task Update()
    {
        myState?.SetTime();
        return Task.CompletedTask;
    }
}

Upvotes: 1

George Korolev
George Korolev

Reputation: 81

Yes, you guys were right. I just noticed that there was 2 MainLayout files i miss that part, sorry. removing from project extra layout solves the problem

Upvotes: 1

Related Questions