Open Closed

Blazor WASM Dynamic switching of Layout for specific pages (Previous recommended fix does not work) #8843


User avatar
0
Spospisil created
  • ABP Framework version: v7.3.0

  • UI Type: Blazor WASM

  • Database System: EF Core (PostgreSQL, etc..)

  • Tiered (for MVC) or Auth Server Separated (for Angular): yes

When I implemented the suggestion for this ticket (https://abp.io/support/questions/7615/Different-Layouts-for-Admin-Pages-vs-Blazor-Application-Pages#answer-3a142526-dea6-14ec-9136-67f84bcec9bd), the application just freezes. If I put a console write statement in the code I see that this method just gets called thousands of times hence why it appears to be frozen.

How can I fix this?

@using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout
@using Volo.Abp.DependencyInjection
@inherits SideMenuLayout

@attribute [ExposeServices(typeof(SideMenuLayout))]
@attribute [Dependency(ReplaceServices = true)]


    @Body


@code
{
    [Inject] private NavigationManager _navManager { get; set; }

    private Type GetLayout()
    {
        var currentUrl = _navManager.Uri;

        //Console.WriteLine($"***** CURRENT URL: {currentUrl} *****");

        // if (currentUrl == "xxx")
        //{
        //     return typeof(AdminLayout);
        // }else if (currentUrl == xxxx)
        // {
        //     return typeof(ApplicationLayout);
        // }

        return typeof(SideMenuLayout);
    }
}

5 Answer(s)
  • User Avatar
    0
    enisn created
    Support Team .NET Developer

    Hi there,

    I can share the following implementation suggestion to you.

    Let's say we have LayoutA.razor, LayoutB.razor and a MyCustomLayout.razor file that manages the current layout

    LayoutA.razor

    @inherits LayoutComponentBase
    
    <div class="layout-container">
        <div class="layout-sidebar">
            <div class="layout-sidebar-header">
                <h1>Layout A</h1>
            </div>
        </div>
        <div class="layout-content">
            @Body
        </div>
    </div>
    

    LayoutB.razor

    @inherits LayoutComponentBase
    
    <div class="layout-container">
        <div class="layout-sidebar">
            <div class="layout-sidebar-header">
                <h1>Layout B</h1>
            </div>
        </div>
        <div class="layout-content">
            @Body
        </div>
    </div>
    

    MyCustomLayout.razor

    @using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout
    @inherits LayoutComponentBase
    @implements IDisposable
    @inject NavigationManager NavigationManager
    
    <LayoutView Layout="@CurrentLayout">
        @Body
    </LayoutView>
    
    
    @code {
        public Type CurrentLayout { get; set; } = typeof(SideMenuLayout);
    
        protected override Task OnInitializedAsync()
        {
            NavigationManager.LocationChanged += OnLocationChanged;
            SetLayout(NavigationManager.Uri);
            return base.OnInitializedAsync();
        }
    
        public void Dispose()
        {
            NavigationManager.LocationChanged -= OnLocationChanged;
        }
    
        private void OnLocationChanged(object sender, LocationChangedEventArgs e)
        {
            SetLayout(e.Location);
        }
    
        private void SetLayout(string location)
        {
    
            if (location.Contains("page-a"))
            {
                CurrentLayout = typeof(LayoutA);
            }
    
            else if (location.Contains("page-b"))
            {
                CurrentLayout = typeof(LayoutB);
            }
            else
            {
                CurrentLayout = typeof(SideMenuLayout);
            }
    
            StateHasChanged();
        }
    }
    

    And create two components with path /page-a and /page-b

    PageA.razor

    @page "/page-a"
    <h3>PageA</h3>
    
    <p>
        This is PageA.
    </p>
    

    PageB.razor

    @page "/page-b"
    <h3>PageA</h3>
    
    <p>
        This is PageA.
    </p>
    

    Then configure it to make it work with LeptonX theme as a default layout:

    Configure<LeptonXThemeBlazorOptions>(options =>
    {
        options.Layout = typeof(MyCustomLayout);
    });
    

    image.png

  • User Avatar
    0
    Spospisil created

    Ok. Thank you! That solution worked as how I needed.

  • User Avatar
    0
    Spospisil created

    Ok. Thank you! That solution worked as how I needed.

  • User Avatar
    0
    Spospisil created

    Ok, so now it appears all the pages that still use the standard ABP SideMenuLayout layout are not adding the MainHeaderToolbarUserMenu component to upper right corner of the page.

    Here is my code

    DynamicLayoutPicker.razor

    @inherits LayoutComponentBase
    
    
        @Body
    
    

    DynamicLayoutPicker.razor.cs

    using CFDataSystems.StructureCloud.Blazor.Themes.LeptonX.Layouts.TenantLayout;
    using CFDataSystems.StructureCloud.Components.Layouts;
    using Microsoft.AspNetCore.Components;
    using Microsoft.AspNetCore.Components.Routing;
    using System.Threading.Tasks;
    using System;
    using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout;
    
    namespace CFDataSystems.StructureCloud.Blazor.Themes.LeptonX.Layouts;
    
    public partial class DynamicLayoutPicker : LayoutComponentBase, IDisposable
    {
        [Inject] private NavigationManager _navManager { get; set; }
        public Type CurrentLayout { get; set; } = typeof(SideMenuLayout);
    
        protected override Task OnInitializedAsync()
        {
            _navManager.LocationChanged += OnLocationChanged;
            SetLayout(_navManager.Uri);
            return base.OnInitializedAsync();
        }
    
        public void Dispose()
        {
            _navManager.LocationChanged -= OnLocationChanged;
        }
    
        private void OnLocationChanged(object sender, LocationChangedEventArgs e)
        {
            SetLayout(e.Location);
        }
    
        private void SetLayout(string location)
        {
            Console.WriteLine($"***** CURRENT URL: {location} *****");
    
            if (location.Contains("tenant-procedure-list"))
            {
                CurrentLayout = typeof(ProceduresPageLayout);
                Console.WriteLine($"***** CURRENT Layout: ProceduresPageLayout *****");
            }
            else if (location.Contains("SampleTenantDefault"))
            {
                CurrentLayout = typeof(TenantDefaultLayout);
                Console.WriteLine($"***** CURRENT Layout: TenantDefaultLayout *****");
            }
            else
            {
                CurrentLayout = typeof(SideMenuLayout);
                Console.WriteLine($"***** CURRENT Layout: SideMenuLayout *****");
            }
    
            StateHasChanged();
        }
    }
    

    BlazorModule.cs

            Configure(options =>
            {
                //options.Layout = LeptonXBlazorLayouts.SideMenu;
                options.Layout = typeof(DynamicLayoutPicker);
            });
    

    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>

  • User Avatar
    0
    Spospisil created

    Hi,

    So you can disregard this last comment as I tracked down the logic and see that I needed to create my own ToolbarContributor that specificaly look at the LeptonXThemeBlazorOptions and factor in my DynamicLayoutPicker in order to add the SideMenuUserMenu component to the ToolBar.Items collection.

    public class StructureToolbarContributor : IToolbarContributor
    {
        public Task ConfigureToolbarAsync(IToolbarConfigurationContext context)
        {
            if (context.Toolbar.Name == StandardToolbars.Main)
            {
                var options = context.ServiceProvider.GetRequiredService>().Value;
    
                if (options.Layout == typeof(DynamicLayoutPicker))
                {
                    context.Toolbar.Items.Add(new ToolbarItem(typeof(SideMenuUserMenu)));
                }
    
            }
            return Task.CompletedTask;
        }
    }
    
    
    Changes to the Blazor ABP Module class
    
            Configure(options =>
            {
                options.Contributors.Add(new LeptonXThemeToolbarContributor());
                options.Contributors.Add(new StructureToolbarContributor());
                //options.Contributors.Add(new MessageToolBarContributor());
            });
    

    Thanks

Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
Do you need assistance from an ABP expert?
Schedule a Meeting
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v9.2.0-preview. Updated on March 13, 2025, 04:08