Skip to content

Commit 076ccf2

Browse files
committed
refactor(composer): extract decortator configurations components into DI
So they can be extended via DI from third-party modules re orchestratora/rfcs#2
1 parent 91b991b commit 076ccf2

36 files changed

Lines changed: 778 additions & 132 deletions
Lines changed: 16 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,21 @@
1-
<form nz-form [formGroup]="formGroup">
1+
<form nz-form [formGroup]="formGroup" #form="ngForm">
22
<nz-form-item *ngFor="let item of (formConfig | keyvalue)">
3-
<nz-form-label [nzRequired]="item.value.extras.required">
3+
<nz-form-label [nzRequired]="item.value.required">
44
{{ item.key | uppercase }}
55
</nz-form-label>
6-
<nz-form-control [ngSwitch]="item.value.tag">
7-
<input
8-
*ngSwitchCase="'input'"
9-
nz-input
10-
[formControlName]="item.key"
11-
[type]="item.value.type"
12-
[min]="item.value.extras.min"
13-
[max]="item.value.extras.max"
14-
[step]="item.value.extras.step"
15-
/>
16-
<nz-input-number
17-
*ngSwitchCase="'input-number'"
18-
[formControlName]="item.key"
19-
[nzMin]="item.value.extras.min"
20-
[nzMax]="item.value.extras.max"
21-
[nzStep]="item.value.extras.step || 1"
22-
></nz-input-number>
23-
<nz-slider
24-
*ngSwitchCase="'slider'"
25-
[formControlName]="item.key"
26-
[nzMin]="item.value.extras.min"
27-
[nzMax]="item.value.extras.max"
28-
[nzStep]="item.value.extras.step"
29-
></nz-slider>
30-
<nz-select
31-
*ngSwitchCase="'select'"
32-
nzAllowClear
33-
[formControlName]="item.key"
34-
>
35-
<nz-option
36-
*ngFor="let opt of item.value.options"
37-
[nzValue]="opt"
38-
[nzLabel]="opt"
39-
></nz-option>
40-
</nz-select>
41-
<nz-switch
42-
*ngSwitchCase="'switch'"
43-
[formControlName]="item.key"
44-
></nz-switch>
45-
<p *ngSwitchDefault>HUI! NA RUL'</p>
46-
</nz-form-control>
6+
<ng-container
7+
*ngIf="item.value.component; else noCompTpl"
8+
[ngComponentOutlet]="item.value.component"
9+
[ndcDynamicInputs]="{
10+
name: item.key,
11+
parent: form,
12+
extras: item.value.extras
13+
}"
14+
></ng-container>
15+
<ng-template #noCompTpl>
16+
<nz-form-control>
17+
<textarea rows="4" nz-input [formControlName]="item.key"></textarea>
18+
</nz-form-control>
19+
</ng-template>
4720
</nz-form-item>
4821
</form>

libs/composer/src/lib/composer-configurator/composer-configurator.component.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,8 @@ import {
1414
import { Observable, Subject } from 'rxjs';
1515
import { switchAll } from 'rxjs/operators';
1616

17-
import {
18-
ComposerConfiguratorService,
19-
ControlConfigObject,
20-
} from './composer-configurator.service';
17+
import { ControlConfigObject } from '../decorator-config';
18+
import { ComposerConfiguratorService } from './composer-configurator.service';
2119

2220
/**
2321
* @internal

libs/composer/src/lib/composer-configurator/composer-configurator.module.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
import { CommonModule } from '@angular/common';
22
import { NgModule } from '@angular/core';
33
import { ReactiveFormsModule } from '@angular/forms';
4+
import { DynamicModule } from 'ng-dynamic-component';
45
import { NzFormModule } from 'ng-zorro-antd/form';
56
import { NzInputModule } from 'ng-zorro-antd/input';
6-
import { NzInputNumberModule } from 'ng-zorro-antd/input-number';
7-
import { NzSelectModule } from 'ng-zorro-antd/select';
8-
import { NzSliderModule } from 'ng-zorro-antd/slider';
9-
import { NzSwitchModule } from 'ng-zorro-antd/switch';
107

118
import { ComposerConfiguratorComponent } from './composer-configurator.component';
129

@@ -19,10 +16,7 @@ import { ComposerConfiguratorComponent } from './composer-configurator.component
1916
ReactiveFormsModule,
2017
NzFormModule,
2118
NzInputModule,
22-
NzInputNumberModule,
23-
NzSelectModule,
24-
NzSliderModule,
25-
NzSwitchModule,
19+
DynamicModule,
2620
],
2721
exports: [ComposerConfiguratorComponent],
2822
declarations: [ComposerConfiguratorComponent],
Lines changed: 57 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,46 @@
1-
import { Injectable } from '@angular/core';
2-
import {
3-
FormBuilder,
4-
FormGroup,
5-
ValidatorFn,
6-
Validators,
7-
} from '@angular/forms';
1+
import { Injectable, Injector } from '@angular/core';
2+
import { FormBuilder, FormGroup } from '@angular/forms';
83
import {
94
ComponentLocatorService,
105
ConfigurationMeta,
116
ConfigurationService,
12-
OptionAllowedValues,
13-
OptionInteger,
14-
OptionRange,
15-
OptionRequired,
167
OrchestratorDynamicComponentType,
178
} from '@orchestrator/core';
189

10+
import {
11+
ControlConfig,
12+
ControlConfigObject,
13+
DECORATOR_CONFIGS_INITIALIZER_TOKEN,
14+
DECORATOR_CONFIGS_TOKEN,
15+
DecoratorConfigFn,
16+
PropDecoratorFactory,
17+
} from '../decorator-config';
18+
1919
export interface ConfigurationMetaObject {
2020
[key: string]: ConfigurationMeta[];
2121
}
2222

23-
export interface ControlConfig {
24-
validators: ValidatorFn[];
25-
tag: string;
26-
type?: string;
27-
options?: any[];
28-
extras: any;
29-
default?: any;
30-
}
31-
32-
export interface ControlConfigObject {
33-
[key: string]: ControlConfig;
34-
}
35-
3623
@Injectable()
3724
export class ComposerConfiguratorService {
25+
private decoratorConfigsInit = this.injector.get(
26+
DECORATOR_CONFIGS_INITIALIZER_TOKEN,
27+
[],
28+
);
29+
private decoratorConfigs = this.injector.get(DECORATOR_CONFIGS_TOKEN, []);
30+
31+
private decoratorConfigGroups = this.decoratorConfigs.reduce(
32+
(acc, config) => {
33+
acc.set(config.type, [
34+
...(acc.get(config.type) || []),
35+
config.fn as DecoratorConfigFn<any>,
36+
]);
37+
return acc;
38+
},
39+
new Map<PropDecoratorFactory, DecoratorConfigFn<any>[]>(),
40+
);
41+
3842
constructor(
43+
private injector: Injector,
3944
private fb: FormBuilder,
4045
private configService: ConfigurationService,
4146
private componentLocatorService: ComponentLocatorService,
@@ -96,65 +101,40 @@ export class ComposerConfiguratorService {
96101
defaultValue?: any,
97102
): ControlConfig {
98103
return configMeta.reduce(
99-
(acc, meta) => {
100-
if (!acc.tag) {
101-
acc.tag = 'input';
102-
103-
switch (meta.type) {
104-
case Number:
105-
acc.tag = 'input-number';
106-
break;
107-
case Boolean:
108-
acc.tag = 'switch';
109-
break;
110-
default:
111-
acc.type = 'text';
112-
}
113-
}
114-
115-
switch (meta.decorator) {
116-
case OptionRequired:
117-
acc.validators.push(Validators.required);
118-
acc.extras.required = true;
119-
break;
120-
case OptionInteger:
121-
acc.validators.push(Validators.pattern(/^-?\d+$/));
122-
acc.tag = 'input-number';
123-
break;
124-
case OptionRange:
125-
const [min, max, step] = meta.args as Parameters<
126-
typeof OptionRange
127-
>;
128-
acc.tag =
129-
!Number.isFinite(max) || !Number.isFinite(min)
130-
? 'input-number'
131-
: 'slider';
132-
acc.extras.min = min;
133-
acc.extras.max = max;
134-
acc.extras.step = step;
135-
acc.validators.push(Validators.min(min));
136-
acc.validators.push(Validators.max(max));
137-
break;
138-
case OptionAllowedValues:
139-
const [options] = meta.args as Parameters<
140-
typeof OptionAllowedValues
141-
>;
142-
143-
acc.tag = options.some(opt => typeof opt === 'function')
144-
? 'input'
145-
: 'select';
146-
acc.type = 'text';
147-
acc.options = options;
148-
break;
149-
}
150-
return acc;
104+
(config, meta) => this.applyConfig(config, meta),
105+
this.getInitialConfig(configMeta, defaultValue),
106+
);
107+
}
108+
109+
private getInitialConfig(
110+
configMeta: ConfigurationMeta[],
111+
defaultValue?: any,
112+
): ControlConfig {
113+
return configMeta.reduce(
114+
(config, meta) => {
115+
this.decoratorConfigsInit.forEach(fn =>
116+
fn(config, meta.args, meta.type, meta.decorator),
117+
);
118+
return config;
151119
},
152120
{
153121
validators: [],
154-
tag: '',
122+
component: null,
155123
extras: {},
156124
default: defaultValue,
157125
} as ControlConfig,
158126
);
159127
}
128+
129+
private applyConfig(config: ControlConfig, meta: ConfigurationMeta) {
130+
const decoratorConfigFns = this.decoratorConfigGroups.get(meta.decorator);
131+
132+
if (!decoratorConfigFns) {
133+
return config;
134+
}
135+
136+
decoratorConfigFns.forEach(fn => fn(config, meta.args, meta.type));
137+
138+
return config;
139+
}
160140
}

libs/composer/src/lib/composer.module.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { ComposerDroppableModule } from './composer-droppable';
2020
import { ComposerErrorsModule } from './composer-errors';
2121
import { ComposerPreviewModule } from './composer-preview';
2222
import { ComposerComponent } from './composer.component';
23+
import { DecoratorConfigModule } from './decorator-config';
2324

2425
@NgModule({
2526
imports: [
@@ -35,6 +36,7 @@ import { ComposerComponent } from './composer.component';
3536
ComposerControlsModule,
3637
ComposerConfiguratorModule,
3738
ComposerDroppableModule,
39+
DecoratorConfigModule,
3840
],
3941
exports: [
4042
ComposerCanvasModule,
@@ -55,6 +57,7 @@ export class ComposerModule {
5557
providers: [
5658
ComposerConfiguratorService,
5759
{ provide: ErrorStrategy, useClass: SuppressErrorStrategy },
60+
DecoratorConfigModule.forRoot().providers,
5861
],
5962
};
6063
}

libs/composer/src/lib/decorator-config/allowed-values/allowed-values.component.css

Whitespace-only changes.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<nz-form-control>
2+
<nz-select
3+
nzAllowClear
4+
[formControlNameEx]="name"
5+
[formControlNameExParent]="parent"
6+
>
7+
<nz-option
8+
*ngFor="let opt of extras?.options"
9+
[nzValue]="opt"
10+
[nzLabel]="opt"
11+
></nz-option>
12+
</nz-select>
13+
</nz-form-control>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/* tslint:disable:no-unused-variable */
2+
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
3+
import { By } from '@angular/platform-browser';
4+
import { DebugElement } from '@angular/core';
5+
6+
import { AllowedValuesComponent } from './allowed-values.component';
7+
8+
describe('AllowedValuesComponent', () => {
9+
let component: AllowedValuesComponent;
10+
let fixture: ComponentFixture<AllowedValuesComponent>;
11+
12+
beforeEach(async(() => {
13+
TestBed.configureTestingModule({
14+
declarations: [ AllowedValuesComponent ]
15+
})
16+
.compileComponents();
17+
}));
18+
19+
beforeEach(() => {
20+
fixture = TestBed.createComponent(AllowedValuesComponent);
21+
component = fixture.componentInstance;
22+
fixture.detectChanges();
23+
});
24+
25+
it('should create', () => {
26+
expect(component).toBeTruthy();
27+
});
28+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
2+
import { ControlContainer } from '@angular/forms';
3+
4+
import { ControlConfigComponent } from '../types';
5+
6+
export interface AllowedValuesExtras {
7+
options: string[];
8+
}
9+
10+
@Component({
11+
selector: 'orc-allowed-values',
12+
templateUrl: './allowed-values.component.html',
13+
styleUrls: ['./allowed-values.component.css'],
14+
changeDetection: ChangeDetectionStrategy.OnPush,
15+
})
16+
export class AllowedValuesComponent
17+
implements ControlConfigComponent<AllowedValuesExtras> {
18+
@Input() name: string;
19+
@Input() parent: ControlContainer;
20+
@Input() extras: AllowedValuesExtras;
21+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { OptionAllowedValues } from '@orchestrator/core';
2+
3+
import { createDecoratorConfigFactory, provideDecoratorConfig } from '../token';
4+
import { AllowedValuesComponent } from './allowed-values.component';
5+
6+
const allowedValuesFactoryFn = createDecoratorConfigFactory<
7+
AllowedValuesComponent
8+
>(OptionAllowedValues, (config, [options]) => {
9+
const areOptionsDeterministic = options.every(
10+
opt => typeof opt !== 'function',
11+
);
12+
if (areOptionsDeterministic) {
13+
config.component = AllowedValuesComponent;
14+
config.extras.options = options;
15+
}
16+
});
17+
18+
export function allowedValuesFactory() {
19+
return allowedValuesFactoryFn();
20+
}
21+
22+
export const allowedValuesProvider = provideDecoratorConfig(
23+
allowedValuesFactory,
24+
);

0 commit comments

Comments
 (0)