Home > Net >  Blazor : Issues with using Cascading parameters for passing values
Blazor : Issues with using Cascading parameters for passing values

Time:06-28

I have two blazor components the first component calls a method

 <CascadingValue Value="this">
@ChildContent
</CascadingValue>

@code{ 


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

public async Task<Debtor> ConsolitdateCurrentDebtor(int debtorID)
{
    Debtor? currentDebtor = null;

    if (DebtorStoreList != null && DebtorStoreList.Count > 0)
    {
        currentDebtor = DebtorStoreList.Where(d => d.ID == debtorID).Single();
    }
    else
    {
        currentDebtor = await _debtorService.GetDebtorInformation();
    }

    CurrentDebtor = currentDebtor;
   // StateHasChanged();  unless called no update takes place
    return currentDebtor;
  }
 }

Now I have a property called CurrentDebtor in this AppState class. One blazor component comes and calls this method and sets the Current Debtor.

From the other component, i am doing something like

@code {
Debtor? debtor;

[CascadingParameter]
AppState AppState{ get; set; }

protected override async Task OnInitializedAsync()
 {
    debtor = AppState.CurrentDebtor;
 }
}

However the CurrentDebtor is coming null, the only way it doesn't come null is if StateHasChanged() is called. Is there anyway without forcing it to call Statehaschanged to populate it correctly?

CodePudding user response:

Trying to "wire" multiple components together using cascading parameters and two way binding isn't always the best way to do things, particularly sub-component to sub-component within a page (which it appears you are trying to do from the information shown in the question).

There is an alternative approach using the standard event model.

My Debtor class

public class Debtor
{
    public Guid Id { get; set; }    
    public string? Name { get; set; }
}

A Debtor view service:

public class DebtorViewService
{
    public Debtor Record { get; private set; } = default!;

    public event EventHandler<Debtor>? DebtorChanged;

    public DebtorViewService()
    {
        // Get and assign the Debtor Store DI service
    }

    public async ValueTask GetCurrentDebtor(Guid debtor)
    {
        // Emulate getting the Debtor from the Debtor Service
        await Task.Delay(1000);
        this.Record = new Debtor() { Name=$"Fred {DateTime.Now.ToLongTimeString()}", Id = Guid.NewGuid()};
        this.DebtorChanged?.Invoke(this, this.Record); 
    }
}

Registered in services like this:

builder.Services.AddScoped<DebtorViewService>();

A Component to get the Debtor:

GetDebtor.razor

@inject DebtorViewService Service

<div >
    <h5>GetDebtor Component</h5>
    <div>
        <button  @onclick=this.GetCurrentDebtor>Get Debtor</button>
    </div>
</div>
@code {
    private async Task GetCurrentDebtor()
        => await Service.GetCurrentDebtor(Guid.Empty);
}

A component to show the Debtor:

ShowDebtor.razor

@inject DebtorViewService Service
@implements IDisposable

<div >
    <h5>ShowDebtor Component</h5>
    @if (this.Service.Record is not null)
    {
        <div>
            Id : @this.Service.Record.Id
        </div>
        <div>
            Name : @this.Service.Record.Name
        </div>
    }
</div>

@code {
    protected override void OnInitialized()
        => this.Service.DebtorChanged  = this.OnDebtorChanged;

    private void OnDebtorChanged(object? sender, Debtor value)
    => this.InvokeAsync(this.StateHasChanged);

    public void Dispose()
        => this.Service.DebtorChanged -= this.OnDebtorChanged;
}

And a demo page:

@page "/"
<PageTitle>Index</PageTitle>

<h1>Hello, world!</h1>

Welcome to your new app.

<GetDebtor />

<ShowDebtor />

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

@code{
}

The data and the change event is now in a single shared (Scoped) DI service instance that everyone has access to.

You should be able to build your components based on this code.

  • Related