Skip to content

Commit

Permalink
fix(badge): duplicate leftover badge after server-side render (angula…
Browse files Browse the repository at this point in the history
…r#15417)

Fixes badges being left over if the current page was rendered via Angular Universal. This can cause issues, because the ids on the badges can clash.
  • Loading branch information
crisbeto authored and josephperrott committed Mar 8, 2019
1 parent ddeeb18 commit c5a32c2
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 5 deletions.
48 changes: 44 additions & 4 deletions src/lib/badge/badge.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ describe('MatBadge', () => {
let badgeDebugElement: DebugElement;

beforeEach(fakeAsync(() => {
TestBed.configureTestingModule({
imports: [MatBadgeModule],
declarations: [BadgeTestApp],
}).compileComponents();
TestBed
.configureTestingModule({
imports: [MatBadgeModule],
declarations: [BadgeTestApp, PreExistingBadge, NestedBadge],
})
.compileComponents();

fixture = TestBed.createComponent(BadgeTestApp);
testComponent = fixture.debugElement.componentInstance;
Expand Down Expand Up @@ -184,6 +186,20 @@ describe('MatBadge', () => {
expect(badgeContent.hasAttribute('aria-label')).toBe(false);
});

it('should clear any pre-existing badges', () => {
const preExistingFixture = TestBed.createComponent(PreExistingBadge);
preExistingFixture.detectChanges();

expect(preExistingFixture.nativeElement.querySelectorAll('.mat-badge-content').length).toBe(1);
});

it('should not clear badge content from child elements', () => {
const preExistingFixture = TestBed.createComponent(NestedBadge);
preExistingFixture.detectChanges();

expect(preExistingFixture.nativeElement.querySelectorAll('.mat-badge-content').length).toBe(2);
});

});

/** Test component that contains a MatBadge. */
Expand Down Expand Up @@ -214,3 +230,27 @@ class BadgeTestApp {
badgeDescription: string;
badgeDisabled = false;
}


@Component({
template: `
<span matBadge="Hello">
home
<div class="mat-badge-content">Pre-existing badge</div>
</span>
`
})
class PreExistingBadge {
}


@Component({
template: `
<span matBadge="Hello">
home
<span matBadge="Hi">Something</span>
</span>
`
})
class NestedBadge {
}
19 changes: 18 additions & 1 deletion src/lib/badge/badge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,12 @@ export class MatBadge extends _MatBadgeMixinBase implements OnDestroy, CanDisabl
const rootNode = this._renderer || this._document;
const badgeElement = rootNode.createElement('span');
const activeClass = 'mat-badge-active';
const contentClass = 'mat-badge-content';

// Clear any existing badges which may have persisted from a server-side render.
this._clearExistingBadges(contentClass);
badgeElement.setAttribute('id', `mat-badge-content-${this._id}`);
badgeElement.classList.add('mat-badge-content');
badgeElement.classList.add(contentClass);
badgeElement.textContent = this.content;

if (this._animationMode === 'NoopAnimations') {
Expand Down Expand Up @@ -232,4 +235,18 @@ export class MatBadge extends _MatBadgeMixinBase implements OnDestroy, CanDisabl
}
}

/** Clears any existing badges that might be left over from server-side rendering. */
private _clearExistingBadges(cssClass: string) {
const element = this._elementRef.nativeElement;
let childCount = element.children.length;

// Use a reverse while, because we'll be removing elements from the list as we're iterating.
while (childCount--) {
const currentChild = element.children[childCount];

if (currentChild.classList.contains(cssClass)) {
element.removeChild(currentChild);
}
}
}
}

0 comments on commit c5a32c2

Please sign in to comment.