diff --git a/src/dynamic/dynamic-attributes.directive.spec.ts b/src/dynamic/dynamic-attributes.directive.spec.ts index 3f4200824..439c274d7 100644 --- a/src/dynamic/dynamic-attributes.directive.spec.ts +++ b/src/dynamic/dynamic-attributes.directive.spec.ts @@ -4,6 +4,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { + AnotherInjectedComponent, InjectedComponent, TestComponent as TestComponentBase, TestModule, @@ -17,6 +18,9 @@ import { DynamicComponent } from './dynamic.component'; const getInjectedComponentFrom = getByPredicate( By.directive(InjectedComponent), ); +const getAnotherInjectedComponentFrom = getByPredicate( + By.directive(AnotherInjectedComponent), +); describe('DynamicAttributesDirective', () => { describe('with `ngComponentOutlet`', () => { @@ -182,6 +186,24 @@ describe('DynamicAttributesDirective', () => { expect(injectedElem.attributes).toEqual(attrs); }); + + it('should reassign attrs when new component injected', () => { + const attrs = { + 'attr-one': 'val-1', + attrTwo: 'val-two', + }; + fixture.componentInstance.attrs = attrs; + + fixture.detectChanges(); + + fixture.componentInstance.comp = AnotherInjectedComponent; + + fixture.detectChanges(); + + const injectedElem = getAnotherInjectedComponentFrom(fixture).componentElem; + + expect(injectedElem.attributes).toMatchObject(attrs); + }); }); describe('with `ngComponentOutlet` * syntax', () => { diff --git a/src/dynamic/dynamic-attributes.directive.ts b/src/dynamic/dynamic-attributes.directive.ts index 884e582dc..af621a471 100644 --- a/src/dynamic/dynamic-attributes.directive.ts +++ b/src/dynamic/dynamic-attributes.directive.ts @@ -9,6 +9,7 @@ import { KeyValueDiffers, Optional, Renderer2, + Type, } from '@angular/core'; import { COMPONENT_INJECTOR, ComponentInjector } from './component-injector'; @@ -18,6 +19,11 @@ export interface AttributesMap { [key: string]: string; } +interface AttributeActions { + set: AttributesMap; + remove: string[]; +} + @Directive({ selector: '[ndcDynamicAttributes],[ngComponentOutletNdcDynamicAttributes]', exportAs: 'ndcDynamicAttributes', @@ -31,9 +37,13 @@ export class DynamicAttributesDirective implements DoCheck { this.componentInjectorType, null, ); + private _lastCompType: Type; + private _lastAttrActions: AttributeActions; private get _attributes() { - return this.ndcDynamicAttributes || this.ngComponentOutletNdcDynamicAttributes; + return ( + this.ndcDynamicAttributes || this.ngComponentOutletNdcDynamicAttributes + ); } private get _compInjector() { @@ -44,6 +54,18 @@ export class DynamicAttributesDirective implements DoCheck { return this._compInjector.componentRef.location.nativeElement; } + private get _compType() { + return this._compInjector.componentRef.componentType; + } + + private get _isCompChanged() { + if (this._lastCompType !== this._compType) { + this._lastCompType = this._compType; + return true; + } + return false; + } + constructor( private renderer: Renderer2, private differs: KeyValueDiffers, @@ -56,10 +78,14 @@ export class DynamicAttributesDirective implements DoCheck { ) {} ngDoCheck(): void { + const isCompChanged = this._isCompChanged; const changes = this._attrsDiffer.diff(this._attributes); if (changes) { - this._updateAttributes(changes); + this._lastAttrActions = this._changesToAttrActions(changes); + this._updateAttributes(this._lastAttrActions); + } else if (isCompChanged && this._lastAttrActions) { + this._updateAttributes(this._lastAttrActions); } } @@ -71,9 +97,26 @@ export class DynamicAttributesDirective implements DoCheck { this.renderer.removeAttribute(this._nativeElement, name, namespace); } - private _updateAttributes(changes: KeyValueChanges) { - changes.forEachAddedItem(r => this.setAttribute(r.key, r.currentValue)); - changes.forEachChangedItem(r => this.setAttribute(r.key, r.currentValue)); - changes.forEachRemovedItem(r => this.removeAttribute(r.key)); + private _updateAttributes(actions: AttributeActions) { + Object.keys(actions.set).forEach(key => + this.setAttribute(key, actions.set[key]), + ); + + actions.remove.forEach(key => this.removeAttribute(key)); + } + + private _changesToAttrActions( + changes: KeyValueChanges, + ): AttributeActions { + const attrActions: AttributeActions = { + set: {}, + remove: [], + }; + + changes.forEachAddedItem(r => (attrActions.set[r.key] = r.currentValue)); + changes.forEachChangedItem(r => (attrActions.set[r.key] = r.currentValue)); + changes.forEachRemovedItem(r => attrActions.remove.push(r.key)); + + return attrActions; } }