diff --git a/src/cdk/accordion/accordion-item.ts b/src/cdk/accordion/accordion-item.ts index 22f4a7f2cb61..2a020c0fc2a4 100644 --- a/src/cdk/accordion/accordion-item.ts +++ b/src/cdk/accordion/accordion-item.ts @@ -14,6 +14,7 @@ import { OnDestroy, Optional, ChangeDetectorRef, + SkipSelf, } from '@angular/core'; import {UniqueSelectionDispatcher} from '@angular/cdk/collections'; import {CdkAccordion} from './accordion'; @@ -30,6 +31,11 @@ let nextId = 0; @Directive({ selector: 'cdk-accordion-item, [cdkAccordionItem]', exportAs: 'cdkAccordionItem', + providers: [ + // Provide CdkAccordion as undefined to prevent nested accordion items from registering + // to the same accordion. + {provide: CdkAccordion, useValue: undefined}, + ], }) export class CdkAccordionItem implements OnDestroy { /** Subscription to openAll/closeAll events. */ @@ -90,7 +96,7 @@ export class CdkAccordionItem implements OnDestroy { /** Unregister function for _expansionDispatcher. */ private _removeUniqueSelectionListener: () => void = () => {}; - constructor(@Optional() public accordion: CdkAccordion, + constructor(@Optional() @SkipSelf() public accordion: CdkAccordion, private _changeDetectorRef: ChangeDetectorRef, protected _expansionDispatcher: UniqueSelectionDispatcher) { this._removeUniqueSelectionListener = diff --git a/src/cdk/accordion/accordion.spec.ts b/src/cdk/accordion/accordion.spec.ts index 4fd65bf86a60..7af1185d3f1b 100644 --- a/src/cdk/accordion/accordion.spec.ts +++ b/src/cdk/accordion/accordion.spec.ts @@ -12,7 +12,8 @@ describe('CdkAccordion', () => { CdkAccordionModule ], declarations: [ - SetOfItems + SetOfItems, + NestedItems, ], }); TestBed.compileComponents(); @@ -53,6 +54,14 @@ describe('CdkAccordion', () => { expect(firstPanel.expanded).toBeTruthy(); expect(secondPanel.expanded).toBeTruthy(); }); + + it('should not register nested items to the same accordion', () => { + const fixture = TestBed.createComponent(NestedItems); + const innerItem = fixture.componentInstance.innerItem; + const outerItem = fixture.componentInstance.outerItem; + + expect(innerItem.accordion).not.toBe(outerItem.accordion); + }); }); @Component({template: ` @@ -65,3 +74,15 @@ class SetOfItems { @ViewChild('item2') item2; multi: boolean = false; } + + +@Component({template: ` + + + + + `}) +class NestedItems { + @ViewChild('outerItem') outerItem: CdkAccordionItem; + @ViewChild('innerItem') innerItem: CdkAccordionItem; +} diff --git a/src/lib/expansion/accordion.spec.ts b/src/lib/expansion/accordion.spec.ts index 6c12e2db42a4..f69022c1444d 100644 --- a/src/lib/expansion/accordion.spec.ts +++ b/src/lib/expansion/accordion.spec.ts @@ -2,10 +2,10 @@ import {async, TestBed} from '@angular/core/testing'; import {Component, ViewChild} from '@angular/core'; import {By} from '@angular/platform-browser'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; -import {MatExpansionModule, MatAccordion} from './index'; +import {MatExpansionModule, MatAccordion, MatExpansionPanel} from './index'; -describe('CdkAccordion', () => { +describe('MatAccordion', () => { beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ @@ -13,7 +13,8 @@ describe('CdkAccordion', () => { MatExpansionModule ], declarations: [ - SetOfItems + NestedPanel, + SetOfItems, ], }); TestBed.compileComponents(); @@ -84,6 +85,14 @@ describe('CdkAccordion', () => { expect(panels[0].classes['mat-expanded']).toBeFalsy(); expect(panels[1].classes['mat-expanded']).toBeFalsy(); }); + + it('should not register nested panels to the same accordion', () => { + const fixture = TestBed.createComponent(NestedPanel); + const innerPanel = fixture.componentInstance.innerPanel; + const outerPanel = fixture.componentInstance.outerPanel; + + expect(innerPanel.accordion).not.toBe(outerPanel.accordion); + }); }); @@ -106,3 +115,18 @@ class SetOfItems { secondPanelExpanded: boolean = false; secondPanelDisabled: boolean = false; } + +@Component({template: ` + + + Outer Panel + + Inner Panel +

Content

+
+
+
`}) +class NestedPanel { + @ViewChild('outerPanel') outerPanel: MatExpansionPanel; + @ViewChild('innerPanel') innerPanel: MatExpansionPanel; +} diff --git a/src/lib/expansion/expansion-panel.ts b/src/lib/expansion/expansion-panel.ts index 63846745a44a..d7348f9da968 100644 --- a/src/lib/expansion/expansion-panel.ts +++ b/src/lib/expansion/expansion-panel.ts @@ -23,6 +23,7 @@ import { OnDestroy, Optional, SimpleChanges, + SkipSelf, ViewContainerRef, ViewEncapsulation, } from '@angular/core'; @@ -56,6 +57,11 @@ let uniqueId = 0; inputs: ['disabled', 'expanded'], outputs: ['opened', 'closed', 'expandedChange'], animations: [matExpansionAnimations.bodyExpansion], + providers: [ + // Provide MatAccordion as undefined to prevent nested expansion panels from registering + // to the same accordion. + {provide: MatAccordion, useValue: undefined}, + ], host: { 'class': 'mat-expansion-panel', '[class.mat-expanded]': 'expanded', @@ -87,7 +93,7 @@ export class MatExpansionPanel extends CdkAccordionItem /** ID for the associated header element. Used for a11y labelling. */ _headerId = `mat-expansion-panel-header-${uniqueId++}`; - constructor(@Optional() accordion: MatAccordion, + constructor(@Optional() @SkipSelf() accordion: MatAccordion, _changeDetectorRef: ChangeDetectorRef, _uniqueSelectionDispatcher: UniqueSelectionDispatcher, private _viewContainerRef: ViewContainerRef) {