Skip to content

Commit 8280a76

Browse files
crisbetojosephperrott
authored andcommitted
fix(icon): clearing all content when inserting a new SVG (#11956)
1 parent fc15fa2 commit 8280a76

File tree

2 files changed

+35
-4
lines changed

2 files changed

+35
-4
lines changed

src/lib/icon/icon.spec.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ describe('MatIcon', () => {
5151
IconWithAriaHiddenFalse,
5252
IconWithBindingAndNgIf,
5353
InlineIcon,
54+
SvgIconWithUserContent,
5455
]
5556
});
5657

@@ -397,6 +398,26 @@ describe('MatIcon', () => {
397398

398399
expect(icon.querySelector('svg')).toBeFalsy();
399400
});
401+
402+
it('should keep non-SVG user content inside the icon element', fakeAsync(() => {
403+
iconRegistry.addSvgIcon('fido', trustUrl('dog.svg'));
404+
405+
const fixture = TestBed.createComponent(SvgIconWithUserContent);
406+
const testComponent = fixture.componentInstance;
407+
const iconElement = fixture.debugElement.nativeElement.querySelector('mat-icon');
408+
409+
testComponent.iconName = 'fido';
410+
fixture.detectChanges();
411+
http.expectOne('dog.svg').flush(FAKE_SVGS.dog);
412+
413+
const userDiv = iconElement.querySelector('div');
414+
415+
expect(userDiv).toBeTruthy();
416+
expect(iconElement.textContent.trim()).toContain('Hello');
417+
418+
tick();
419+
}));
420+
400421
});
401422

402423
describe('Icons from HTML string', () => {
@@ -706,3 +727,8 @@ class IconWithBindingAndNgIf {
706727
class InlineIcon {
707728
inline = false;
708729
}
730+
731+
@Component({template: `<mat-icon [svgIcon]="iconName"><div>Hello</div></mat-icon>`})
732+
class SvgIconWithUserContent {
733+
iconName: string | undefined = '';
734+
}

src/lib/icon/icon.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ export const _MatIconMixinBase = mixinColor(MatIconBase);
7474
changeDetection: ChangeDetectionStrategy.OnPush,
7575
})
7676
export class MatIcon extends _MatIconMixinBase implements OnChanges, OnInit, CanColor {
77-
7877
/**
7978
* Whether the icon should be inlined, automatically sizing the icon to match the font size of
8079
* the element the icon is contained in.
@@ -199,10 +198,16 @@ export class MatIcon extends _MatIconMixinBase implements OnChanges, OnInit, Can
199198
const layoutElement: HTMLElement = this._elementRef.nativeElement;
200199
const childCount = layoutElement.childNodes.length;
201200

202-
// Remove existing child nodes and add the new SVG element. Note that we can't
203-
// use innerHTML, because IE will throw if the element has a data binding.
201+
// Remove existing non-element child nodes and SVGs, and add the new SVG element. Note that
202+
// we can't use innerHTML, because IE will throw if the element has a data binding.
204203
for (let i = 0; i < childCount; i++) {
205-
layoutElement.removeChild(layoutElement.childNodes[i]);
204+
const child = layoutElement.childNodes[i];
205+
206+
// 1 corresponds to Node.ELEMENT_NODE. We remove all non-element nodes in order to get rid
207+
// of any loose text nodes, as well as any SVG elements in order to remove any old icons.
208+
if (child.nodeType !== 1 || child.nodeName.toLowerCase() === 'svg') {
209+
layoutElement.removeChild(child);
210+
}
206211
}
207212
}
208213

0 commit comments

Comments
 (0)