abpframework / abpframework/abp
SideMenuLayoutComponent destroys router-outlet on window resize across md breakpoint, losing all routed component state
@sumeyyeKurtulus is already working on this.
Since Apr 13, 2026.
- Dominant language
- C#
- Stars
- 14.4k
- Forks
- 3.7k
- Avg merge
- 15h 32m
- Merged PRs (30d)
- 106
Description
Is there an existing issue for this?
- I have searched the existing issues
Description
In SideMenuLayoutComponent (@volosoft/ngx-lepton-x), the entire page content area -- including the router-outlet and its active routed component -- is wrapped inside two mutually exclusive *lpxResponsive structural directives. When the browser window is resized across the md breakpoint (768px), the ResponsiveDirective destroys one embedded view and creates the other, which destroys and recreates the entire routed component tree. This causes all component state -- including reactive form data, scroll position, and any in-progress user input -- to be lost.
Not affected: TopMenuLayoutComponent renders its content area unconditionally without any *lpxResponsive wrapper, confirming this is a bug specific to the side-menu layout.
Reproduction Steps
- Use the side-menu layout (default for ABP Commercial Angular apps)
- Navigate to any page with a form (e.g., a create/edit entity page)
- Fill in several form fields
- Resize the browser window width across the 768px boundary (from above to below, or vice versa)
- Observe that all form state is reset -- the component has been destroyed and recreated
Expected behavior
Resizing the browser window should not destroy the routed component. Only layout chrome elements (sidebar visibility, scroll container wrapper) should be toggled by *lpxResponsive. The content area containing the router-outlet should remain stable across breakpoint changes.
Actual behavior
Crossing the 768px breakpoint destroys the entire routed component tree (including the router-outlet), causing:
- All reactive form data to be lost
- Scroll position to reset
- Any in-progress user input to disappear
- Component lifecycle hooks (
ngOnInit, etc.) to re-run, triggering duplicate API calls
Regression?
Unknown -- the SideMenuLayoutComponent template is identical between v4.1.1 and v5.1.0 of @volosoft/ngx-lepton-x. This pattern has likely existed since the introduction of the *lpxResponsive directive in the side-menu layout.
Known Workarounds
No clean workaround. Switching to TopMenuLayoutComponent avoids the issue but changes the entire application layout. Overriding SideMenuLayoutComponent with a custom template is possible but fragile across upgrades.
Version
9.1.1 (LeptonX theme v4.1.1 -- also verified unfixed in @volosoft/ngx-lepton-x v5.1.0)
User Interface
Angular
Database Provider
EF Core (Default)
Tiered or separate authentication server
Separate Auth Server
Operation System
Windows (Default)
Other information
Technical root cause:
1. ResponsiveDirective (@volo/ngx-lepton-x.core) -- destroys and recreates its embedded view on breakpoint change:
this.render = (shouldRender) => {
if (shouldRender && !this.hasRendered) {
this.viewContainer.createEmbeddedView(this.templateRef);
this.hasRendered = true;
} else if (!shouldRender && this.hasRendered) {
this.viewContainer.clear(); // <-- destroys entire DOM subtree
this.hasRendered = false;
}
this.parentCdr.detectChanges();
};
2. SideMenuLayoutComponent template (@volosoft/ngx-lepton-x) -- wraps #content (which contains router-outlet) in competing *lpxResponsive guards:
<!-- Rendered when viewport is BELOW md -->
<ng-container *lpxResponsive="'all md-none'">
<ng-container *ngTemplateOutlet="content" />
</ng-container>
<!-- Rendered when viewport is AT OR ABOVE md -->
<ng-container *lpxResponsive="'md'">
<div class="lpx-scroll-container">
<ng-container *ngTemplateOutlet="content" />
</div>
</ng-container>
3. Contrast with TopMenuLayoutComponent (NOT affected) -- renders content unconditionally:
<div class="lpx-content-container">
<div class="lpx-content">
<ng-container *ngTemplateOutlet="contentPanel?.template || defaultContent" />
<ng-template #defaultContent>
<ng-content />
</ng-template>
</div>
</div>
Suggested fix -- use a stable wrapper element with a conditional CSS class instead of two competing structural directives:
<!-- Always rendered -- never destroyed -->
<div [class.lpx-scroll-container]="isAboveMd$ | async">
<ng-container *ngTemplateOutlet="content" />
</div>
This preserves the layout behavior (applying lpx-scroll-container styles only above md) without destroying the DOM subtree.
Packages affected:
@volosoft/ngx-lepton-xv4.1.1 through v5.1.0@volo/ngx-lepton-x.corev4.1.1 through v5.1.0@volosoft/abp.ng.theme.lepton-x(ABP v9.1.0 through v9.2+)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.