Reputation: 109
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
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
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
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:
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
.
[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