blazorblazor-server-side

Blazor CascadingParameter is always null


New to Blazor, having trouble getting a CascadingParameter to populate from my MainLayout.razor:

@code
{
    private Toasts Toasts { get; set; }
}
        
<Toasts @ref="@Toasts" />
    
<CascadingValue Value="Toasts">
    @Body
</CascadingValue>

And Index.razor page:

[CascadingParameter]
public Toasts Toasts { get; set; }

public async Task PackageChanged(string value)
{
   Toasts.Something(value); //Toasts is null here
}

And finally Toasts.razor:

@code {

    private string Message { get; set; }

    public void Something(string message)
    {
        Message = message;
        StateHasChanged();
    }
}

@Message

This code below frustratingly works, but I'd like to cascade just the one component (<Toasts />) instead of the whole layout:

<CascadingValue Value="@this">
    <Toasts @ref="@Toasts" />
    @Body
</CascadingValue>

And then index:

[CascadingParameter]
public MainLayout Layout { get; set; }

 public async Task PackageChanged(string value)
 {
      Layout.Toasts.Something(value); //this behaves as expected
 }

Solution

  • This code is working for me

    MainLayout...partial code

    <div class="main">
            <div class="top-row px-4">
                <Toasts @ref="Toasts" />
            </div>
    
            @if (Toasts != null)
            {
                <CascadingValue Value="@Toasts">
                    @Body
                </CascadingValue>
            }
     </div>
    
    @code
    {
        protected Toasts Toasts;
    
        protected override void OnAfterRender(bool firstRender)
        {
            if (firstRender)
            {
                StateHasChanged();
            }
        }
     }
    

    The following solution, in which we cascade a reference to MainLayout, and then retrieve a reference to the Toasts component, is a bit more effective than the one above, as it does not require the MainLayout to render a second time.

    MainLayout...partial code

    <div class="main">
            <div class="top-row px-4">
                <Toasts @ref="Toasts" />
            </div>
    
             <CascadingValue Value="this">
                @Body
             </CascadingValue>     
     </div>
    
    @code
    {
        public Toasts Toasts { get; set; }
     } 
    

    Index.razor

    @page "/"
    
    @code{
       
        [CascadingParameter]
        public MainLayout Layout { get; set; }
        
       
        protected override void OnInitialized()
        {
            // Reference the Toasts component's methods and properties
            // Note: You must not pass parameter values to the Toasts
            // component from here. You'll get a warning, or perhaps 
            // an error.
     
            Layout.Toasts.Something("Pass a message to the 
                                    Something method");
        }
    
    }