jack
jack

Reputation: 109

CascadingParameter is null problem in Blazor Server

I'm using Blazor Server on .NET 9, I have this components tree:

- <ShoppingCartState>
   - <ShoppingCartPage>
     - <ShoppingCartLayout>
        - <ShoppingCart>   route start here  @page/shopping-cart
          - <CartItems>

ShoppingCartState provides a CascadingValue called CartModel when I navigate to /shopping-cart page, the CartModel is always accessible in ShoppingCart component or after it, but not before it for example in ShoppingCartLayout it always null.

I'm confused and I don't know. I feel that because the page starts rendering from here, that parameter can be received from here. If this is the problem, what is the solution to receive it in the previous components?

ShoppingCartState code :

@inject IShoppingCartService CartService
@implements IDisposable
@rendermode InteractiveServer

<div class="">
    <Loader IsLoading="IsLoading||CartModel is null">
        @if (CartModel is not null)
        {
            <CascadingValue Value="OnCartUpdated">
                <CascadingValue Value="CartModel">
                    @ChildContent
                </CascadingValue>
            </CascadingValue>
        }
    </Loader>
</div>

@code {

    private bool IsLoading;

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

    public Action OnCartUpdated { get; set; } = null!;

    public ShoppingCartModel? CartModel { get; set; } = new();

    protected override void OnInitialized()
    {
        OnCartUpdated += StateHasChanged;
    }

    public void Dispose()
    {
        OnCartUpdated -= StateHasChanged;
    }


    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await LoadCartAsync();
        }
    }

    private async Task LoadCartAsync()
    {
        IsLoading = true;
        CartModel = await CartService.GetCartAsync();
        IsLoading = false;
        StateHasChanged();
    }
}

Upvotes: 0

Views: 66

Answers (3)

samsoft
samsoft

Reputation: 56

in all child components you need to add [CascadingParameter]. did you?

@code {
    [CascadingParameter]
    public ShoppingCartModel? CartModel { get; set; } = default!;
 
}

in child component it should not be new() and be default!

Upvotes: 0

Qiang Fu
Qiang Fu

Reputation: 8811

You could try put state in the the Routes.razor

<ShoppingCartState>
    <Router AppAssembly="typeof(Program).Assembly">
...
    </Router>
</ShoppingCartState>

By the way how do you wrap the components? what difference between <ShoppingCartPage> and <ShoppingCart>?

Upvotes: 0

MrC aka Shaun Curtis
MrC aka Shaun Curtis

Reputation: 30310

It's not obvious from the code you've provided where the issue is. However, the ShoppingCartState code looks messy, doesn't need much of the logic you've coded, and is probably the cause the problem.

Some observations:

  1. Why are you getting CartModel in OnAfterRenderAsync instead of OnInitializedAsync? If you doing so to avoid trying to load CartModel in the pre-render, then use RendererInfo as I've shown in the code below. See - Is the NET 8 Blazor web app template flawed? and many other answers on why you shouldn't do what you're doing in OnAfterRenderAsync.

  2. [CascadingParameter] can be null, so deal with a possible null where your use it. The Loader stuff is an overcomplication you don't need.

Here's a simpler version of ShoppingCartState.

@inject IShoppingCartService CartService
@implements IDisposable
@rendermode InteractiveServer

<CascadingValue Value="OnCartUpdated">
    <CascadingValue Value="CartModel">
        @ChildContent
    </CascadingValue>
</CascadingValue>

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

    public Action OnCartUpdated { get; set; } = null!;

    public ShoppingCartModel? CartModel { get; set; } = new();

    protected override async Task OnInitializedAsync()
    {
      if (this.RendererInfo.IsInteractive)
      {
        CartModel = await CartService.GetCartAsync();
        OnCartUpdated += StateHasChanged;
      }
    }

    public void Dispose()
    {
        OnCartUpdated -= StateHasChanged;
    }
}

Upvotes: 0

Related Questions