Skip to content

Commit e3f0835

Browse files
LukasBolllucas-koehler
authored andcommitted
change path to JSON pointer
1 parent c00b664 commit e3f0835

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+449
-382
lines changed

packages/angular-material/src/library/layouts/array-layout.renderer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ export class ArrayLayoutRenderer
233233
}
234234
return {
235235
schema: this.scopedSchema,
236-
path: Paths.compose(this.propsPath, `${index}`),
236+
path: Paths.compose(this.propsPath, `/${index}`),
237237
uischema,
238238
};
239239
}

packages/angular-material/src/library/other/master-detail/master.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ export const removeSchemaKeywords = (path: string) => {
9191
<button
9292
mat-icon-button
9393
class="button item-button hide"
94-
(click)="onDeleteClick(i)"
94+
(click)="onDeleteClick($event, i)"
9595
[ngClass]="{ show: highlightedIdx == i }"
9696
*ngIf="isEnabled()"
9797
>
@@ -224,7 +224,7 @@ export class MasterListComponent
224224
? d.toString()
225225
: get(d, labelRefInstancePath ?? getFirstPrimitiveProp(schema)),
226226
data: d,
227-
path: `${path}.${index}`,
227+
path: `${path}/${index}`,
228228
schema,
229229
uischema: detailUISchema,
230230
};
@@ -278,7 +278,8 @@ export class MasterListComponent
278278
)();
279279
}
280280

281-
onDeleteClick(item: number) {
281+
onDeleteClick(e: any, item: number) {
282+
e.stopPropagation();
282283
this.removeItems(this.propsPath, [item])();
283284
}
284285

packages/angular-material/src/library/other/table.renderer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ export const controlWithoutLabel = (scope: string): ControlElement => ({
273273
@Pipe({ name: 'getProps' })
274274
export class GetProps implements PipeTransform {
275275
transform(index: number, props: OwnPropsOfRenderer) {
276-
const rowPath = Paths.compose(props.path, `${index}`);
276+
const rowPath = Paths.compose(props.path, `/${index}`);
277277
return {
278278
schema: props.schema,
279279
uischema: props.uischema,

packages/angular-material/test/master-detail.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ describe('Master detail', () => {
223223
// delete 1st item
224224
spyOn(component, 'removeItems').and.callFake(() => () => {
225225
getJsonFormsService(component).updateCore(
226-
Actions.update('orders', () => moreData.orders.slice(1))
226+
Actions.update('/orders', () => moreData.orders.slice(1))
227227
);
228228
fixture.detectChanges();
229229
});
@@ -267,7 +267,7 @@ describe('Master detail', () => {
267267
const copy = moreData.orders.slice();
268268
copy.splice(1, 1);
269269
getJsonFormsService(component).updateCore(
270-
Actions.update('orders', () => copy)
270+
Actions.update('/orders', () => copy)
271271
);
272272
fixture.detectChanges();
273273
});
@@ -382,7 +382,7 @@ describe('Master detail', () => {
382382
customer: { name: 'ACME' },
383383
title: 'Carrots',
384384
},
385-
path: 'orders.0',
385+
path: '/orders/0',
386386
schema: schema.definitions.order,
387387
uischema: {
388388
type: 'VerticalLayout',

packages/core/src/i18n/i18nUtil.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { ErrorObject } from 'ajv';
22
import { isInternationalized, Labelable, UISchemaElement } from '../models';
33
import { getControlPath } from '../reducers';
4-
import { formatErrorMessage } from '../util';
4+
import { formatErrorMessage, toLodashPath } from '../util';
55
import type { i18nJsonSchema, ErrorTranslator, Translator } from './i18nTypes';
66
import {
77
ArrayDefaultTranslation,
@@ -28,7 +28,7 @@ export const getI18nKeyPrefixBySchema = (
2828
*/
2929
export const transformPathToI18nPrefix = (path: string): string => {
3030
return (
31-
path
31+
toLodashPath(path)
3232
?.split('.')
3333
.filter((segment) => !/^\d+$/.test(segment))
3434
.join('.') || 'root'

packages/core/src/reducers/core.ts

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,13 @@ import {
4646
UPDATE_CORE,
4747
UpdateCoreAction,
4848
} from '../actions';
49-
import { createAjv, decode, isOneOfEnumSchema, Reducer } from '../util';
49+
import {
50+
composePaths,
51+
createAjv,
52+
isOneOfEnumSchema,
53+
Reducer,
54+
toLodashSegments,
55+
} from '../util';
5056
import type { JsonSchema, UISchemaElement } from '../models';
5157

5258
export const validate = (
@@ -269,18 +275,19 @@ export const coreReducer: Reducer<JsonFormsCore, CoreActions> = (
269275
errors,
270276
};
271277
} else {
272-
const oldData: any = get(state.data, action.path);
278+
const lodashDataPathSegments = toLodashSegments(action.path);
279+
const oldData: any = get(state.data, lodashDataPathSegments);
273280
const newData = action.updater(cloneDeep(oldData));
274281
let newState: any;
275282
if (newData !== undefined) {
276283
newState = setFp(
277-
action.path,
284+
lodashDataPathSegments,
278285
newData,
279286
state.data === undefined ? {} : state.data
280287
);
281288
} else {
282289
newState = unsetFp(
283-
action.path,
290+
lodashDataPathSegments,
284291
state.data === undefined ? {} : state.data
285292
);
286293
}
@@ -352,19 +359,11 @@ export const getControlPath = (error: ErrorObject) => {
352359
// With AJV v8 the property was renamed to 'instancePath'
353360
let controlPath = (error as any).dataPath || error.instancePath || '';
354361

355-
// change '/' chars to '.'
356-
controlPath = controlPath.replace(/\//g, '.');
357-
358362
const invalidProperty = getInvalidProperty(error);
359363
if (invalidProperty !== undefined && !controlPath.endsWith(invalidProperty)) {
360-
controlPath = `${controlPath}.${invalidProperty}`;
364+
controlPath = composePaths(controlPath, invalidProperty);
361365
}
362366

363-
// remove '.' chars at the beginning of paths
364-
controlPath = controlPath.replace(/^./, '');
365-
366-
// decode JSON Pointer escape sequences
367-
controlPath = decode(controlPath);
368367
return controlPath;
369368
};
370369

@@ -462,5 +461,5 @@ export const errorAt = (instancePath: string, schema: JsonSchema) =>
462461
getErrorsAt(instancePath, schema, (path) => path === instancePath);
463462
export const subErrorsAt = (instancePath: string, schema: JsonSchema) =>
464463
getErrorsAt(instancePath, schema, (path) =>
465-
path.startsWith(instancePath + '.')
464+
path.startsWith(instancePath + '/')
466465
);

packages/core/src/reducers/i18n.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,19 @@
2626
import {
2727
defaultErrorTranslator,
2828
defaultTranslator,
29+
ErrorTranslator,
2930
JsonFormsI18nState,
31+
Translator,
3032
} from '../i18n';
3133
import {
3234
I18nActions,
3335
SET_LOCALE,
3436
SET_TRANSLATOR,
3537
UPDATE_I18N,
3638
} from '../actions';
37-
import type { Reducer } from '../util';
39+
import { Reducer, toLodashPath } from '../util';
40+
import { ErrorObject } from 'ajv';
41+
import { UISchemaElement } from '../models';
3842

3943
export const defaultJsonFormsI18nState: Required<JsonFormsI18nState> = {
4044
locale: 'en',
@@ -53,7 +57,6 @@ export const i18nReducer: Reducer<JsonFormsI18nState, I18nActions> = (
5357
action.translator ?? defaultJsonFormsI18nState.translate;
5458
const translateError =
5559
action.errorTranslator ?? defaultJsonFormsI18nState.translateError;
56-
5760
if (
5861
locale !== state.locale ||
5962
translate !== state.translate ||
@@ -84,6 +87,22 @@ export const i18nReducer: Reducer<JsonFormsI18nState, I18nActions> = (
8487
}
8588
};
8689

90+
export const wrapTranslateFunction = (translator: Translator): Translator => {
91+
return (id: string, defaultMessage?: string | undefined, values?: any) => {
92+
return translator(toLodashPath(id), defaultMessage, values);
93+
};
94+
};
95+
96+
export const wrapErrorTranslateFunction = (
97+
translator: ErrorTranslator
98+
): ErrorTranslator => {
99+
return (
100+
error: ErrorObject,
101+
translate: Translator,
102+
uischema?: UISchemaElement
103+
) => translator(error, wrapTranslateFunction(translate), uischema);
104+
};
105+
87106
export const fetchLocale = (state?: JsonFormsI18nState) => {
88107
if (state === undefined) {
89108
return undefined;

packages/core/src/util/path.ts

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,26 @@ import isEmpty from 'lodash/isEmpty';
2727
import range from 'lodash/range';
2828
import { isScoped, Scopable } from '../models';
2929

30-
export const compose = (path1: string, path2: string) => {
31-
let p1 = path1;
32-
if (!isEmpty(path1) && !isEmpty(path2) && !path2.startsWith('[')) {
33-
p1 = path1 + '.';
30+
/**
31+
* Composes two JSON pointer. Pointer1 is appended to pointer2.
32+
* JSON pointer is seperated and start with '/' e.g: /foo/0
33+
*
34+
* @param {string} pointer1 JSON pointer
35+
* @param {string} pointer2 JSON pointer
36+
* @returns {string} resulting JSON pointer
37+
*/
38+
export const compose = (pointer1: string, pointer2: string) => {
39+
let p2 = pointer2;
40+
if (!isEmpty(pointer2) && !pointer2.startsWith('/')) {
41+
p2 = '/' + pointer2;
3442
}
3543

36-
if (isEmpty(p1)) {
37-
return path2;
38-
} else if (isEmpty(path2)) {
39-
return p1;
44+
if (isEmpty(pointer1)) {
45+
return p2;
46+
} else if (isEmpty(pointer2)) {
47+
return pointer1;
4048
} else {
41-
return `${p1}${path2}`;
49+
return `${pointer1}${p2}`;
4250
}
4351
};
4452

@@ -76,13 +84,13 @@ export const toDataPathSegments = (schemaPath: string): string[] => {
7684
* Data paths can be used in field change event handlers like handleChange.
7785
*
7886
* @example
79-
* toDataPath('#/properties/foo/properties/bar') === 'foo.bar')
87+
* toDataPath('#/properties/foo/properties/bar') === '/foo/bar')
8088
*
8189
* @param {string} schemaPath the schema path to be converted
8290
* @returns {string} the data path
8391
*/
8492
export const toDataPath = (schemaPath: string): string => {
85-
return toDataPathSegments(schemaPath).join('.');
93+
return '/' + toDataPathSegments(schemaPath).join('/');
8694
};
8795

8896
export const composeWithUi = (scopableUi: Scopable, path: string): string => {
@@ -96,7 +104,19 @@ export const composeWithUi = (scopableUi: Scopable, path: string): string => {
96104
return path ?? '';
97105
}
98106

99-
return compose(path, segments.join('.'));
107+
return compose(path, '/' + segments.join('/'));
108+
};
109+
110+
export const toLodashSegments = (jsonPointer: string): string[] => {
111+
let path = jsonPointer;
112+
if (jsonPointer && jsonPointer.startsWith('/')) {
113+
path = jsonPointer.substring(1);
114+
}
115+
return path ? path.split('/').map(decode) : [];
116+
};
117+
118+
export const toLodashPath = (jsonPointer: string) => {
119+
return toLodashSegments(jsonPointer).join('.');
100120
};
101121

102122
/**

packages/core/src/util/resolvers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export const resolveData = (instance: any, dataPath: string): any => {
4646
if (isEmpty(dataPath)) {
4747
return instance;
4848
}
49-
const dataPathSegments = dataPath.split('.');
49+
const dataPathSegments = dataPath.split('/').slice(1);
5050

5151
return dataPathSegments.reduce((curInstance, decodedSegment) => {
5252
if (

packages/core/src/util/util.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,9 @@ export const Resolve: {
161161
};
162162

163163
// Paths --
164-
const fromScoped = (scopable: Scoped): string =>
165-
toDataPathSegments(scopable.scope).join('.');
164+
const fromScoped = (scopable: Scoped): string => {
165+
return '/' + toDataPathSegments(scopable.scope).join('/');
166+
};
166167

167168
export const Paths = {
168169
compose: composePaths,

0 commit comments

Comments
 (0)