Skip to content

Commit

Permalink
fix(attributes): Reassign attributes if new dynamic component was set
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex Malkevich committed Apr 23, 2018
1 parent ad0117c commit 48bacb4
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 6 deletions.
22 changes: 22 additions & 0 deletions src/dynamic/dynamic-attributes.directive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -17,6 +18,9 @@ import { DynamicComponent } from './dynamic.component';
const getInjectedComponentFrom = getByPredicate<InjectedComponent>(
By.directive(InjectedComponent),
);
const getAnotherInjectedComponentFrom = getByPredicate<AnotherInjectedComponent>(
By.directive(AnotherInjectedComponent),
);

describe('DynamicAttributesDirective', () => {
describe('with `ngComponentOutlet`', () => {
Expand Down Expand Up @@ -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', () => {
Expand Down
55 changes: 49 additions & 6 deletions src/dynamic/dynamic-attributes.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
KeyValueDiffers,
Optional,
Renderer2,
Type,
} from '@angular/core';

import { COMPONENT_INJECTOR, ComponentInjector } from './component-injector';
Expand All @@ -18,6 +19,11 @@ export interface AttributesMap {
[key: string]: string;
}

interface AttributeActions {
set: AttributesMap;
remove: string[];
}

@Directive({
selector: '[ndcDynamicAttributes],[ngComponentOutletNdcDynamicAttributes]',
exportAs: 'ndcDynamicAttributes',
Expand All @@ -31,9 +37,13 @@ export class DynamicAttributesDirective implements DoCheck {
this.componentInjectorType,
null,
);
private _lastCompType: Type<any>;
private _lastAttrActions: AttributeActions;

private get _attributes() {
return this.ndcDynamicAttributes || this.ngComponentOutletNdcDynamicAttributes;
return (
this.ndcDynamicAttributes || this.ngComponentOutletNdcDynamicAttributes
);
}

private get _compInjector() {
Expand All @@ -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,
Expand All @@ -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);
}
}

Expand All @@ -71,9 +97,26 @@ export class DynamicAttributesDirective implements DoCheck {
this.renderer.removeAttribute(this._nativeElement, name, namespace);
}

private _updateAttributes(changes: KeyValueChanges<string, string>) {
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<string, string>,
): 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;
}
}

0 comments on commit 48bacb4

Please sign in to comment.