0

I have MainLayout.razor with two components: Header.razor and Content.razor. By selecting different menu options content gets changed. With each content changing I need to send content-depended value to the header. I tried different techniques with the same result - the value in the header doesn't get updated. Is there something else I need to do? My code:

Header.razor:

    <div>Content Name: @ContentName</div>
    @code {
        [Parameter]
        public string ContentName{ get; set; }
    }

Content.razor:

@code{
    [CascadingParameter]
    MainLayout Parent { get; set; }

    protected override void OnInitialized()
        {
            base.OnInitialized();
            ContentName = "Content 1";
            Parent.SetParameters(ContentName);
        }
}

MainLayout.razor:

@inherits LayoutComponentBase
@inject BlazorQART.Data.AppState AState
<div class="sidebar" >
    <NavMenu />
</div>

<div class="main">
    <div class="top-row px-4">
        <Header ContentName="@_ContentName" />
    </div>
    <div class="content px-4">
        <CascadingValue Value="@this">
            @Body
        </CascadingValue>
    </div>
</div>
@code{
    private string _ContentName{ get; set; }
    protected override void OnInitialized()
    {
        base.OnInitialized();
        AState.OnContentChange = OnContentChanged;
    }
    public void SetParameters (string content)
    {
        AState.ChangedContent = content;
    }
    protected async void OnContentChanged(string content)
    {
        await InvokeAsync(() =>
        {
            _ContentName = content;
        });
    }

AppState.cs:

    public class AppState
    {
        private string _ChangedContent;
        public Action<string> OnContentChange { get; set; }
        public string ChangedContent
        {
            get { return _ChangedContent; }
            set
            {
                _ChangedContent= value;
                OnContentChange.Invoke(_ChangedContent);
            }
        }
    }

poviry
  • 15
  • 4

1 Answers1

1

There are a number of approaches you can use to solve this problem and I'll share a couple of them.

The first option is to use an outer component to handle state and passing in parameters for your content and an EventCallBack method. In most cases the Blazor engine should pick up the change and re-render on it's own.

Link to MS Component Parameters documentation

Link to MS Event Callback documentation

The second option is a bit more involved, and involves using an injected service as a state container and event hub, and subscribing to events from each component to collect data. Take a look at the answer in this post for discussion on how this can be accomplished, and I would recommend diving into the article mentioned about Blazor DI lifetimes and making sure you understand them, but once you do you could set up Scoped or Transient service to support just the page you are on. This option discusses updating components across connections, but using the service in the same manner works with different components on the same page as well. This might be overkill for your situation, but it's good tool to have in your box.

A quick note on Cascading Parameters as well: I see you have one listed above, and this feature has it's uses, but it also couples any components that capture a [CascadingParameter] property to the component that supplies the parameter, as some functionality of your component will depend on that cascaded parameter. If you only have a 1 parent -> 1 child relationship, I would stick with [Parameter] and EventCallBack, which will keep your child component much more portable and reusable. However, if you need a value to pass down through multiple levels of child components, you might have a good case for cascading the value instead. You'll have to weight the pros and cons of each approach, but I encourage giving it some thought.

Nik P
  • 2,693
  • 9
  • 21