Search code examples
c#blazorblazor-server-side.net-8.0

C# Blazor .net 8 Service feature to access changes in mainlayouts @body and apply to a component in Mainlayout


I've referenced my code from Here and have a follow up

Attempting to access changed infomation in MainLayout based on changes in the @body

Mainlayout with NavPathComponent

<div class="page">
    <div class="sidebar">
        <NavMenu />
    </div>

    <main>
        <div class="top-row px-4">
            <NavPathComponent />
            <a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
        </div>

        <article class="content px-4">
            @Body
        </article>



        <footer style="background-color:#3a0647; position: fixed;bottom: 0; width: -webkit-fill-available; padding:5px;">
            <div class="logo-footer">
                <a href="https://bhinc.com/" target="_blank">
                    <img src="Images/bh-logo-light.svg" />
                </a>
            </div>
        </footer>
    </main>

</div>

My component.razor with what I want to update based on @vody changes

@inject NavigablePathService NavigablePath
@implements IDisposable

<h3>NavPathComponent</h3>
<div class="col top-row px-4">
    <p>
        @if (NavigablePath.NavigablePath != null)
        {
            @foreach (string folder in NavigablePath.NavigablePath.Split("\\"))
            {
                <label style="cursor: pointer;color:blue;">@(folder)</label>
                <label> > </label>
            }
        }
    </p>
</div>

@code {
    public Func<Task> NavPathChange { get; set; }

    protected override void OnInitialized()
    {
        NavPathChange = async () =>
        {
            await InvokeAsync(StateHasChanged);
        };
        NavigablePath.NavigablePathSelector += NavPathChange;
    }

    public void Dispose()
    {
        NavigablePath.NavigablePathSelector -= NavPathChange;
    }
}

My service.cs

public class NavigablePathService
{
    public string NavigablePath { get; set; }
    public Func<Task> NavigablePathSelector { get; set; }


    public async Task ChangePath(string newPath)
    {
        NavigablePath = newPath;
        await NavigablePathSelector.Invoke();
    }
}

The issue I run into is that Func is null when I call it from my OnIntialize in my @page

protected override void OnInitialized()
{

    navigablePathService.ChangePath("test\\banana");
}

First is this the right practive I attempted Casacading Value nesting a this Value="this" in MainLayout that didn't make much sense to me.

Would adding a constructor to my service and injecting it with a Func be a thing?

public NavigablePathService(Func<Task> navigablePathSelector)
{
    NavigablePathSelector = navigablePathSelector;
}

Solution

  • You could try this code:

    MainLayout.razor:

    @inherits LayoutComponentBase
    
    <PageTitle>NavPathApp</PageTitle>
    
    <div class="page">
        <div class="sidebar">
            <NavMenu />
        </div>
    
        <main>
            <div class="top-row px-4">
                <NavPathComponent />
                <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
            </div>
    
            <article class="content px-4">
                @Body
            </article>
        </main>
    </div>
    

    NavPathComponent.razor:

    @inject NavPathApp.Services.NavigablePathService NavigablePath
    @implements IDisposable
    
    <h3>NavPathComponent</h3>
    <div class="col top-row px-4">
        <p>
            @if (NavigablePath.NavigablePath != null)
            {
                @foreach (string folder in NavigablePath.NavigablePath.Split("\\"))
                {
                    <label style="cursor: pointer; color: blue;">@(folder)</label>
                    <label> > </label>
                }
            }
        </p>
    </div>
    
    @code {
        protected override void OnInitialized()
        {
           // Console.WriteLine("NavPathComponent: Subscribing to OnNavigablePathChanged event...");
            NavigablePath.OnNavigablePathChanged += UpdateNavPath;
        }
    
        private async Task UpdateNavPath()
        {
            //Console.WriteLine("NavPathComponent: Event triggered, updating state...");
            await InvokeAsync(StateHasChanged);
        }
    
        public void Dispose()
        {
            //Console.WriteLine("NavPathComponent: Unsubscribing from OnNavigablePathChanged event...");
            NavigablePath.OnNavigablePathChanged -= UpdateNavPath;
        }
    }
    

    NavigablePathService.cs:

    using System;
    using System.Threading.Tasks;
    
    namespace NavPathApp.Services
    {
        public class NavigablePathService
        {
            public string NavigablePath { get; set; }
            public event Func<Task> OnNavigablePathChanged;
    
            public async Task ChangePath(string newPath)
            {
                NavigablePath = newPath;
               // Console.WriteLine($"Path changed to: {NavigablePath}");
                if (OnNavigablePathChanged != null)
                {
                   // Console.WriteLine("Invoking OnNavigablePathChanged event...");
                    await OnNavigablePathChanged.Invoke();
                }
            }
        }
    }
    

    TestPage.razor:

    @page "/test"
    @inject NavPathApp.Services.NavigablePathService navigablePathService
    
    <h3>Test Page</h3>
    
    <button @onclick="ChangePath">Change Path</button>
    
    @code {
        private async Task ChangePath()
        {
           // Console.WriteLine("TestPage: Changing path to test\\banana...");
            await navigablePathService.ChangePath("test\\banana");
        }
    
        protected override async Task OnInitializedAsync()
        {
           // Console.WriteLine("TestPage: Initializing and setting path to initial\\path...");
            // Initial path setup if needed
            await navigablePathService.ChangePath("initial\\path");
        }
    }
    

    Program.cs:

    builder.Services.AddSingleton<NavigablePathService>();
    

    Result:

    enter image description here