Skip to content

Commit

Permalink
feat: add NgModuleRef and EnvironmentInjector inputs to `ndc-dyna…
Browse files Browse the repository at this point in the history
…mic` component

* Add `ndcDynamicNgModuleRef` input to `ndc-dynamic` component
* Add `ndcDynamicEnvironmentInjector` input to `ndc-dynamic` component

Co-authored-by: Mihail Goloborodov <Mihail.Goloborodov@nexign-systems.com>
Co-authored-by: Alex Malkevich <malkevich.alex@gmail.com>
  • Loading branch information
3 people committed Jan 25, 2023
1 parent 5df76ff commit bfaceb3
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 0 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,9 @@ You can have more advanced stuff over your dynamically rendered components like
or providing additional/overriding providers (`[ndcDynamicProviders]`) or both simultaneously
or projecting nodes (`[ndcDynamicContent]`).

**Since v10.6.0**: You can provide custom NgModuleRef (`[ndcDynamicNgModuleRef]`)
or EnvironmentInjector (`[ndcDynamicEnvironmentInjector]`) for your dynamic component.

NOTE: In practice functionality of this library is split in two pieces:

- one - component (`ndc-dynamic`) that is responsible for instantiating and rendering of dynamic components;
Expand Down
108 changes: 108 additions & 0 deletions projects/ng-dynamic-component/src/lib/dynamic.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
/* eslint-disable @angular-eslint/component-selector */
import {
Component,
createNgModule,
EnvironmentInjector,
InjectionToken,
Injector,
NgModule,
NgModuleRef,
StaticProvider,
Type,
} from '@angular/core';
Expand All @@ -21,6 +25,8 @@ describe('DynamicComponent', () => {
[ndcDynamicInjector]="injector"
[ndcDynamicProviders]="providers"
[ndcDynamicContent]="content"
[ndcDynamicNgModuleRef]="ngModuleRef"
[ndcDynamicEnvironmentInjector]="environmentInjector"
(ndcDynamicCreated)="createdComponent($event)"
></ndc-dynamic>`,
})
Expand All @@ -29,6 +35,8 @@ describe('DynamicComponent', () => {
injector?: Injector;
providers?: StaticProvider[];
content?: Node[][];
ngModuleRef?: NgModuleRef<unknown>;
environmentInjector?: EnvironmentInjector | NgModuleRef<unknown>;
createdComponent = jest.fn();
}

Expand Down Expand Up @@ -258,4 +266,104 @@ describe('DynamicComponent', () => {
);
});
});

describe('@Input(ndcDynamicNgModuleRef)', () => {
const testToken = new InjectionToken<any>('TEST_TOKEN');
const testTokenValue = {};
let ngModuleRef: NgModuleRef<any>;
let parentInjector: Injector;

@NgModule({
providers: [{ provide: testToken, useValue: testTokenValue }],
})
class CustomNgModule {}

it('should use input if provided for ngModuleRef', async () => {
const fixture = await testSetup.redner();

parentInjector = TestBed.inject(NgModuleRef).injector;
ngModuleRef = createNgModule(CustomNgModule, parentInjector);
fixture.setHostProps({ ngModuleRef });

expect(fixture.getInjectedInjector().get(testToken)).toBe(testTokenValue);
});

it('should change component if input updated', async () => {
const fixture = await testSetup.redner();

parentInjector = TestBed.inject(NgModuleRef).injector;
ngModuleRef = createNgModule(CustomNgModule, parentInjector);
fixture.setHostProps({ ngModuleRef });

const injectedComp = fixture.getInjectedComponent();

expect(fixture.getInjectedInjector().get(testToken)).toBe(testTokenValue);

const testTokenValue2 = {};
@NgModule({
providers: [{ provide: testToken, useValue: testTokenValue2 }],
})
class CustomNgModule2 {}
ngModuleRef = createNgModule(CustomNgModule2, parentInjector);

fixture.setHostProps({ ngModuleRef });

expect(fixture.getInjectedInjector().get(testToken)).toBe(
testTokenValue2,
);
expect(fixture.getInjectedComponent()).not.toBe(injectedComp);
});
});

describe('@Input(ndcDynamicEnvironmentInjector)', () => {
const testToken = new InjectionToken<any>('TEST_TOKEN');
const testTokenValue = {};
let ngModuleRef: NgModuleRef<any>;
let environmentInjector: EnvironmentInjector;
let parentInjector: Injector;

@NgModule({
providers: [{ provide: testToken, useValue: testTokenValue }],
})
class CustomNgModule {}

it('should use input if provided for environmentInjector', async () => {
const fixture = await testSetup.redner();

parentInjector = TestBed.inject(NgModuleRef).injector;
ngModuleRef = createNgModule(CustomNgModule, parentInjector);
environmentInjector = ngModuleRef.injector;
fixture.setHostProps({ environmentInjector });

expect(fixture.getInjectedInjector().get(testToken)).toBe(testTokenValue);
});

it('should change component if input updated', async () => {
const fixture = await testSetup.redner();

parentInjector = TestBed.inject(NgModuleRef).injector;
ngModuleRef = createNgModule(CustomNgModule, parentInjector);
environmentInjector = ngModuleRef.injector;
fixture.setHostProps({ environmentInjector });

const injectedComp = fixture.getInjectedComponent();

expect(fixture.getInjectedInjector().get(testToken)).toBe(testTokenValue);

const testTokenValue2 = {};
@NgModule({
providers: [{ provide: testToken, useValue: testTokenValue2 }],
})
class CustomNgModule2 {}
ngModuleRef = createNgModule(CustomNgModule2, parentInjector);
environmentInjector = ngModuleRef.injector;

fixture.setHostProps({ environmentInjector });

expect(fixture.getInjectedInjector().get(testToken)).toBe(
testTokenValue2,
);
expect(fixture.getInjectedComponent()).not.toBe(injectedComp);
});
});
});
10 changes: 10 additions & 0 deletions projects/ng-dynamic-component/src/lib/dynamic.component.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {
Component,
ComponentRef,
EnvironmentInjector,
EventEmitter,
Injector,
Input,
NgModuleRef,
OnChanges,
Output,
SimpleChanges,
Expand Down Expand Up @@ -31,6 +33,8 @@ export class DynamicComponent<C = unknown>
'ndcDynamicInjector',
'ndcDynamicProviders',
'ndcDynamicContent',
'ndcDynamicNgModuleRef',
'ndcDynamicEnvironmentInjector',
];

@Input()
Expand All @@ -41,6 +45,10 @@ export class DynamicComponent<C = unknown>
ndcDynamicProviders?: StaticProvider[] | null;
@Input()
ndcDynamicContent?: Node[][];
@Input()
ndcDynamicNgModuleRef?: NgModuleRef<unknown>;
@Input()
ndcDynamicEnvironmentInjector?: EnvironmentInjector | NgModuleRef<unknown>;

@Output()
ndcDynamicCreated = new EventEmitter<ComponentRef<C>>();
Expand Down Expand Up @@ -68,6 +76,8 @@ export class DynamicComponent<C = unknown>
index: 0,
injector: this._resolveInjector(),
projectableNodes: this.ndcDynamicContent,
ngModuleRef: this.ndcDynamicNgModuleRef,
environmentInjector: this.ndcDynamicEnvironmentInjector,
});
this.ndcDynamicCreated.emit(this.componentRef);
}
Expand Down

0 comments on commit bfaceb3

Please sign in to comment.