ABP Framework version: v7.2.3
UI Type: Blazor Server
Database System: EF Core
Tiered (for MVC) or Auth Server Separated (for Angular): yes
Exception message and full stack trace:
Steps to reproduce the issue: seems to be related to resource release.
Hello I am using LeptonXTheme UI. I want to be able to see which pages have been opened like the picture above. Display all open tabs at the top and click on the tab above to achieve the effect of switching pages. This is what I have here. A usage habit, does LeptonXTheme UI support this function? In addition, every time I open the page and return to the previous page, the page status will not be retained. Is it possible to maintain the page status like vue keep-alive or do A short-lived storage; now I cannot use LeptonXTheme UI to achieve the above functions. Can you give me some methods or suggestions to achieve these functions?
42 Answer(s)
-
0
Hi,
Here is the code, But there is a serious problem when deleting the tab, which may be related to
blazor.zone
. There is nothing I can do. so, I don't recommend you to useblazor.zone
<BootstrapBlazor.Components.Tab @ref="_tabComponent" class="mt-3" ShowClose="true" OnClickTabItemAsync="OnSelectedTabChanged" OnCloseTabItemAsync="ClosePage"> @foreach (var tab in RouteDataList) { <BootstrapBlazor.Components.TabItem Text="@tab.Title"> <ContentTabToolbar RouteData="tab"></ContentTabToolbar> @tab.Body </BootstrapBlazor.Components.TabItem> } </BootstrapBlazor.Components.Tab> @code { private BootstrapBlazor.Components.Tab _tabComponent; [Inject] NavigationManager NavigationManager { get; set; } [Inject] PageHeaderService PageHeaderService { get; set; } [Inject] IMenuManager MenuManager { get; set; } [CascadingParameter(Name = "RouteData")] RouteData? RouteData { get; set; } List<ContentTabRouteData> RouteDataList { get; set; } = new(); [Parameter] public RenderFragment Body { get; set; } string? CurrentUrl { get; set; } ApplicationMenu? ApplicationMenu { get; set; } protected override async Task OnInitializedAsync() { ApplicationMenu = await MenuManager.GetMainMenuAsync(); PageHeaderService.PageHeaderDataChanged += OnPageHeaderDataChanged; NavigationManager.LocationChanged += OnLocationChanged; } protected override void OnAfterRender(bool firstRender) { if (firstRender) { OnLocationChanged(this, new LocationChangedEventArgs(NavigationManager.Uri, false)); } base.OnAfterRender(firstRender); } private async void OnPageHeaderDataChanged(object? sender, PageHeaderData? e) { if (e == null) { return; } var route = RouteDataList.FirstOrDefault(x => x.PageType == e.Type); route?.SetPageLayout(e.Title, e.BreadcrumbItems, e.PageToolbarItems); await InvokeAsync(StateHasChanged); } private async Task OnSelectedTabChanged(BootstrapBlazor.Components.TabItem tabItem) { var route = RouteDataList.FirstOrDefault(x => x.Title == tabItem.Text); if (route != null) { CurrentUrl = route.Url; var data = PageHeaderService.GetPageHeaderData(route.PageType); if (data != null) { route.SetPageLayout(data.Title, data.BreadcrumbItems, data.PageToolbarItems); } _tabComponent.ActiveTab(tabItem); NavigationManager.NavigateTo(route.Url); } await InvokeAsync(StateHasChanged); } private RenderFragment GenerateBody(ContentTabRouteData routeData) { return builder => { builder.OpenComponent(0, routeData.PageType); foreach (var routeValue in routeData.RouteValues) { builder.AddAttribute(1, routeValue.Key, routeValue.Value); } builder.CloseComponent(); }; } private async Task<bool> ClosePage(TabItem tabItem) { var routeData = GetRouteData(tabItem); if (routeData == null) { return true; } RouteDataList.Remove(routeData); await _tabComponent.RemoveTab(tabItem); var activeTab = _tabComponent.GetActiveTab(); if (activeTab != null) { await OnSelectedTabChanged(activeTab); } return false; } private ContentTabRouteData? GetRouteData(TabItem? tabItem) { return tabItem == null ? null : RouteDataList.FirstOrDefault(x => x.Title == tabItem.Text); } private void OnLocationChanged(object? sender, LocationChangedEventArgs e) { if (RouteData == null || CurrentUrl == e.Location) { return; } var contentTabRouteData = new ContentTabRouteData { PageType = RouteData.PageType, RouteValues = new Dictionary<string, object>(RouteData.RouteValues), TabName = RouteData.PageType.Name.ToLower(), Url = e.Location, Title = TryGetTitle(ApplicationMenu.Items, e.Location) }; contentTabRouteData.Body = GenerateBody(contentTabRouteData); RouteDataList.AddIfNotContains(x => x.PageType == RouteData.PageType, () => contentTabRouteData); StateHasChanged(); OnSelectedTabChanged(_tabComponent.Items.First(x => x.Text == contentTabRouteData.Title)); } protected override void Dispose(bool disposing) { PageHeaderService.PageHeaderDataChanged -= OnPageHeaderDataChanged; NavigationManager.LocationChanged -= OnLocationChanged; base.Dispose(disposing); } private string? TryGetTitle(ApplicationMenuItemList items, string url) { foreach (var item in items) { if (item.Items.Any()) { var result = TryGetTitle(item.Items, url); if (result != null) { return result; } continue; } if (item.Url.IsNullOrWhiteSpace()) { continue; } if (url.EndsWith(item.Url!.Replace("~/", ""))) { return item.DisplayName; } } // you can custom here return null; } }
-
0
You can consider https://mudblazor.com/components/tabs#scrolling-tabs or wait for Blazorise implements this function
-
0
Hi,
Here is the code,
But there is a serious problem when deleting the tab, which may be related toblazor.zone
. There is nothing I can do. so, I don't recommend you to useblazor.zone
<BootstrapBlazor.Components.Tab @ref="_tabComponent" class="mt-3" ShowClose="true" OnClickTabItemAsync="OnSelectedTabChanged" OnCloseTabItemAsync="ClosePage"> @foreach (var tab in RouteDataList) { <BootstrapBlazor.Components.TabItem Text="@tab.Title"> <ContentTabToolbar RouteData="tab"></ContentTabToolbar> @tab.Body </BootstrapBlazor.Components.TabItem> } </BootstrapBlazor.Components.Tab> @code { private BootstrapBlazor.Components.Tab _tabComponent; [Inject] NavigationManager NavigationManager { get; set; } [Inject] PageHeaderService PageHeaderService { get; set; } [Inject] IMenuManager MenuManager { get; set; } [CascadingParameter(Name = "RouteData")] RouteData? RouteData { get; set; } List<ContentTabRouteData> RouteDataList { get; set; } = new(); [Parameter] public RenderFragment Body { get; set; } string? CurrentUrl { get; set; } ApplicationMenu? ApplicationMenu { get; set; } protected override async Task OnInitializedAsync() { ApplicationMenu = await MenuManager.GetMainMenuAsync(); PageHeaderService.PageHeaderDataChanged += OnPageHeaderDataChanged; NavigationManager.LocationChanged += OnLocationChanged; } protected override void OnAfterRender(bool firstRender) { if (firstRender) { OnLocationChanged(this, new LocationChangedEventArgs(NavigationManager.Uri, false)); } base.OnAfterRender(firstRender); } private async void OnPageHeaderDataChanged(object? sender, PageHeaderData? e) { if (e == null) { return; } var route = RouteDataList.FirstOrDefault(x => x.PageType == e.Type); route?.SetPageLayout(e.Title, e.BreadcrumbItems, e.PageToolbarItems); await InvokeAsync(StateHasChanged); } private async Task OnSelectedTabChanged(BootstrapBlazor.Components.TabItem tabItem) { var route = RouteDataList.FirstOrDefault(x => x.Title == tabItem.Text); if (route != null) { CurrentUrl = route.Url; var data = PageHeaderService.GetPageHeaderData(route.PageType); if (data != null) { route.SetPageLayout(data.Title, data.BreadcrumbItems, data.PageToolbarItems); } _tabComponent.ActiveTab(tabItem); NavigationManager.NavigateTo(route.Url); } await InvokeAsync(StateHasChanged); } private RenderFragment GenerateBody(ContentTabRouteData routeData) { return builder => { builder.OpenComponent(0, routeData.PageType); foreach (var routeValue in routeData.RouteValues) { builder.AddAttribute(1, routeValue.Key, routeValue.Value); } builder.CloseComponent(); }; } private async Task<bool> ClosePage(TabItem tabItem) { var routeData = GetRouteData(tabItem); if (routeData == null) { return true; } RouteDataList.Remove(routeData); await _tabComponent.RemoveTab(tabItem); var activeTab = _tabComponent.GetActiveTab(); if (activeTab != null) { await OnSelectedTabChanged(activeTab); } return false; } private ContentTabRouteData? GetRouteData(TabItem? tabItem) { return tabItem == null ? null : RouteDataList.FirstOrDefault(x => x.Title == tabItem.Text); } private void OnLocationChanged(object? sender, LocationChangedEventArgs e) { if (RouteData == null || CurrentUrl == e.Location) { return; } var contentTabRouteData = new ContentTabRouteData { PageType = RouteData.PageType, RouteValues = new Dictionary<string, object>(RouteData.RouteValues), TabName = RouteData.PageType.Name.ToLower(), Url = e.Location, Title = TryGetTitle(ApplicationMenu.Items, e.Location) }; contentTabRouteData.Body = GenerateBody(contentTabRouteData); RouteDataList.AddIfNotContains(x => x.PageType == RouteData.PageType, () => contentTabRouteData); StateHasChanged(); OnSelectedTabChanged(_tabComponent.Items.First(x => x.Text == contentTabRouteData.Title)); } protected override void Dispose(bool disposing) { PageHeaderService.PageHeaderDataChanged -= OnPageHeaderDataChanged; NavigationManager.LocationChanged -= OnLocationChanged; base.Dispose(disposing); } private string? TryGetTitle(ApplicationMenuItemList items, string url) { foreach (var item in items) { if (item.Items.Any()) { var result = TryGetTitle(item.Items, url); if (result != null) { return result; } continue; } if (item.Url.IsNullOrWhiteSpace()) { continue; } if (url.EndsWith(item.Url!.Replace("~/", ""))) { return item.DisplayName; } } // you can custom here return null; } }
Hi.What's the problem with closing it?
-
0
You can give it a try
-
0
-
0
Clicking the close button will trigger the SelectedTabChanged method of Tabs
-
0
This may be the limitation of blazorise and blazor (I'm not sure)
Add custom
class
andstopPropagation
: The page will flash, but it works.<Tab Name="@tab.TabName" Class="keep-tab-active"> @tab.Title <CloseButton @onclick:stopPropagation="true" Clicked="() => ClosePage(tab)"/> </Tab> .... <TabPanel Name="@route.TabName" Class="keep-panel-active" > ....
-
0
This may be the limitation of blazorise and blazor (I'm not sure)
Add custom
class
andstopPropagation
: The page will flash, but it works.<Tab Name="@tab.TabName" Class="keep-tab-active"> @tab.Title <CloseButton @onclick:stopPropagation="true" Clicked="() => ClosePage(tab)"/> </Tab> .... <TabPanel Name="@route.TabName" Class="keep-panel-active" > ....
Hi,This flash will clear all input boxes and other conditions,It's like refreshing
-
0
Yes, but I have no idea about this now.
-
0
Yes, but I have no idea about this now.
Sometimes clicking the close button won't refresh, it's really weird
-
0
Yes, it's weird
-
0
-
0
Hi,
Here is the code: https://github.com/abpframework/abp/blob/956706eb62dba926ecd8876a9ba4b490088b8259/framework/src/Volo.Abp.AspNetCore.Components.Web.Theming/Layout/PageHeader.razor#L16-L39
-
0
Yes, but I have no idea about this now.
It is related to Blazor's comparison algorithm. @key specifies the key used by the diffing algorithm to retain the elements in the set. I have solved this problem, thank you.
-
0
Can you share your solution, it might help others, thanks
-
0
Can you share your solution, it might help others, thanks
Sure.Set key for TabPanel in foreach
<Tabs Class="custom-nav-tabs" SelectedTab="@selectedTab" SelectedTabChanged="@OnSelectedTabChanged"> <Items> @foreach (var tab in RouteDataList) { <Tab Class="tab keep-tab-active" Name="@tab.TabName"> <Span Class="tab-span">@tab.Title</Span> <Button @onclick:stopPropagation="true" Clicked="()=>ClosePage(tab)" Class="close-button">×</Button> </Tab> } </Items> <Content> @foreach (var route in RouteDataList) { <TabPanel @key="route.Url" Class="keep-panel-active" Name="@route.TabName"> <ContentTabToolbar RouteData="route"></ContentTabToolbar> @route.Body </TabPanel> } </Content> </Tabs>
-
0
Thanks