-
Notifications
You must be signed in to change notification settings - Fork 6.8k
Description
Is this a regression?
- Yes, this behavior used to work in the previous version
The previous version in which this bug was not present was
No response
Description
When building an Angular library (using partial compilation mode / ng-packagr), binding to the preserveContent input on AccordionPanel causes the following error:
NG3004: Unable to import symbol DeferredContentAware.
The symbol is not exported from @angular/aria/types/accordion.d.ts (module '@angular/aria/accordion').
Root Cause Analysis
The AccordionPanel directive uses hostDirectives to expose DeferredContentAware:
// From @angular/aria/types/accordion.d.ts (line 53)
static ɵdir: ɵɵDirectiveDeclaration<AccordionPanel, "[ngAccordionPanel]", ...,
[{ directive: typeof DeferredContentAware; inputs: { "preserveContent": "preserveContent"; }; outputs: {}; }]>;The preserveContent input IS exposed via the hostDirectives inputs mapping, making it appear as a valid input on AccordionPanel. However, when Angular's AOT compiler type-checks a template binding like [preserveContent]="true", it needs to import the DeferredContentAware type to verify the binding.
The problem is that DeferredContentAware is imported from an internal module:
// From @angular/aria/types/accordion.d.ts (line 4)
import { DeferredContentAware } from '@angular/aria/_deferred-content-chunk.js';The underscore prefix (_) indicates this is a private/internal module, and DeferredContentAware is not re-exported from @angular/aria/accordion.
The Inconsistency
AccordionPanelexposespreserveContentas a usable input viahostDirectives- But the type required to use this input (
DeferredContentAware) is not exported - This works in JIT mode (development) but fails in AOT/partial compilation (library builds)
Reproduction
StackBlitz link: Unable to reproduce in StackBlitz as this requires a library build with ng-packagr (partial compilation mode). StackBlitz runs in JIT mode where the issue doesn't manifest.
- Create an Angular library project using ng-packagr
- Create a component that uses
AccordionPanelwithpreserveContentbinding:
@Component({
selector: 'my-accordion',
imports: [AccordionGroup, AccordionTrigger, AccordionPanel, AccordionContent],
template: `
<div ngAccordionGroup>
<button ngAccordionTrigger [panelId]="'panel-1'">Toggle</button>
<div ngAccordionPanel
[panelId]="'panel-1'"
[preserveContent]="true"> <!-- THIS CAUSES NG3004 -->
<ng-template ngAccordionContent>
<p>Panel content</p>
</ng-template>
</div>
</div>
`
})
export class MyAccordionComponent {}- Build the library:
ng build my-library
# or
npx ng-packagr -p ng-package.json- Observe the error:
NG3004: Unable to import symbol DeferredContentAware.
The symbol is not exported from @angular/aria/types/accordion.d.ts (module '@angular/aria/accordion').
Expected Behavior
One of the following:
Option A: Export DeferredContentAware (Preferred)
Export DeferredContentAware from @angular/aria/accordion so that consumers can use the preserveContent input that is already exposed via hostDirectives:
// @angular/aria/accordion/index.ts
export { DeferredContentAware } from '../_deferred-content-chunk';
export { AccordionGroup, AccordionTrigger, AccordionPanel, AccordionContent } from './accordion';Option B: Document the limitation
If preserveContent is intentionally internal, remove it from the hostDirectives inputs mapping or document that it cannot be used in library builds.
Actual Behavior
- The
preserveContentinput appears to be available (TypeScript/IDE shows it as a valid input) - Development mode (JIT) works fine
- Library builds (AOT partial compilation) fail with
NG3004 - There is no workaround - attribute binding
[attr.preserveContent]="true"bypasses the error but doesn't actually set the signal input value
Environment
Angular CLI: 21.0.4
Node: 24.12.0
Package Manager: npm 11.6.2
OS: win32 arm64
Angular: 21.0.6
@angular/aria: 21.0.5
@angular/cdk: 21.0.5
Browser(s): Chrome, Firefox, Safari (not browser-specific)
Operating System: Windows 11 ARM64 (also reproducible on other platforms)
Additional Context
Why preserveContent is needed
Without preserveContent=true, there can be content duplication issues when combining:
ngAccordionContent(deferred content)NgTemplateOutlet(for user-provided content templates)- Native Angular animations (
animate.enter/animate.leave)
When the panel visibility changes, the deferred content system destroys/recreates views, which can conflict with animation lifecycle and NgTemplateOutlet, resulting in duplicate content appearing in the DOM.
Current Workaround
The only workaround is to not use preserveContent and avoid combining deferred content with animations - but this limits the functionality that Angular ARIA accordion can provide.