Skip to content

Commit dca7aaa

Browse files
authored
feat: add middleware support for Angular and Vue
1 parent aba1aac commit dca7aaa

9 files changed

Lines changed: 194 additions & 43 deletions

File tree

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
The MIT License
3+
4+
Copyright (c) 2023-2023 EclipseSource Munich
5+
https://github.com/eclipsesource/jsonforms
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in
15+
all copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
THE SOFTWARE.
24+
*/
25+
26+
import { ReactiveFormsModule } from '@angular/forms';
27+
import { MatFormFieldModule } from '@angular/material/form-field';
28+
import { MatInputModule } from '@angular/material/input';
29+
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
30+
import { NumberControlRenderer } from '../src';
31+
import { JsonFormsAngularService, JsonFormsControl } from '@jsonforms/angular';
32+
import {
33+
baseSetup,
34+
getJsonFormsService,
35+
prepareComponent,
36+
} from '@jsonforms/angular-test';
37+
38+
const imports = [
39+
MatFormFieldModule,
40+
MatInputModule,
41+
NoopAnimationsModule,
42+
ReactiveFormsModule,
43+
];
44+
const providers = [JsonFormsAngularService];
45+
const componentUT: any = NumberControlRenderer;
46+
const testConfig = { imports, providers, componentUT };
47+
48+
describe('middleware tests', () => {
49+
let component: JsonFormsControl;
50+
const startingValues = {
51+
core: {
52+
data: 'startValue',
53+
schema: { type: 'string' },
54+
uischema: {
55+
type: 'control',
56+
},
57+
},
58+
};
59+
60+
baseSetup(testConfig);
61+
62+
beforeEach(() => {
63+
const preparedComponents = prepareComponent(testConfig, 'input');
64+
component = preparedComponents.component;
65+
});
66+
67+
it('init using middleware', () => {
68+
const jsonFormsService: JsonFormsAngularService =
69+
getJsonFormsService(component);
70+
const spyMiddleware = jasmine.createSpy('spy1').and.returnValue({
71+
data: 4,
72+
schema: { type: 'number' },
73+
uischema: {
74+
type: 'VerticalLayout',
75+
elements: [
76+
{
77+
type: 'Control',
78+
},
79+
],
80+
},
81+
});
82+
jsonFormsService.init(startingValues, spyMiddleware);
83+
expect(spyMiddleware).toHaveBeenCalled();
84+
const core = jsonFormsService.getState().jsonforms.core;
85+
expect(core?.data).toBe(4);
86+
expect(core?.schema.type).toBe('number');
87+
expect(core?.uischema.type).toBe('VerticalLayout');
88+
});
89+
});

packages/angular/src/library/jsonforms-root.component.ts

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,11 @@ import {
3737
JsonFormsI18nState,
3838
JsonFormsRendererRegistryEntry,
3939
JsonSchema,
40+
Middleware,
4041
UISchemaElement,
4142
UISchemaTester,
4243
ValidationMode,
44+
defaultMiddleware,
4345
} from '@jsonforms/core';
4446
import Ajv, { ErrorObject } from 'ajv';
4547
import { JsonFormsAngularService, USE_STATE_VALUE } from './jsonforms.service';
@@ -64,6 +66,7 @@ export class JsonForms implements DoCheck, OnChanges, OnInit {
6466
@Input() config: any;
6567
@Input() i18n: JsonFormsI18nState;
6668
@Input() additionalErrors: ErrorObject[];
69+
@Input() middleware: Middleware = defaultMiddleware;
6770
@Output() errors = new EventEmitter<ErrorObject[]>();
6871

6972
private previousData: any;
@@ -75,21 +78,24 @@ export class JsonForms implements DoCheck, OnChanges, OnInit {
7578
constructor(private jsonformsService: JsonFormsAngularService) {}
7679

7780
ngOnInit(): void {
78-
this.jsonformsService.init({
79-
core: {
80-
data: this.data,
81-
uischema: this.uischema,
82-
schema: this.schema,
83-
ajv: this.ajv,
84-
validationMode: this.validationMode,
85-
additionalErrors: this.additionalErrors,
81+
this.jsonformsService.init(
82+
{
83+
core: {
84+
data: this.data,
85+
uischema: this.uischema,
86+
schema: this.schema,
87+
ajv: this.ajv,
88+
validationMode: this.validationMode,
89+
additionalErrors: this.additionalErrors,
90+
},
91+
uischemas: this.uischemas,
92+
i18n: this.i18n,
93+
renderers: this.renderers,
94+
config: this.config,
95+
readonly: this.readonly,
8696
},
87-
uischemas: this.uischemas,
88-
i18n: this.i18n,
89-
renderers: this.renderers,
90-
config: this.config,
91-
readonly: this.readonly,
92-
});
97+
this.middleware
98+
);
9399
this.jsonformsService.$state.subscribe((state) => {
94100
const data = state?.jsonforms?.core?.data;
95101
const errors = state?.jsonforms?.core?.errors;

packages/angular/src/library/jsonforms.service.ts

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ import {
4444
UISchemaTester,
4545
ValidationMode,
4646
updateI18n,
47+
Middleware,
48+
defaultMiddleware,
4749
} from '@jsonforms/core';
4850
import { BehaviorSubject, Observable } from 'rxjs';
4951
import type { JsonFormsBaseRenderer } from './base.renderer';
@@ -56,6 +58,7 @@ export const USE_STATE_VALUE = Symbol('Marker to use state value');
5658
export class JsonFormsAngularService {
5759
private _state: JsonFormsSubStates;
5860
private state: BehaviorSubject<JsonFormsState>;
61+
private middleware: Middleware;
5962

6063
init(
6164
initialState: JsonFormsSubStates = {
@@ -66,8 +69,10 @@ export class JsonFormsAngularService {
6669
validationMode: 'ValidateAndShow',
6770
additionalErrors: undefined,
6871
},
69-
}
72+
},
73+
middleware: Middleware = defaultMiddleware
7074
) {
75+
this.middleware = middleware;
7176
this._state = initialState;
7277
this._state.config = configReducer(
7378
undefined,
@@ -143,9 +148,10 @@ export class JsonFormsAngularService {
143148
}
144149

145150
updateValidationMode(validationMode: ValidationMode): void {
146-
const coreState = coreReducer(
151+
const coreState = this.middleware(
147152
this._state.core,
148-
Actions.setValidationMode(validationMode)
153+
Actions.setValidationMode(validationMode),
154+
coreReducer
149155
);
150156
this._state.core = coreState;
151157
this.updateSubject();
@@ -161,7 +167,11 @@ export class JsonFormsAngularService {
161167
}
162168

163169
updateCore<T extends CoreActions>(coreAction: T): T {
164-
const coreState = coreReducer(this._state.core, coreAction);
170+
const coreState = this.middleware(
171+
this._state.core,
172+
coreAction,
173+
coreReducer
174+
);
165175
if (coreState !== this._state.core) {
166176
this._state.core = coreState;
167177
this.updateSubject();
@@ -199,13 +209,14 @@ export class JsonFormsAngularService {
199209
setUiSchema(uischema: UISchemaElement | undefined): void {
200210
const newUiSchema =
201211
uischema ?? generateDefaultUISchema(this._state.core.schema);
202-
const coreState = coreReducer(
212+
const coreState = this.middleware(
203213
this._state.core,
204214
Actions.updateCore(
205215
this._state.core.data,
206216
this._state.core.schema,
207217
newUiSchema
208-
)
218+
),
219+
coreReducer
209220
);
210221
if (coreState !== this._state.core) {
211222
this._state.core = coreState;
@@ -214,13 +225,14 @@ export class JsonFormsAngularService {
214225
}
215226

216227
setSchema(schema: JsonSchema | undefined): void {
217-
const coreState = coreReducer(
228+
const coreState = this.middleware(
218229
this._state.core,
219230
Actions.updateCore(
220231
this._state.core.data,
221232
schema ?? generateJsonSchema(this._state.core.data),
222233
this._state.core.uischema
223-
)
234+
),
235+
coreReducer
224236
);
225237
if (coreState !== this._state.core) {
226238
this._state.core = coreState;
@@ -229,13 +241,14 @@ export class JsonFormsAngularService {
229241
}
230242

231243
setData(data: any): void {
232-
const coreState = coreReducer(
244+
const coreState = this.middleware(
233245
this._state.core,
234246
Actions.updateCore(
235247
data,
236248
this._state.core.schema,
237249
this._state.core.uischema
238-
)
250+
),
251+
coreReducer
239252
);
240253
if (coreState !== this._state.core) {
241254
this._state.core = coreState;
@@ -257,6 +270,11 @@ export class JsonFormsAngularService {
257270
this.updateSubject();
258271
}
259272

273+
setMiddleware(middleware: Middleware): void {
274+
this._state.middleware = middleware;
275+
this.updateSubject();
276+
}
277+
260278
getState(): JsonFormsState {
261279
return cloneDeep({ jsonforms: this._state });
262280
}

packages/core/src/reducers/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ export * from './reducers';
3232
export * from './renderers';
3333
export * from './selectors';
3434
export * from './uischemas';
35+
export * from './middleware';
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
The MIT License
3+
4+
Copyright (c) 2023 EclipseSource Munich
5+
https://github.com/eclipsesource/jsonforms
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in
15+
all copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
THE SOFTWARE.
24+
*/
25+
import { CoreActions } from '../actions';
26+
import { JsonFormsCore } from './core';
27+
28+
export interface Middleware {
29+
(
30+
state: JsonFormsCore,
31+
action: CoreActions,
32+
defaultReducer: (state: JsonFormsCore, action: CoreActions) => JsonFormsCore
33+
): JsonFormsCore;
34+
}
35+
export const defaultMiddleware: Middleware = (state, action, defaultReducer) =>
36+
defaultReducer(state, action);

packages/react/src/JsonForms.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@ import {
3838
JsonFormsRendererRegistryEntry,
3939
JsonFormsUISchemaRegistryEntry,
4040
JsonSchema,
41+
Middleware,
4142
OwnPropsOfJsonFormsRenderer,
4243
removeId,
4344
UISchemaElement,
4445
ValidationMode,
4546
} from '@jsonforms/core';
4647
import {
4748
JsonFormsStateProvider,
48-
Middleware,
4949
withJsonFormsRendererProps,
5050
} from './JsonFormsContext';
5151

packages/react/src/JsonFormsContext.tsx

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ import {
7676
LabelProps,
7777
mapStateToLabelProps,
7878
CoreActions,
79+
Middleware,
80+
defaultMiddleware,
7981
} from '@jsonforms/core';
8082
import debounce from 'lodash/debounce';
8183
import React, {
@@ -128,17 +130,6 @@ const useEffectAfterFirstRender = (
128130
}, dependencies);
129131
};
130132

131-
export interface Middleware {
132-
(
133-
state: JsonFormsCore,
134-
action: CoreActions,
135-
defaultReducer: (state: JsonFormsCore, action: CoreActions) => JsonFormsCore
136-
): JsonFormsCore;
137-
}
138-
139-
const defaultMiddleware: Middleware = (state, action, defaultReducer) =>
140-
defaultReducer(state, action);
141-
142133
export const JsonFormsStateProvider = ({
143134
children,
144135
initState,

packages/react/test/renderers/JsonForms.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import type {
3333
JsonFormsStore,
3434
JsonSchema,
3535
Layout,
36+
Middleware,
3637
RendererProps,
3738
UISchemaElement,
3839
} from '@jsonforms/core';
@@ -58,7 +59,6 @@ import {
5859
} from '../../src/JsonForms';
5960
import {
6061
JsonFormsStateProvider,
61-
Middleware,
6262
useJsonForms,
6363
withJsonFormsControlProps,
6464
} from '../../src/JsonFormsContext';

0 commit comments

Comments
 (0)