@ditojs/admin 3.2.1 → 3.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/dito-admin.es.js +43 -22
- package/dist/dito-admin.umd.js +4 -4
- package/package.json +2 -2
- package/src/DitoAdmin.js +1 -0
- package/src/components/DitoDialog.vue +13 -7
- package/src/components/DitoForm.vue +21 -10
- package/src/components/DitoView.vue +12 -6
- package/src/mixins/DitoMixin.js +23 -10
- package/src/mixins/OptionsMixin.js +6 -6
- package/src/mixins/RouteMixin.js +1 -1
- package/src/mixins/SourceMixin.js +3 -3
- package/src/mixins/TypeMixin.js +3 -8
- package/src/mixins/ValueMixin.js +1 -1
- package/src/types/DitoTypeComputed.vue +1 -1
- package/src/utils/{FormModel.js → DataModel.js} +4 -4
- package/src/utils/schema/data.js +8 -8
- package/src/utils/schema/lookup.js +1 -1
- package/types/index.d.ts +7 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ditojs/admin",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Dito.js Admin is a schema based admin interface for Dito.js Server, featuring auto-generated views and forms and built with Vue.js",
|
|
6
6
|
"repository": "https://github.com/ditojs/dito/tree/main/packages/admin",
|
|
@@ -131,5 +131,5 @@
|
|
|
131
131
|
"vue-router": "^5.3.1",
|
|
132
132
|
"vue-upload-component": "^3.2.2"
|
|
133
133
|
},
|
|
134
|
-
"gitHead": "
|
|
134
|
+
"gitHead": "04e5e45534cf76e1472d62c5fb9203d37d0161f7"
|
|
135
135
|
}
|
package/src/DitoAdmin.js
CHANGED
|
@@ -36,7 +36,7 @@ import { clone } from '@ditojs/utils'
|
|
|
36
36
|
import DitoComponent from '../DitoComponent.js'
|
|
37
37
|
import DomMixin from '../mixins/DomMixin.js'
|
|
38
38
|
import { getButtonSchemas } from '../utils/schema/lookup.js'
|
|
39
|
-
import {
|
|
39
|
+
import { DataModel } from '../utils/DataModel.js'
|
|
40
40
|
import { UseFocusTrap } from '@vueuse/integrations/useFocusTrap/component'
|
|
41
41
|
|
|
42
42
|
// @vue/component
|
|
@@ -47,7 +47,8 @@ export default DitoComponent.component('DitoDialog', {
|
|
|
47
47
|
|
|
48
48
|
provide() {
|
|
49
49
|
return {
|
|
50
|
-
$dialogComponent: () => this
|
|
50
|
+
$dialogComponent: () => this,
|
|
51
|
+
$dataModel: () => this.ownDataModel
|
|
51
52
|
}
|
|
52
53
|
},
|
|
53
54
|
|
|
@@ -79,6 +80,11 @@ export default DitoComponent.component('DitoDialog', {
|
|
|
79
80
|
},
|
|
80
81
|
|
|
81
82
|
computed: {
|
|
83
|
+
// @override DitoMixin.dataModel
|
|
84
|
+
dataModel() {
|
|
85
|
+
return this.ownDataModel
|
|
86
|
+
},
|
|
87
|
+
|
|
82
88
|
dialogComponent() {
|
|
83
89
|
return this
|
|
84
90
|
},
|
|
@@ -129,8 +135,8 @@ export default DitoComponent.component('DitoDialog', {
|
|
|
129
135
|
|
|
130
136
|
created() {
|
|
131
137
|
// Writes defaults and computed values into the dialog's data and resolves
|
|
132
|
-
// options, see `
|
|
133
|
-
this.
|
|
138
|
+
// options, see `DataModel`:
|
|
139
|
+
this.ownDataModel = new DataModel({
|
|
134
140
|
component: this,
|
|
135
141
|
getSchema: () => this.schema,
|
|
136
142
|
getData: () => this.dialogData
|
|
@@ -149,8 +155,8 @@ export default DitoComponent.component('DitoDialog', {
|
|
|
149
155
|
|
|
150
156
|
beforeUnmount() {
|
|
151
157
|
// Stop the model before the component's own watchers are stopped, as the
|
|
152
|
-
// model's watchers aren't part of the component, see `
|
|
153
|
-
this.
|
|
158
|
+
// model's watchers aren't part of the component, see `DataModel`.
|
|
159
|
+
this.ownDataModel.stop()
|
|
154
160
|
},
|
|
155
161
|
|
|
156
162
|
methods: {
|
|
@@ -179,7 +185,7 @@ export default DitoComponent.component('DitoDialog', {
|
|
|
179
185
|
|
|
180
186
|
async submit() {
|
|
181
187
|
// Wait for computed values that depend on pending loads, e.g. options.
|
|
182
|
-
await this.waitForPendingLoadsOf(this.
|
|
188
|
+
await this.waitForPendingLoadsOf(this.dataModel)
|
|
183
189
|
this.resolve(this.dialogData)
|
|
184
190
|
},
|
|
185
191
|
|
|
@@ -60,12 +60,18 @@ import { isObjectSource } from '../utils/schema/structure.js'
|
|
|
60
60
|
import { getButtonSchemas } from '../utils/schema/lookup.js'
|
|
61
61
|
import { getComponentPathByDataPath } from '../utils/schema/data.js'
|
|
62
62
|
import { resolvePath } from '../utils/path.js'
|
|
63
|
-
import {
|
|
63
|
+
import { DataModel } from '../utils/DataModel.js'
|
|
64
64
|
|
|
65
65
|
// @vue/component
|
|
66
66
|
export default DitoComponent.component('DitoForm', {
|
|
67
67
|
mixins: [RouteMixin, ResourceMixin],
|
|
68
68
|
|
|
69
|
+
provide() {
|
|
70
|
+
return {
|
|
71
|
+
$dataModel: () => this.ownDataModel
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
|
|
69
75
|
data() {
|
|
70
76
|
return {
|
|
71
77
|
createdData: null,
|
|
@@ -74,6 +80,11 @@ export default DitoComponent.component('DitoForm', {
|
|
|
74
80
|
},
|
|
75
81
|
|
|
76
82
|
computed: {
|
|
83
|
+
// @override DitoMixin.dataModel
|
|
84
|
+
dataModel() {
|
|
85
|
+
return this.ownDataModel
|
|
86
|
+
},
|
|
87
|
+
|
|
77
88
|
isForm() {
|
|
78
89
|
return true
|
|
79
90
|
},
|
|
@@ -149,7 +160,7 @@ export default DitoComponent.component('DitoForm', {
|
|
|
149
160
|
isDirty() {
|
|
150
161
|
// Forms that mutate the inherited data directly aren't dirty themselves,
|
|
151
162
|
// their changes make the form that owns the data dirty instead.
|
|
152
|
-
return !this.isMutating && this.
|
|
163
|
+
return !this.isMutating && this.dataModel.isDirty
|
|
153
164
|
},
|
|
154
165
|
|
|
155
166
|
isMutating() {
|
|
@@ -354,8 +365,8 @@ export default DitoComponent.component('DitoForm', {
|
|
|
354
365
|
|
|
355
366
|
created() {
|
|
356
367
|
// Writes defaults and computed values into the form's data, resolves
|
|
357
|
-
// options and tracks whether the data is dirty, see `
|
|
358
|
-
this.
|
|
368
|
+
// options and tracks whether the data is dirty, see `DataModel`:
|
|
369
|
+
this.ownDataModel = new DataModel({
|
|
359
370
|
component: this,
|
|
360
371
|
getSchema: () => this.schema,
|
|
361
372
|
getData: () => this.data,
|
|
@@ -365,8 +376,8 @@ export default DitoComponent.component('DitoForm', {
|
|
|
365
376
|
|
|
366
377
|
beforeUnmount() {
|
|
367
378
|
// Stop the model before the component's own watchers are stopped, as the
|
|
368
|
-
// model's watchers aren't part of the component, see `
|
|
369
|
-
this.
|
|
379
|
+
// model's watchers aren't part of the component, see `DataModel`.
|
|
380
|
+
this.ownDataModel.stop()
|
|
370
381
|
},
|
|
371
382
|
|
|
372
383
|
methods: {
|
|
@@ -437,9 +448,9 @@ export default DitoComponent.component('DitoForm', {
|
|
|
437
448
|
|
|
438
449
|
// Makes clean changes to the data, which don't make the form dirty, e.g. to
|
|
439
450
|
// apply what an action on the server already saved, see
|
|
440
|
-
// `
|
|
451
|
+
// `DataModel.applyCleanChanges()`.
|
|
441
452
|
applyCleanChanges(makeChanges) {
|
|
442
|
-
return this.
|
|
453
|
+
return this.dataModel.applyCleanChanges(makeChanges)
|
|
443
454
|
},
|
|
444
455
|
|
|
445
456
|
async cancel() {
|
|
@@ -462,7 +473,7 @@ export default DitoComponent.component('DitoForm', {
|
|
|
462
473
|
|
|
463
474
|
async submit(button, { validate = true, closeForm = false } = {}) {
|
|
464
475
|
// Wait for computed values that depend on pending loads, e.g. options.
|
|
465
|
-
await this.waitForPendingLoadsOf(this.
|
|
476
|
+
await this.waitForPendingLoadsOf(this.dataModel)
|
|
466
477
|
if (validate && !this.validateAll()) {
|
|
467
478
|
return false
|
|
468
479
|
}
|
|
@@ -545,7 +556,7 @@ export default DitoComponent.component('DitoForm', {
|
|
|
545
556
|
this.resetValidation()
|
|
546
557
|
// The saved or applied data isn't dirty, also when it isn't replaced,
|
|
547
558
|
// e.g. by a response without data, and before the closing below:
|
|
548
|
-
this.
|
|
559
|
+
this.dataModel
|
|
549
560
|
.takeProcessedDataSnapshot()
|
|
550
561
|
.catch(console.error)
|
|
551
562
|
if (closeForm || button.closeForm) {
|
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
someNestedSchemaComponent
|
|
36
36
|
} from '../utils/schema/structure.js'
|
|
37
37
|
import { hasResource } from '../utils/resource.js'
|
|
38
|
-
import {
|
|
38
|
+
import { DataModel } from '../utils/DataModel.js'
|
|
39
39
|
|
|
40
40
|
// @vue/component
|
|
41
41
|
export default DitoComponent.component('DitoView', {
|
|
@@ -45,7 +45,8 @@ export default DitoComponent.component('DitoView', {
|
|
|
45
45
|
// Redirect $sourceComponent and $resourceComponent to the main component:
|
|
46
46
|
return {
|
|
47
47
|
$sourceComponent: () => this.mainComponent?.sourceComponent || null,
|
|
48
|
-
$resourceComponent: () => this.mainComponent?.resourceComponent || null
|
|
48
|
+
$resourceComponent: () => this.mainComponent?.resourceComponent || null,
|
|
49
|
+
$dataModel: () => this.ownDataModel
|
|
49
50
|
}
|
|
50
51
|
},
|
|
51
52
|
|
|
@@ -60,6 +61,11 @@ export default DitoComponent.component('DitoView', {
|
|
|
60
61
|
},
|
|
61
62
|
|
|
62
63
|
computed: {
|
|
64
|
+
// @override DitoMixin.dataModel
|
|
65
|
+
dataModel() {
|
|
66
|
+
return this.ownDataModel
|
|
67
|
+
},
|
|
68
|
+
|
|
63
69
|
schema() {
|
|
64
70
|
return this.meta.schema ?? {}
|
|
65
71
|
},
|
|
@@ -119,8 +125,8 @@ export default DitoComponent.component('DitoView', {
|
|
|
119
125
|
|
|
120
126
|
created() {
|
|
121
127
|
// Writes defaults and computed values into the view's data and resolves
|
|
122
|
-
// options, see `
|
|
123
|
-
this.
|
|
128
|
+
// options, see `DataModel`:
|
|
129
|
+
this.ownDataModel = new DataModel({
|
|
124
130
|
component: this,
|
|
125
131
|
getSchema: () => this.viewSchema,
|
|
126
132
|
getData: () => this.data
|
|
@@ -136,8 +142,8 @@ export default DitoComponent.component('DitoView', {
|
|
|
136
142
|
|
|
137
143
|
beforeUnmount() {
|
|
138
144
|
// Stop the model before the component's own watchers are stopped, as the
|
|
139
|
-
// model's watchers aren't part of the component, see `
|
|
140
|
-
this.
|
|
145
|
+
// model's watchers aren't part of the component, see `DataModel`.
|
|
146
|
+
this.ownDataModel.stop()
|
|
141
147
|
},
|
|
142
148
|
|
|
143
149
|
methods: {
|
package/src/mixins/DitoMixin.js
CHANGED
|
@@ -32,6 +32,7 @@ export default {
|
|
|
32
32
|
'$sourceComponent',
|
|
33
33
|
'$resourceComponent',
|
|
34
34
|
'$dialogComponent',
|
|
35
|
+
'$dataModel',
|
|
35
36
|
'$panelComponent',
|
|
36
37
|
'$tabComponent'
|
|
37
38
|
],
|
|
@@ -57,6 +58,12 @@ export default {
|
|
|
57
58
|
},
|
|
58
59
|
|
|
59
60
|
computed: {
|
|
61
|
+
// The data model, provided by the form, view or dialog that owns it, which
|
|
62
|
+
// override it with their own.
|
|
63
|
+
dataModel() {
|
|
64
|
+
return this.$dataModel()
|
|
65
|
+
},
|
|
66
|
+
|
|
60
67
|
providesData() {
|
|
61
68
|
// NOTE: This is overridden in ResourceMixin, used by lists.
|
|
62
69
|
return false
|
|
@@ -491,12 +498,12 @@ export default {
|
|
|
491
498
|
: parentComponentPath
|
|
492
499
|
},
|
|
493
500
|
|
|
494
|
-
// Waits for the pending loads of the
|
|
501
|
+
// Waits for the pending loads of the data model, e.g. before submitting,
|
|
495
502
|
// showing the spinner in the header in the meantime, like requests do.
|
|
496
|
-
async waitForPendingLoadsOf(
|
|
503
|
+
async waitForPendingLoadsOf(dataModel) {
|
|
497
504
|
this.rootComponent.registerLoading(true)
|
|
498
505
|
try {
|
|
499
|
-
await
|
|
506
|
+
await dataModel.waitForPendingLoads()
|
|
500
507
|
} finally {
|
|
501
508
|
this.rootComponent.registerLoading(false)
|
|
502
509
|
}
|
|
@@ -615,17 +622,23 @@ export default {
|
|
|
615
622
|
: handleParentListeners(undefined)
|
|
616
623
|
|
|
617
624
|
return ['load', 'change'].includes(event)
|
|
618
|
-
? // The effects of
|
|
619
|
-
//
|
|
620
|
-
//
|
|
621
|
-
//
|
|
622
|
-
// NOTE: The result of `handleListeners()`
|
|
623
|
-
|
|
624
|
-
this.$nextTick(handleListeners)
|
|
625
|
+
? // The effects of these events need time to propagate, so that the
|
|
626
|
+
// handlers see them: Components that are newly rendered due to
|
|
627
|
+
// data changes register, e.g. for `processedItem`, and the form
|
|
628
|
+
// model writes the values that it derives from the data.
|
|
629
|
+
// NOTE: The result of `handleListeners()` is returned as expected.
|
|
630
|
+
this.waitForDataModel().then(handleListeners)
|
|
625
631
|
: handleListeners()
|
|
626
632
|
}
|
|
627
633
|
},
|
|
628
634
|
|
|
635
|
+
// Waits until the data model settled: its loads finished, and it wrote the
|
|
636
|
+
// values that it derives from the data.
|
|
637
|
+
async waitForDataModel() {
|
|
638
|
+
await this.$nextTick()
|
|
639
|
+
await this.dataModel?.waitForPendingLoads()
|
|
640
|
+
},
|
|
641
|
+
|
|
629
642
|
emitSchemaEvent(event, params) {
|
|
630
643
|
return this.schemaComponent.emitEvent(event, params)
|
|
631
644
|
}
|
|
@@ -82,12 +82,12 @@ export default {
|
|
|
82
82
|
return this.getOptionForValue(this.selectedValue)
|
|
83
83
|
},
|
|
84
84
|
|
|
85
|
-
// The resolver of the options in the
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
//
|
|
85
|
+
// The resolver of the options in the data model, which loads them and
|
|
86
|
+
// shares them with the computes of the form. It is looked up with the entry
|
|
87
|
+
// of this component, in the shape of the entries of `processSchemaData()`,
|
|
88
|
+
// see `DataModel`.
|
|
89
89
|
optionsResolver() {
|
|
90
|
-
return this.
|
|
90
|
+
return this.dataModel.getOptionsResolver({
|
|
91
91
|
schema: this.schema,
|
|
92
92
|
data: this.data,
|
|
93
93
|
name: this.name,
|
|
@@ -247,7 +247,7 @@ export default {
|
|
|
247
247
|
// made as a clean change, which doesn't make the form dirty.
|
|
248
248
|
replaceValueWithSelectedValue() {
|
|
249
249
|
const { selectedValue } = this
|
|
250
|
-
this.
|
|
250
|
+
this.dataModel
|
|
251
251
|
.applyCleanChanges(() => {
|
|
252
252
|
this.selectedValue = selectedValue
|
|
253
253
|
})
|
package/src/mixins/RouteMixin.js
CHANGED
|
@@ -129,7 +129,7 @@ export default {
|
|
|
129
129
|
// `DitoRouterView`. Records of other paths, or none, mean that
|
|
130
130
|
// the component is being left, and it keeps its record until it's
|
|
131
131
|
// unmounted, as its schema and meta are still read in the meantime,
|
|
132
|
-
// e.g. by the watchers of its
|
|
132
|
+
// e.g. by the watchers of its data model. Records of the same path
|
|
133
133
|
// replace it, e.g. when the routes are set up again.
|
|
134
134
|
const isOwnRouteRecord = (
|
|
135
135
|
!!routeRecord &&
|
|
@@ -481,7 +481,7 @@ export default {
|
|
|
481
481
|
// their order key with the offset of the loaded page, so that their order
|
|
482
482
|
// can be stored even if it never changes, e.g. by a button that saves the
|
|
483
483
|
// current order. Lists in the data of forms, views and dialogs are
|
|
484
|
-
// numbered by `
|
|
484
|
+
// numbered by `DataModel`, see `initializeData()`.
|
|
485
485
|
setLoadedListItems(items) {
|
|
486
486
|
this.setLoadedValue(
|
|
487
487
|
updateOrder(this.sourceSchema, items, this.paginationRange)
|
|
@@ -491,9 +491,9 @@ export default {
|
|
|
491
491
|
// Writes the value loaded through the source's resource. Loading isn't an
|
|
492
492
|
// edit of the data, so the value is written as a clean change, which
|
|
493
493
|
// doesn't make the form dirty, e.g. for sources whose value is stored,
|
|
494
|
-
// like the order of their items, see `
|
|
494
|
+
// like the order of their items, see `DataModel.applyCleanChanges()`.
|
|
495
495
|
setLoadedValue(value) {
|
|
496
|
-
this.
|
|
496
|
+
this.dataModel
|
|
497
497
|
.applyCleanChanges(() => {
|
|
498
498
|
this.value = value
|
|
499
499
|
})
|
package/src/mixins/TypeMixin.js
CHANGED
|
@@ -37,11 +37,6 @@ export default {
|
|
|
37
37
|
return this.schema.name
|
|
38
38
|
},
|
|
39
39
|
|
|
40
|
-
// The form model of the data, owned by the dialog or route component.
|
|
41
|
-
formModel() {
|
|
42
|
-
return (this.dialogComponent ?? this.routeComponent).formModel
|
|
43
|
-
},
|
|
44
|
-
|
|
45
40
|
type() {
|
|
46
41
|
return this.schema.type
|
|
47
42
|
},
|
|
@@ -197,17 +192,17 @@ export default {
|
|
|
197
192
|
},
|
|
198
193
|
|
|
199
194
|
methods: {
|
|
200
|
-
// Only the
|
|
195
|
+
// Only the data model calls `compute()`, see `computeValue()`. Components
|
|
201
196
|
// that it doesn't visit, e.g. in panels with their own data, never get
|
|
202
197
|
// their values computed.
|
|
203
198
|
warnAboutUncomputedValue() {
|
|
204
199
|
if (
|
|
205
200
|
this.schema.compute &&
|
|
206
|
-
!this.
|
|
201
|
+
!this.dataModel.hasComputedValueEntry(this.componentPath)
|
|
207
202
|
) {
|
|
208
203
|
console.warn(
|
|
209
204
|
`The value of the component at '${this.componentPath}' isn't ` +
|
|
210
|
-
`computed, as its data isn't part of a
|
|
205
|
+
`computed, as its data isn't part of a data model.`
|
|
211
206
|
)
|
|
212
207
|
}
|
|
213
208
|
},
|
package/src/mixins/ValueMixin.js
CHANGED
|
@@ -20,7 +20,7 @@ export default DitoTypeComponent.register(
|
|
|
20
20
|
defaultValue: () => undefined, // Callback to override `defaultValue: null`
|
|
21
21
|
defaultVisible: false,
|
|
22
22
|
// The value is resolved from `schema.data` or `schema.dataPath` by
|
|
23
|
-
// `
|
|
23
|
+
// `DataModel`, see `hasValueFromDataSchema()`:
|
|
24
24
|
valueFromDataSchema: true
|
|
25
25
|
}
|
|
26
26
|
)
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
processData
|
|
23
23
|
} from './schema/data.js'
|
|
24
24
|
|
|
25
|
-
//
|
|
25
|
+
// DataModel holds the state of the data edited by a form, view or dialog that
|
|
26
26
|
// is derived from schema and data, independently of what is rendered:
|
|
27
27
|
//
|
|
28
28
|
// - Missing values are set to their defaults when the data is set up, and when
|
|
@@ -39,7 +39,7 @@ import {
|
|
|
39
39
|
// - Options, `schema.options`, are resolved per component when they are first
|
|
40
40
|
// read, by `compute()` through `context.options` or by the component that
|
|
41
41
|
// displays them, see `getOptions()`. Both get the same option objects, and
|
|
42
|
-
// computes that read options run
|
|
42
|
+
// computes that read options only run once they are loaded.
|
|
43
43
|
// - Submitting waits for the loads of data schemas and options that are still
|
|
44
44
|
// pending, see `waitForPendingLoads()`.
|
|
45
45
|
// - The data is dirty when its processed data differs from a snapshot taken
|
|
@@ -60,7 +60,7 @@ import {
|
|
|
60
60
|
// `dataPath`, `componentPath`, `rootData` and `mainSchemaComponent` are used
|
|
61
61
|
// when present. The model needs to be stopped before the component unmounts.
|
|
62
62
|
|
|
63
|
-
export class
|
|
63
|
+
export class DataModel {
|
|
64
64
|
// The entries of the components with computed values and the scopes of the
|
|
65
65
|
// watchers that write these values into the data, by component path:
|
|
66
66
|
computedValueRecords = new Map()
|
|
@@ -651,7 +651,7 @@ const notFound = Symbol('notFound')
|
|
|
651
651
|
const modelWatchOptions = { immediate: true, flush: 'post' }
|
|
652
652
|
|
|
653
653
|
// Aborts `compute()` when it reads options that aren't loaded, see
|
|
654
|
-
// `
|
|
654
|
+
// `DataModel.getComputedValueResult()`.
|
|
655
655
|
const optionsNotLoaded = Symbol('optionsNotLoaded')
|
|
656
656
|
|
|
657
657
|
// Returns whether the schema is a source of computed values, through
|
package/src/utils/schema/data.js
CHANGED
|
@@ -129,7 +129,7 @@ export function shouldIgnoreMissingValue(schema, context) {
|
|
|
129
129
|
// the type treats that as missing, e.g. nested sections.
|
|
130
130
|
export function isMissingValue(schema, data, name, context) {
|
|
131
131
|
// Only read the value if needed, so that callers that are tracked, e.g. the
|
|
132
|
-
// walk of the
|
|
132
|
+
// walk of the data model's data, don't depend on all values.
|
|
133
133
|
return (
|
|
134
134
|
!(name in data) || (
|
|
135
135
|
!!getTypeOptions(schema)?.treatNullAsMissing?.(getContext(context)) &&
|
|
@@ -142,7 +142,7 @@ export function isMissingValue(schema, data, name, context) {
|
|
|
142
142
|
* Returns whether the value of the component described by `schema` is
|
|
143
143
|
* resolved from its data schema, `schema.data` or `schema.dataPath`, as for
|
|
144
144
|
* the types with the `valueFromDataSchema` option, e.g. `computed`. See
|
|
145
|
-
* `
|
|
145
|
+
* `DataModel`.
|
|
146
146
|
*/
|
|
147
147
|
export function hasValueFromDataSchema(schema) {
|
|
148
148
|
return (
|
|
@@ -227,7 +227,7 @@ export function initializeData(schema, data = {}, component, {
|
|
|
227
227
|
* Returns the value of the component described by `schema` and `name` in
|
|
228
228
|
* `data`: the result of `schema.compute()` if it returns a value, else the
|
|
229
229
|
* value in `data`, or its default if it's missing. Never writes into `data`:
|
|
230
|
-
* `
|
|
230
|
+
* `DataModel` is the only caller and writes the computed values, including the
|
|
231
231
|
* defaults of components with `compute()`. Other defaults are written when the
|
|
232
232
|
* data is set up, see `initializeData()`, or when they go missing later. If
|
|
233
233
|
* provided, `getOptions()` returns the options for `context.options`, called
|
|
@@ -265,7 +265,7 @@ export function computeValue(schema, data, name, dataPath, {
|
|
|
265
265
|
return properties
|
|
266
266
|
})
|
|
267
267
|
const { compute } = schema
|
|
268
|
-
// Like the
|
|
268
|
+
// Like the data model, only compute values of components that are shown
|
|
269
269
|
// through their `if`, as `compute()` may rely on the same conditions, e.g.
|
|
270
270
|
// `if: ({ item }) => item.preview` with `compute: ({ item }) =>
|
|
271
271
|
// item.preview.name`.
|
|
@@ -288,7 +288,7 @@ export function computeValue(schema, data, name, dataPath, {
|
|
|
288
288
|
|
|
289
289
|
// Returns the value in `data`, or its default if it's missing. `context` can be
|
|
290
290
|
// a function that creates the context, called only when it's needed. Computed
|
|
291
|
-
// values are in `data` already, as `
|
|
291
|
+
// values are in `data` already, as `DataModel` writes them, see
|
|
292
292
|
// `computeValue()`.
|
|
293
293
|
export function getValueOrDefault(schema, data, name, context) {
|
|
294
294
|
const shouldUseDefault = (
|
|
@@ -325,11 +325,11 @@ export function processData(schema, sourceSchema, data, dataPath, {
|
|
|
325
325
|
schemaOnly, // whether to only include data covered by the schema, or all data
|
|
326
326
|
target,
|
|
327
327
|
// Whether to call the schema callbacks `process()`. Data that is compared
|
|
328
|
-
// while it's edited, e.g. by `
|
|
328
|
+
// while it's edited, e.g. by `DataModel.isDirty`, isn't validated yet, which
|
|
329
329
|
// the callbacks may rely on. Without them, the types still process the values
|
|
330
330
|
// through `processValue()`, and the excluded values of components with
|
|
331
331
|
// `process()` are kept, as `process()` may store them elsewhere through
|
|
332
|
-
// `processedItem`. Computed values are in the data already, as `
|
|
332
|
+
// `processedItem`. Computed values are in the data already, as `DataModel`
|
|
333
333
|
// writes them, see `computeValue()`.
|
|
334
334
|
shouldCallProcess = true
|
|
335
335
|
} = {}) {
|
|
@@ -558,7 +558,7 @@ export function processSchemaData(schema, data, {
|
|
|
558
558
|
before?.(entry)
|
|
559
559
|
|
|
560
560
|
// Only read the values of sources with forms, so that callers that
|
|
561
|
-
// are tracked, e.g. the walk of the
|
|
561
|
+
// are tracked, e.g. the walk of the data model's data, don't depend
|
|
562
562
|
// on all values.
|
|
563
563
|
if (hasFormSchema(componentSchema)) {
|
|
564
564
|
let value = processedData ? processedData[name] : data[name]
|
|
@@ -112,7 +112,7 @@ export function getFormSchemas(schema, context, modifyForm) {
|
|
|
112
112
|
// Returns the form with the components that its `components()` callback
|
|
113
113
|
// creates for the data of `context`. The forms are cached per data, and the
|
|
114
114
|
// callback only creates new components when the data that it reads changes,
|
|
115
|
-
// so that rendering and the walks of the
|
|
115
|
+
// so that rendering and the walks of the data model, see `DataModel`, share
|
|
116
116
|
// the same component schemas. `components()` callbacks therefore derive the
|
|
117
117
|
// components from the data of their context, not from its components.
|
|
118
118
|
function getFormWithCreatedComponents(form, context) {
|
package/types/index.d.ts
CHANGED
|
@@ -1092,14 +1092,17 @@ export interface SchemaFields<$Item> {
|
|
|
1092
1092
|
onDestroy?: ItemEventHandler<$Item>
|
|
1093
1093
|
/**
|
|
1094
1094
|
* Called after data has been fetched from the API.
|
|
1095
|
-
* Fires after all reactive updates have propagated
|
|
1095
|
+
* Fires after all reactive updates have propagated,
|
|
1096
|
+
* once the values derived from the data are written,
|
|
1097
|
+
* including the ones that depend on options to load.
|
|
1096
1098
|
*/
|
|
1097
1099
|
onLoad?: ItemEventHandler<$Item>
|
|
1098
1100
|
/**
|
|
1099
1101
|
* Called after a value change is committed. Fires
|
|
1100
|
-
* after all reactive updates have propagated
|
|
1101
|
-
*
|
|
1102
|
-
*
|
|
1102
|
+
* after all reactive updates have propagated, once the
|
|
1103
|
+
* values derived from the change are written, including
|
|
1104
|
+
* the ones that depend on options to load. Bubbles to
|
|
1105
|
+
* parent schemas — return `false` to stop propagation.
|
|
1103
1106
|
*/
|
|
1104
1107
|
onChange?: ItemEventHandler<$Item>
|
|
1105
1108
|
}
|