Skip to content

Commit 0fb28ca

Browse files
committed
initial migration doc for new paths
1 parent 4f1c4cd commit 0fb28ca

File tree

1 file changed

+113
-0
lines changed

1 file changed

+113
-0
lines changed

MIGRATION.md

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,118 @@
11
# Migration guide
22

3+
## Migrating to JSON Forms 4.0
4+
5+
### Unified internal path handling to JSON pointers
6+
7+
Previously, JSON Forms used two different ways to express paths:
8+
9+
- The `scope` JSON Pointer (see [RFC 6901](https://datatracker.ietf.org/doc/html/rfc6901)) paths used in UI Schemas to resolve subschemas of the provided JSON Schema
10+
- The dot-separated paths (lodash format) to resolve entries in the form-wide data object
11+
12+
This led to confusion and prevented property names from containing dots (`.`) because lodash paths don't support escaping.
13+
14+
The rework unifies these paths to all use the JSON Pointer format.
15+
Therefore, this breaks custom renderers that manually modify or create paths to resolve additional data.
16+
They used the dot-separated paths and need to be migrated to use JSON Pointers instead.
17+
18+
To abstract the composition of paths away from renderers, the `Paths.compose` utility of `@jsonforms/core` should be used.
19+
It takes a valid JSON Pointer and an arbitrary number of _unencoded_ segments to append.
20+
The utility takes care of adding separators and encoding special characters in the given segments.
21+
22+
#### Brief example
23+
24+
This showcases only calculating a new path in the dot-separated way vs the new way.
25+
Assume `path` is a valid JSON Pointer that a sub property should be addressed of.
26+
27+
```ts
28+
import { Paths } from '@jsonforms/core';
29+
30+
// Previous: Calculate the path manually
31+
const oldManual = `${path}.foo.~bar`;
32+
// Previous: Use the Paths.compose util
33+
const oldWithUtil = Paths.compose(path, 'foo.~bar');
34+
35+
// Now: After the initial path, hand in each segment separately.
36+
// Segments must be unencoded. The util automatically encodes them.
37+
// In this case the ~ will be encoded.
38+
const new = Paths.compose(path, 'foo', '~bar');
39+
40+
// Calculate a path relative to the root data that the path is resolved against
41+
const oldFromRoot = 'nested.prop';
42+
const newFromRoot = Paths.compose('', 'nested', 'prop'); // The empty JSON Pointer '' points to the whole data.
43+
```
44+
45+
#### Extensive Example
46+
47+
This example shows in a more elaborate way, how path composition might be used in a custom renderer.
48+
This example uses a custom renderer implemented for the React bindings.
49+
However, the approach is similar for all bindings.
50+
51+
To showcase how a migration could look like, assume a custom renderer that gets handed in this data object:
52+
53+
```ts
54+
const data = {
55+
foo: 'abc',
56+
'b/ar': {
57+
'~': 'tilde',
58+
},
59+
'array~Data': ['entry1', 'entry2'],
60+
};
61+
```
62+
63+
The renderer wants to resolve the `~` property to directly use it and iterate over the array and use the dispatch to render each entry.
64+
65+
<details>
66+
<summary>Renderer code</summary>
67+
68+
```tsx
69+
import { Paths, Resolve } from '@jsonforms/core';
70+
import { JsonFormsDispatch } from '@jsonforms/react';
71+
72+
export const CustomRenderer = (props: ControlProps & WithInput) => {
73+
const {
74+
// [...]
75+
data, // The data object to be rendered. See content above
76+
path, // Path to the data object handed into this renderer
77+
schema, // JSON Schema describing this renderers data
78+
} = props;
79+
80+
// Calculate path from the given data to the nested ~ property
81+
// You could also do this manually without the Resolve.data util
82+
const tildePath = Paths.compose('', 'b/ar', '~');
83+
const tildeValue = Resolve.data(data, tildePath);
84+
85+
const arrayData = data['array~Data'];
86+
// Resolve schema of array entries from this renderer's schema.
87+
const entrySchemaPath = Paths.compose(
88+
'#',
89+
'properties',
90+
'array~Data',
91+
'items'
92+
);
93+
const entrySchema = Resolve.schema(schema, entrySchemaPath);
94+
// Iterate over array~Data and dispatch for each entry
95+
// Dispatch needs the path from the root of JSON Forms's data
96+
// Thus, calculate it by extending this control's path
97+
const dispatchEntries = arrayData.map((arrayEntry, index) => {
98+
const entryPath = Paths.compose(path, 'array~Data', `${index}`);
99+
const schema = Resolve.schema();
100+
return (
101+
<JsonFormsDispatch
102+
key={index}
103+
schema={entrySchema}
104+
path={path}
105+
// [...] other props like cells, etc
106+
/>
107+
);
108+
});
109+
110+
// [...]
111+
};
112+
```
113+
114+
</details>
115+
3116
## Migrating to JSON Forms 3.3
4117

5118
### Angular support now targets Angular 17 and Angular 18

0 commit comments

Comments
 (0)