Skip to content

Commit

Permalink
Merge pull request #505 from gund/feat-standalone-api
Browse files Browse the repository at this point in the history
[Feat] Add standalone API support
  • Loading branch information
gund authored Mar 15, 2023
2 parents 268e464 + a93bbb5 commit 4471a05
Show file tree
Hide file tree
Showing 19 changed files with 226 additions and 75 deletions.
156 changes: 156 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,33 @@ class MyComponent {
}
```

#### Standalone API

**Since vv10.7.0**

You may use `<ndc-dynamic>` as a standalone component:

```ts
import { DynamicComponent } from 'ng-dynamic-component';

@Component({
selector: 'my-component',
template: ` <ndc-dynamic [ndcDynamicComponent]="component"></ndc-dynamic> `,
imports: [DynamicComponent],
standalone: true,
})
class MyComponent {
component = Math.random() > 0.5 ? MyDynamicComponent1 : MyDynamicComponent2;
}
```

_NOTE:_ Hovewer you should be aware that this will only import `<ndc-dynamic>`
into your component and nothing else so things like dynamic inputs/outputs
will not work and you will have to import them separately (see their respective sections).

If you still need to use both `<ndc-dynamic>` and dynamic inputs/outputs it is recommended
to keep using `DynamicModule` API.

### NgComponentOutlet

You can also use [`NgComponentOutlet`](https://angular.io/api/common/NgComponentOutlet)
Expand Down Expand Up @@ -121,6 +148,53 @@ class MyComponent {
}
```

#### Standalone API

**Since vv10.7.0**

You may use dynamic inputs/outputs with `*ngComponentOutlet` as a standalone API:

```ts
import { ComponentOutletInjectorModule } from 'ng-dynamic-component';

@Component({
selector: 'my-component',
template: `<ng-container *ngComponentOutlet="component;
ndcDynamicInputs: inputs;
ndcDynamicOutputs: outputs"
></ng-container>`
imports: [ComponentOutletInjectorModule],
standalone: true,
})
class MyComponent {
component = MyDynamicComponent1;
inputs = {...};
outputs = {...};
}
```

If you want to use standard dynamic inputs/outputs with `ngComponentOutlet` as a standalone API
you need to add the `DynamicIoDirective` to your imports:

```ts
import { DynamicIoDirective, ComponentOutletInjectorModule } from 'ng-dynamic-component';

@Component({
selector: 'my-component',
template: `<ng-container *ngComponentOutlet="component;
ndcDynamicInputs: inputs;
ndcDynamicOutputs: outputs"
></ng-container>`
imports: [DynamicIoDirective, ComponentOutletInjectorModule],
standalone: true,
})
class MyComponent {
component = MyDynamicComponent1;
inputs = {...};
outputs = {...};
}
```

### Inputs and Outputs

You can pass `inputs` and `outputs` to your dynamic components:
Expand Down Expand Up @@ -166,6 +240,34 @@ class MyDynamicComponent1 {
Here you can update your inputs (ex. `inputs.hello = 'WORLD'`) and they will trigger standard Angular's life-cycle hooks
(of course you should consider which change detection strategy you are using).

#### Standalone API

**Since vv10.7.0**

You can use standalone API to pass dynamic inputs/outputs
using `DynamicIoDirective` with `DynamicComponent` or `ngComponentOutlet`:

```ts
import { DynamicIoDirective, DynamicComponent } from 'ng-dynamic-component';

@Component({
selector: 'my-component',
template: `
<ndc-dynamic
[ndcDynamicComponent]="component"
[ndcDynamicInputs]="inputs"
[ndcDynamicOutputs]="outputs"
></ndc-dynamic>
`,
imports: [DynamicIoDirective, DynamicComponent]
})
class MyComponent {
component = MyDynamicComponent1;
inputs = {...};
outputs = {...};
}
```

#### Output template variables

**Since v6.1.0**
Expand Down Expand Up @@ -317,6 +419,32 @@ class MyComponent {
}
```

#### Standalone API

**Since vv10.7.0**

You can use standalone API to pass dynamic inputs/outputs
using `DynamicAttributesDirective` with `DynamicComponent` or `ngComponentOutlet`:

```ts
import { DynamicAttributesDirective, DynamicComponent } from 'ng-dynamic-component';

@Component({
selector: 'my-component',
template: `
<ndc-dynamic
[ndcDynamicComponent]="component"
[ndcDynamicAttributes]="attrs"
></ndc-dynamic>
`,
imports: [DynamicAttributesDirective, DynamicComponent]
})
class MyComponent {
component = MyDynamicComponent1;
attrs: AttributesMap = {...};
}
```

### Directives (experimental)

**Since v3.1.0** you can now declaratively set directives, via `ndcDynamicDirectives`.
Expand Down Expand Up @@ -412,6 +540,32 @@ class MyComponent {
}
```

#### Standalone API

**Since vv10.7.0**

You can use standalone API to pass dynamic inputs/outputs
using `DynamicDirectivesDirective` with `DynamicComponent` or `ngComponentOutlet`:

```ts
import { DynamicDirectivesDirective, DynamicComponent } from 'ng-dynamic-component';

@Component({
selector: 'my-component',
template: `
<ng-container
[ngComponentOutlet]="component"
[ndcDynamicDirectives]="dirs"
></ng-container>
`,
imports: [DynamicDirectivesDirective, DynamicComponent]
})
class MyComponent {
component = MyDynamicComponent1;
dirs = [...];
}
```

### Extra

You can have more advanced stuff over your dynamically rendered components like setting custom injector (`[ndcDynamicInjector]`)
Expand All @@ -421,6 +575,8 @@ 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
27 changes: 13 additions & 14 deletions goldens/ng-dynamic-component/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { ElementRef } from '@angular/core';
import { EnvironmentInjector } from '@angular/core';
import { EventEmitter } from '@angular/core';
import * as i0 from '@angular/core';
import * as i3 from '@angular/common';
import { InjectionToken } from '@angular/core';
import { Injector } from '@angular/core';
import { IterableDiffers } from '@angular/core';
Expand Down Expand Up @@ -43,7 +42,7 @@ export class ComponentOutletInjectorDirective implements DynamicComponentInjecto
// (undocumented)
get componentRef(): ComponentRef<unknown>;
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<ComponentOutletInjectorDirective, "[ngComponentOutlet]", ["ndcComponentOutletInjector"], {}, {}, never, never, false>;
static ɵdir: i0.ɵɵDirectiveDeclaration<ComponentOutletInjectorDirective, "[ngComponentOutlet]", ["ndcComponentOutletInjector"], {}, {}, never, never, true>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<ComponentOutletInjectorDirective, [{ host: true; }]>;
}
Expand All @@ -58,7 +57,7 @@ export class ComponentOutletInjectorModule {
// Warning: (ae-forgotten-export) The symbol "i2" needs to be exported by the entry point public-api.d.ts
//
// (undocumented)
static ɵmod: i0.ɵɵNgModuleDeclaration<ComponentOutletInjectorModule, [typeof i1.ComponentOutletInjectorDirective, typeof i2.ComponentOutletIoDirective], [typeof i3.CommonModule], [typeof i1.ComponentOutletInjectorDirective, typeof i2.ComponentOutletIoDirective]>;
static ɵmod: i0.ɵɵNgModuleDeclaration<ComponentOutletInjectorModule, never, [typeof i1.ComponentOutletInjectorDirective, typeof i2.ComponentOutletIoDirective], [typeof i1.ComponentOutletInjectorDirective, typeof i2.ComponentOutletIoDirective]>;
}

// @public (undocumented)
Expand All @@ -71,7 +70,7 @@ export class ComponentOutletIoDirective implements DoCheck {
// (undocumented)
ngDoCheck(): void;
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<ComponentOutletIoDirective, "[ngComponentOutletNdcDynamicInputs],[ngComponentOutletNdcDynamicOutputs]", ["ndcDynamicIo"], { "ngComponentOutletNdcDynamicInputs": "ngComponentOutletNdcDynamicInputs"; "ngComponentOutletNdcDynamicOutputs": "ngComponentOutletNdcDynamicOutputs"; }, {}, never, never, false>;
static ɵdir: i0.ɵɵDirectiveDeclaration<ComponentOutletIoDirective, "[ngComponentOutletNdcDynamicInputs],[ngComponentOutletNdcDynamicOutputs]", ["ndcDynamicIo"], { "ngComponentOutletNdcDynamicInputs": "ngComponentOutletNdcDynamicInputs"; "ngComponentOutletNdcDynamicOutputs": "ngComponentOutletNdcDynamicOutputs"; }, {}, never, never, true>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<ComponentOutletIoDirective, never>;
}
Expand Down Expand Up @@ -113,7 +112,7 @@ export class DynamicAttributesDirective implements DoCheck {
// (undocumented)
setAttribute(name: string, value: string, namespace?: string): void;
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<DynamicAttributesDirective, "[ndcDynamicAttributes],[ngComponentOutletNdcDynamicAttributes]", ["ndcDynamicAttributes"], { "ndcDynamicAttributes": "ndcDynamicAttributes"; "ngComponentOutletNdcDynamicAttributes": "ngComponentOutletNdcDynamicAttributes"; }, {}, never, never, false>;
static ɵdir: i0.ɵɵDirectiveDeclaration<DynamicAttributesDirective, "[ndcDynamicAttributes],[ngComponentOutletNdcDynamicAttributes]", ["ndcDynamicAttributes"], { "ndcDynamicAttributes": "ndcDynamicAttributes"; "ngComponentOutletNdcDynamicAttributes": "ngComponentOutletNdcDynamicAttributes"; }, {}, never, never, true>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<DynamicAttributesDirective, [null, null, { optional: true; }]>;
}
Expand All @@ -125,10 +124,10 @@ export class DynamicAttributesModule {
// (undocumented)
static ɵinj: i0.ɵɵInjectorDeclaration<DynamicAttributesModule>;
// Warning: (ae-forgotten-export) The symbol "i1_4" needs to be exported by the entry point public-api.d.ts
// Warning: (ae-forgotten-export) The symbol "i3_3" needs to be exported by the entry point public-api.d.ts
// Warning: (ae-forgotten-export) The symbol "i2_2" needs to be exported by the entry point public-api.d.ts
//
// (undocumented)
static ɵmod: i0.ɵɵNgModuleDeclaration<DynamicAttributesModule, [typeof i1_4.DynamicAttributesDirective], [typeof i3.CommonModule], [typeof i1_4.DynamicAttributesDirective, typeof i3_3.ComponentOutletInjectorModule]>;
static ɵmod: i0.ɵɵNgModuleDeclaration<DynamicAttributesModule, never, [typeof i1_4.DynamicAttributesDirective], [typeof i1_4.DynamicAttributesDirective, typeof i2_2.ComponentOutletInjectorModule]>;
}

// @public (undocumented)
Expand All @@ -155,7 +154,7 @@ export class DynamicComponent<C = unknown> implements OnChanges, DynamicComponen
// (undocumented)
ngOnChanges(changes: SimpleChanges): void;
// (undocumented)
static ɵcmp: i0.ɵɵComponentDeclaration<DynamicComponent<any>, "ndc-dynamic", never, { "ndcDynamicComponent": "ndcDynamicComponent"; "ndcDynamicInjector": "ndcDynamicInjector"; "ndcDynamicProviders": "ndcDynamicProviders"; "ndcDynamicContent": "ndcDynamicContent"; "ndcDynamicNgModuleRef": "ndcDynamicNgModuleRef"; "ndcDynamicEnvironmentInjector": "ndcDynamicEnvironmentInjector"; }, { "ndcDynamicCreated": "ndcDynamicCreated"; }, never, never, false>;
static ɵcmp: i0.ɵɵComponentDeclaration<DynamicComponent<any>, "ndc-dynamic", never, { "ndcDynamicComponent": "ndcDynamicComponent"; "ndcDynamicInjector": "ndcDynamicInjector"; "ndcDynamicProviders": "ndcDynamicProviders"; "ndcDynamicContent": "ndcDynamicContent"; "ndcDynamicNgModuleRef": "ndcDynamicNgModuleRef"; "ndcDynamicEnvironmentInjector": "ndcDynamicEnvironmentInjector"; }, { "ndcDynamicCreated": "ndcDynamicCreated"; }, never, never, true>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<DynamicComponent<any>, never>;
}
Expand Down Expand Up @@ -196,7 +195,7 @@ export class DynamicDirectivesDirective implements OnDestroy, DoCheck {
// (undocumented)
ngOnDestroy(): void;
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<DynamicDirectivesDirective, "[ndcDynamicDirectives],[ngComponentOutletNdcDynamicDirectives]", never, { "ndcDynamicDirectives": "ndcDynamicDirectives"; "ngComponentOutletNdcDynamicDirectives": "ngComponentOutletNdcDynamicDirectives"; }, { "ndcDynamicDirectivesCreated": "ndcDynamicDirectivesCreated"; }, never, never, false>;
static ɵdir: i0.ɵɵDirectiveDeclaration<DynamicDirectivesDirective, "[ndcDynamicDirectives],[ngComponentOutletNdcDynamicDirectives]", never, { "ndcDynamicDirectives": "ndcDynamicDirectives"; "ngComponentOutletNdcDynamicDirectives": "ngComponentOutletNdcDynamicDirectives"; }, { "ndcDynamicDirectivesCreated": "ndcDynamicDirectivesCreated"; }, never, never, true>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<DynamicDirectivesDirective, [null, null, null, null, { optional: true; }]>;
}
Expand All @@ -210,7 +209,7 @@ export class DynamicDirectivesModule {
// Warning: (ae-forgotten-export) The symbol "i1_5" needs to be exported by the entry point public-api.d.ts
//
// (undocumented)
static ɵmod: i0.ɵɵNgModuleDeclaration<DynamicDirectivesModule, [typeof i1_5.DynamicDirectivesDirective], [typeof i3.CommonModule], [typeof i1_5.DynamicDirectivesDirective, typeof i3_3.ComponentOutletInjectorModule]>;
static ɵmod: i0.ɵɵNgModuleDeclaration<DynamicDirectivesModule, never, [typeof i1_5.DynamicDirectivesDirective], [typeof i1_5.DynamicDirectivesDirective, typeof i2_2.ComponentOutletInjectorModule]>;
}

// @public (undocumented)
Expand All @@ -223,7 +222,7 @@ export class DynamicIoDirective implements DoCheck {
// (undocumented)
ngDoCheck(): void;
// (undocumented)
static ɵdir: i0.ɵɵDirectiveDeclaration<DynamicIoDirective, "[ndcDynamicInputs],[ndcDynamicOutputs]", ["ndcDynamicIo"], { "ndcDynamicInputs": "ndcDynamicInputs"; "ndcDynamicOutputs": "ndcDynamicOutputs"; }, {}, never, never, false>;
static ɵdir: i0.ɵɵDirectiveDeclaration<DynamicIoDirective, "[ndcDynamicInputs],[ndcDynamicOutputs]", ["ndcDynamicIo"], { "ndcDynamicInputs": "ndcDynamicInputs"; "ndcDynamicOutputs": "ndcDynamicOutputs"; }, {}, never, never, true>;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<DynamicIoDirective, never>;
}
Expand All @@ -237,7 +236,7 @@ export class DynamicIoModule {
// Warning: (ae-forgotten-export) The symbol "i1_3" needs to be exported by the entry point public-api.d.ts
//
// (undocumented)
static ɵmod: i0.ɵɵNgModuleDeclaration<DynamicIoModule, [typeof i1_3.DynamicIoDirective], [typeof i3.CommonModule], [typeof i1_3.DynamicIoDirective, typeof i3_3.ComponentOutletInjectorModule]>;
static ɵmod: i0.ɵɵNgModuleDeclaration<DynamicIoModule, never, [typeof i1_3.DynamicIoDirective], [typeof i1_3.DynamicIoDirective, typeof i2_2.ComponentOutletInjectorModule]>;
}

// @public (undocumented)
Expand All @@ -247,10 +246,10 @@ export class DynamicModule {
// (undocumented)
static ɵinj: i0.ɵɵInjectorDeclaration<DynamicModule>;
// Warning: (ae-forgotten-export) The symbol "i1_2" needs to be exported by the entry point public-api.d.ts
// Warning: (ae-forgotten-export) The symbol "i3_2" needs to be exported by the entry point public-api.d.ts
// Warning: (ae-forgotten-export) The symbol "i2_3" needs to be exported by the entry point public-api.d.ts
//
// (undocumented)
static ɵmod: i0.ɵɵNgModuleDeclaration<DynamicModule, [typeof i1_2.DynamicComponent], [typeof i3.CommonModule, typeof i3_2.DynamicIoModule], [typeof i1_2.DynamicComponent, typeof i3_2.DynamicIoModule]>;
static ɵmod: i0.ɵɵNgModuleDeclaration<DynamicModule, never, [typeof i1_2.DynamicIoModule, typeof i2_3.DynamicComponent], [typeof i1_2.DynamicIoModule, typeof i2_3.DynamicComponent]>;
}

// @public @deprecated (undocumented)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ComponentOutletInjectorDirective } from './component-outlet-injector.di

describe('ComponentOutletInjectorDirective', () => {
@Component({ selector: 'dynamic', template: '' })
class DynamicComponent {}
class Dynamic1Component {}

@Component({
selector: 'host',
Expand All @@ -18,9 +18,10 @@ describe('ComponentOutletInjectorDirective', () => {
}

const testSetup = new TestSetup(HostComponent, {
props: { component: DynamicComponent },
props: { component: Dynamic1Component },
ngModule: {
declarations: [ComponentOutletInjectorDirective, DynamicComponent],
imports: [ComponentOutletInjectorDirective],
declarations: [Dynamic1Component],
},
});

Expand All @@ -45,6 +46,6 @@ describe('ComponentOutletInjectorDirective', () => {

const directive = fixture.getHost().directive;

expect(directive?.componentRef.instance).toBeInstanceOf(DynamicComponent);
expect(directive?.componentRef.instance).toBeInstanceOf(Dynamic1Component);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
// eslint-disable-next-line @angular-eslint/directive-selector
selector: '[ngComponentOutlet]',
exportAs: 'ndcComponentOutletInjector',
standalone: true,
providers: [
{
provide: DynamicComponentInjectorToken,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';

import { ComponentOutletInjectorDirective } from './component-outlet-injector.directive';
Expand All @@ -8,8 +7,7 @@ import { ComponentOutletIoDirective } from './component-outlet-io.directive';
* @public
*/
@NgModule({
imports: [CommonModule],
imports: [ComponentOutletInjectorDirective, ComponentOutletIoDirective],
exports: [ComponentOutletInjectorDirective, ComponentOutletIoDirective],
declarations: [ComponentOutletInjectorDirective, ComponentOutletIoDirective],
})
export class ComponentOutletInjectorModule {}
Loading

0 comments on commit 4471a05

Please sign in to comment.