@angular/forms 21.0.0-next.0 → 21.0.0-next.10
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/fesm2022/forms.mjs +4852 -7211
- package/fesm2022/forms.mjs.map +1 -1
- package/fesm2022/signals.mjs +2056 -0
- package/fesm2022/signals.mjs.map +1 -0
- package/package.json +11 -6
- package/{index.d.ts → types/forms.d.ts} +164 -56
- package/types/signals.d.ts +2725 -0
|
@@ -0,0 +1,2056 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Angular v21.0.0-next.10
|
|
3
|
+
* (c) 2010-2025 Google LLC. https://angular.dev/
|
|
4
|
+
* License: MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { httpResource } from '@angular/common/http';
|
|
8
|
+
import * as i0 from '@angular/core';
|
|
9
|
+
import { computed, untracked, InjectionToken, inject, Injector, input, ɵCONTROL as _CONTROL, effect, Directive, runInInjectionContext, linkedSignal, signal, APP_ID, ɵisPromise as _isPromise, resource } from '@angular/core';
|
|
10
|
+
import { Validators, NG_VALUE_ACCESSOR, NgControl } from '@angular/forms';
|
|
11
|
+
import { SIGNAL } from '@angular/core/primitives/signals';
|
|
12
|
+
|
|
13
|
+
function isArray(value) {
|
|
14
|
+
return Array.isArray(value);
|
|
15
|
+
}
|
|
16
|
+
function isObject(value) {
|
|
17
|
+
return (typeof value === 'object' || typeof value === 'function') && value != null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function reduceChildren(node, initialValue, fn, shortCircuit) {
|
|
21
|
+
const childrenMap = node.structure.childrenMap();
|
|
22
|
+
if (!childrenMap) {
|
|
23
|
+
return initialValue;
|
|
24
|
+
}
|
|
25
|
+
let value = initialValue;
|
|
26
|
+
for (const child of childrenMap.values()) {
|
|
27
|
+
if (shortCircuit?.(value)) {
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
value = fn(child, value);
|
|
31
|
+
}
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
function shortCircuitFalse(value) {
|
|
35
|
+
return !value;
|
|
36
|
+
}
|
|
37
|
+
function shortCircuitTrue(value) {
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function calculateValidationSelfStatus(state) {
|
|
42
|
+
if (state.errors().length > 0) {
|
|
43
|
+
return 'invalid';
|
|
44
|
+
}
|
|
45
|
+
if (state.pending()) {
|
|
46
|
+
return 'unknown';
|
|
47
|
+
}
|
|
48
|
+
return 'valid';
|
|
49
|
+
}
|
|
50
|
+
class FieldValidationState {
|
|
51
|
+
node;
|
|
52
|
+
constructor(node) {
|
|
53
|
+
this.node = node;
|
|
54
|
+
}
|
|
55
|
+
rawSyncTreeErrors = computed(() => {
|
|
56
|
+
if (this.shouldSkipValidation()) {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
return [...this.node.logicNode.logic.syncTreeErrors.compute(this.node.context), ...(this.node.structure.parent?.validationState.rawSyncTreeErrors() ?? [])];
|
|
60
|
+
}, ...(ngDevMode ? [{
|
|
61
|
+
debugName: "rawSyncTreeErrors"
|
|
62
|
+
}] : []));
|
|
63
|
+
syncErrors = computed(() => {
|
|
64
|
+
if (this.shouldSkipValidation()) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
return [...this.node.logicNode.logic.syncErrors.compute(this.node.context), ...this.syncTreeErrors(), ...normalizeErrors(this.node.submitState.serverErrors())];
|
|
68
|
+
}, ...(ngDevMode ? [{
|
|
69
|
+
debugName: "syncErrors"
|
|
70
|
+
}] : []));
|
|
71
|
+
syncValid = computed(() => {
|
|
72
|
+
if (this.shouldSkipValidation()) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
return reduceChildren(this.node, this.syncErrors().length === 0, (child, value) => value && child.validationState.syncValid(), shortCircuitFalse);
|
|
76
|
+
}, ...(ngDevMode ? [{
|
|
77
|
+
debugName: "syncValid"
|
|
78
|
+
}] : []));
|
|
79
|
+
syncTreeErrors = computed(() => this.rawSyncTreeErrors().filter(err => err.field === this.node.fieldProxy), ...(ngDevMode ? [{
|
|
80
|
+
debugName: "syncTreeErrors"
|
|
81
|
+
}] : []));
|
|
82
|
+
rawAsyncErrors = computed(() => {
|
|
83
|
+
if (this.shouldSkipValidation()) {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
return [...this.node.logicNode.logic.asyncErrors.compute(this.node.context), ...(this.node.structure.parent?.validationState.rawAsyncErrors() ?? [])];
|
|
87
|
+
}, ...(ngDevMode ? [{
|
|
88
|
+
debugName: "rawAsyncErrors"
|
|
89
|
+
}] : []));
|
|
90
|
+
asyncErrors = computed(() => {
|
|
91
|
+
if (this.shouldSkipValidation()) {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
return this.rawAsyncErrors().filter(err => err === 'pending' || err.field === this.node.fieldProxy);
|
|
95
|
+
}, ...(ngDevMode ? [{
|
|
96
|
+
debugName: "asyncErrors"
|
|
97
|
+
}] : []));
|
|
98
|
+
errors = computed(() => [...this.syncErrors(), ...this.asyncErrors().filter(err => err !== 'pending')], ...(ngDevMode ? [{
|
|
99
|
+
debugName: "errors"
|
|
100
|
+
}] : []));
|
|
101
|
+
errorSummary = computed(() => reduceChildren(this.node, this.errors(), (child, result) => [...result, ...child.errorSummary()]), ...(ngDevMode ? [{
|
|
102
|
+
debugName: "errorSummary"
|
|
103
|
+
}] : []));
|
|
104
|
+
pending = computed(() => reduceChildren(this.node, this.asyncErrors().includes('pending'), (child, value) => value || child.validationState.asyncErrors().includes('pending')), ...(ngDevMode ? [{
|
|
105
|
+
debugName: "pending"
|
|
106
|
+
}] : []));
|
|
107
|
+
status = computed(() => {
|
|
108
|
+
if (this.shouldSkipValidation()) {
|
|
109
|
+
return 'valid';
|
|
110
|
+
}
|
|
111
|
+
let ownStatus = calculateValidationSelfStatus(this);
|
|
112
|
+
return reduceChildren(this.node, ownStatus, (child, value) => {
|
|
113
|
+
if (value === 'invalid' || child.validationState.status() === 'invalid') {
|
|
114
|
+
return 'invalid';
|
|
115
|
+
} else if (value === 'unknown' || child.validationState.status() === 'unknown') {
|
|
116
|
+
return 'unknown';
|
|
117
|
+
}
|
|
118
|
+
return 'valid';
|
|
119
|
+
}, v => v === 'invalid');
|
|
120
|
+
}, ...(ngDevMode ? [{
|
|
121
|
+
debugName: "status"
|
|
122
|
+
}] : []));
|
|
123
|
+
valid = computed(() => this.status() === 'valid', ...(ngDevMode ? [{
|
|
124
|
+
debugName: "valid"
|
|
125
|
+
}] : []));
|
|
126
|
+
invalid = computed(() => this.status() === 'invalid', ...(ngDevMode ? [{
|
|
127
|
+
debugName: "invalid"
|
|
128
|
+
}] : []));
|
|
129
|
+
shouldSkipValidation = computed(() => this.node.hidden() || this.node.disabled() || this.node.readonly(), ...(ngDevMode ? [{
|
|
130
|
+
debugName: "shouldSkipValidation"
|
|
131
|
+
}] : []));
|
|
132
|
+
}
|
|
133
|
+
function normalizeErrors(error) {
|
|
134
|
+
if (error === undefined) {
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
if (isArray(error)) {
|
|
138
|
+
return error;
|
|
139
|
+
}
|
|
140
|
+
return [error];
|
|
141
|
+
}
|
|
142
|
+
function addDefaultField(errors, field) {
|
|
143
|
+
if (isArray(errors)) {
|
|
144
|
+
for (const error of errors) {
|
|
145
|
+
error.field ??= field;
|
|
146
|
+
}
|
|
147
|
+
} else if (errors) {
|
|
148
|
+
errors.field ??= field;
|
|
149
|
+
}
|
|
150
|
+
return errors;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const DYNAMIC = Symbol();
|
|
154
|
+
const IGNORED = Symbol();
|
|
155
|
+
class AbstractLogic {
|
|
156
|
+
predicates;
|
|
157
|
+
fns = [];
|
|
158
|
+
constructor(predicates) {
|
|
159
|
+
this.predicates = predicates;
|
|
160
|
+
}
|
|
161
|
+
push(logicFn) {
|
|
162
|
+
this.fns.push(wrapWithPredicates(this.predicates, logicFn));
|
|
163
|
+
}
|
|
164
|
+
mergeIn(other) {
|
|
165
|
+
const fns = this.predicates ? other.fns.map(fn => wrapWithPredicates(this.predicates, fn)) : other.fns;
|
|
166
|
+
this.fns.push(...fns);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
class BooleanOrLogic extends AbstractLogic {
|
|
170
|
+
get defaultValue() {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
compute(arg) {
|
|
174
|
+
return this.fns.some(f => {
|
|
175
|
+
const result = f(arg);
|
|
176
|
+
return result && result !== IGNORED;
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
class ArrayMergeIgnoreLogic extends AbstractLogic {
|
|
181
|
+
ignore;
|
|
182
|
+
static ignoreNull(predicates) {
|
|
183
|
+
return new ArrayMergeIgnoreLogic(predicates, e => e === null);
|
|
184
|
+
}
|
|
185
|
+
constructor(predicates, ignore) {
|
|
186
|
+
super(predicates);
|
|
187
|
+
this.ignore = ignore;
|
|
188
|
+
}
|
|
189
|
+
get defaultValue() {
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
192
|
+
compute(arg) {
|
|
193
|
+
return this.fns.reduce((prev, f) => {
|
|
194
|
+
const value = f(arg);
|
|
195
|
+
if (value === undefined || value === IGNORED) {
|
|
196
|
+
return prev;
|
|
197
|
+
} else if (isArray(value)) {
|
|
198
|
+
return [...prev, ...(this.ignore ? value.filter(e => !this.ignore(e)) : value)];
|
|
199
|
+
} else {
|
|
200
|
+
if (this.ignore && this.ignore(value)) {
|
|
201
|
+
return prev;
|
|
202
|
+
}
|
|
203
|
+
return [...prev, value];
|
|
204
|
+
}
|
|
205
|
+
}, []);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
class ArrayMergeLogic extends ArrayMergeIgnoreLogic {
|
|
209
|
+
constructor(predicates) {
|
|
210
|
+
super(predicates, undefined);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
class AggregateMetadataMergeLogic extends AbstractLogic {
|
|
214
|
+
key;
|
|
215
|
+
get defaultValue() {
|
|
216
|
+
return this.key.getInitial();
|
|
217
|
+
}
|
|
218
|
+
constructor(predicates, key) {
|
|
219
|
+
super(predicates);
|
|
220
|
+
this.key = key;
|
|
221
|
+
}
|
|
222
|
+
compute(ctx) {
|
|
223
|
+
if (this.fns.length === 0) {
|
|
224
|
+
return this.key.getInitial();
|
|
225
|
+
}
|
|
226
|
+
let acc = this.key.getInitial();
|
|
227
|
+
for (let i = 0; i < this.fns.length; i++) {
|
|
228
|
+
const item = this.fns[i](ctx);
|
|
229
|
+
if (item !== IGNORED) {
|
|
230
|
+
acc = this.key.reduce(acc, item);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return acc;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function wrapWithPredicates(predicates, logicFn) {
|
|
237
|
+
if (predicates.length === 0) {
|
|
238
|
+
return logicFn;
|
|
239
|
+
}
|
|
240
|
+
return arg => {
|
|
241
|
+
for (const predicate of predicates) {
|
|
242
|
+
let predicateField = arg.stateOf(predicate.path);
|
|
243
|
+
const depthDiff = untracked(predicateField.structure.pathKeys).length - predicate.depth;
|
|
244
|
+
for (let i = 0; i < depthDiff; i++) {
|
|
245
|
+
predicateField = predicateField.structure.parent;
|
|
246
|
+
}
|
|
247
|
+
if (!predicate.fn(predicateField.context)) {
|
|
248
|
+
return IGNORED;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return logicFn(arg);
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
class LogicContainer {
|
|
255
|
+
predicates;
|
|
256
|
+
hidden;
|
|
257
|
+
disabledReasons;
|
|
258
|
+
readonly;
|
|
259
|
+
syncErrors;
|
|
260
|
+
syncTreeErrors;
|
|
261
|
+
asyncErrors;
|
|
262
|
+
aggregateMetadataKeys = new Map();
|
|
263
|
+
metadataFactories = new Map();
|
|
264
|
+
constructor(predicates) {
|
|
265
|
+
this.predicates = predicates;
|
|
266
|
+
this.hidden = new BooleanOrLogic(predicates);
|
|
267
|
+
this.disabledReasons = new ArrayMergeLogic(predicates);
|
|
268
|
+
this.readonly = new BooleanOrLogic(predicates);
|
|
269
|
+
this.syncErrors = ArrayMergeIgnoreLogic.ignoreNull(predicates);
|
|
270
|
+
this.syncTreeErrors = ArrayMergeIgnoreLogic.ignoreNull(predicates);
|
|
271
|
+
this.asyncErrors = ArrayMergeIgnoreLogic.ignoreNull(predicates);
|
|
272
|
+
}
|
|
273
|
+
hasAggregateMetadata(key) {
|
|
274
|
+
return this.aggregateMetadataKeys.has(key);
|
|
275
|
+
}
|
|
276
|
+
getAggregateMetadataEntries() {
|
|
277
|
+
return this.aggregateMetadataKeys.entries();
|
|
278
|
+
}
|
|
279
|
+
getMetadataFactoryEntries() {
|
|
280
|
+
return this.metadataFactories.entries();
|
|
281
|
+
}
|
|
282
|
+
getAggregateMetadata(key) {
|
|
283
|
+
if (!this.aggregateMetadataKeys.has(key)) {
|
|
284
|
+
this.aggregateMetadataKeys.set(key, new AggregateMetadataMergeLogic(this.predicates, key));
|
|
285
|
+
}
|
|
286
|
+
return this.aggregateMetadataKeys.get(key);
|
|
287
|
+
}
|
|
288
|
+
addMetadataFactory(key, factory) {
|
|
289
|
+
if (this.metadataFactories.has(key)) {
|
|
290
|
+
throw new Error(`Can't define value twice for the same MetadataKey`);
|
|
291
|
+
}
|
|
292
|
+
this.metadataFactories.set(key, factory);
|
|
293
|
+
}
|
|
294
|
+
mergeIn(other) {
|
|
295
|
+
this.hidden.mergeIn(other.hidden);
|
|
296
|
+
this.disabledReasons.mergeIn(other.disabledReasons);
|
|
297
|
+
this.readonly.mergeIn(other.readonly);
|
|
298
|
+
this.syncErrors.mergeIn(other.syncErrors);
|
|
299
|
+
this.syncTreeErrors.mergeIn(other.syncTreeErrors);
|
|
300
|
+
this.asyncErrors.mergeIn(other.asyncErrors);
|
|
301
|
+
for (const [key, metadataLogic] of other.getAggregateMetadataEntries()) {
|
|
302
|
+
this.getAggregateMetadata(key).mergeIn(metadataLogic);
|
|
303
|
+
}
|
|
304
|
+
for (const [key, metadataFactory] of other.getMetadataFactoryEntries()) {
|
|
305
|
+
this.addMetadataFactory(key, metadataFactory);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
let boundPathDepth = 0;
|
|
311
|
+
function getBoundPathDepth() {
|
|
312
|
+
return boundPathDepth;
|
|
313
|
+
}
|
|
314
|
+
function setBoundPathDepthForResolution(fn, depth) {
|
|
315
|
+
return (...args) => {
|
|
316
|
+
try {
|
|
317
|
+
boundPathDepth = depth;
|
|
318
|
+
return fn(...args);
|
|
319
|
+
} finally {
|
|
320
|
+
boundPathDepth = 0;
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
class AbstractLogicNodeBuilder {
|
|
326
|
+
depth;
|
|
327
|
+
constructor(depth) {
|
|
328
|
+
this.depth = depth;
|
|
329
|
+
}
|
|
330
|
+
build() {
|
|
331
|
+
return new LeafLogicNode(this, [], 0);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
class LogicNodeBuilder extends AbstractLogicNodeBuilder {
|
|
335
|
+
constructor(depth) {
|
|
336
|
+
super(depth);
|
|
337
|
+
}
|
|
338
|
+
current;
|
|
339
|
+
all = [];
|
|
340
|
+
addHiddenRule(logic) {
|
|
341
|
+
this.getCurrent().addHiddenRule(logic);
|
|
342
|
+
}
|
|
343
|
+
addDisabledReasonRule(logic) {
|
|
344
|
+
this.getCurrent().addDisabledReasonRule(logic);
|
|
345
|
+
}
|
|
346
|
+
addReadonlyRule(logic) {
|
|
347
|
+
this.getCurrent().addReadonlyRule(logic);
|
|
348
|
+
}
|
|
349
|
+
addSyncErrorRule(logic) {
|
|
350
|
+
this.getCurrent().addSyncErrorRule(logic);
|
|
351
|
+
}
|
|
352
|
+
addSyncTreeErrorRule(logic) {
|
|
353
|
+
this.getCurrent().addSyncTreeErrorRule(logic);
|
|
354
|
+
}
|
|
355
|
+
addAsyncErrorRule(logic) {
|
|
356
|
+
this.getCurrent().addAsyncErrorRule(logic);
|
|
357
|
+
}
|
|
358
|
+
addAggregateMetadataRule(key, logic) {
|
|
359
|
+
this.getCurrent().addAggregateMetadataRule(key, logic);
|
|
360
|
+
}
|
|
361
|
+
addMetadataFactory(key, factory) {
|
|
362
|
+
this.getCurrent().addMetadataFactory(key, factory);
|
|
363
|
+
}
|
|
364
|
+
getChild(key) {
|
|
365
|
+
return this.getCurrent().getChild(key);
|
|
366
|
+
}
|
|
367
|
+
hasLogic(builder) {
|
|
368
|
+
if (this === builder) {
|
|
369
|
+
return true;
|
|
370
|
+
}
|
|
371
|
+
return this.all.some(({
|
|
372
|
+
builder: subBuilder
|
|
373
|
+
}) => subBuilder.hasLogic(builder));
|
|
374
|
+
}
|
|
375
|
+
mergeIn(other, predicate) {
|
|
376
|
+
if (predicate) {
|
|
377
|
+
this.all.push({
|
|
378
|
+
builder: other,
|
|
379
|
+
predicate: {
|
|
380
|
+
fn: setBoundPathDepthForResolution(predicate.fn, this.depth),
|
|
381
|
+
path: predicate.path
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
} else {
|
|
385
|
+
this.all.push({
|
|
386
|
+
builder: other
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
this.current = undefined;
|
|
390
|
+
}
|
|
391
|
+
getCurrent() {
|
|
392
|
+
if (this.current === undefined) {
|
|
393
|
+
this.current = new NonMergeableLogicNodeBuilder(this.depth);
|
|
394
|
+
this.all.push({
|
|
395
|
+
builder: this.current
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
return this.current;
|
|
399
|
+
}
|
|
400
|
+
static newRoot() {
|
|
401
|
+
return new LogicNodeBuilder(0);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
class NonMergeableLogicNodeBuilder extends AbstractLogicNodeBuilder {
|
|
405
|
+
logic = new LogicContainer([]);
|
|
406
|
+
children = new Map();
|
|
407
|
+
constructor(depth) {
|
|
408
|
+
super(depth);
|
|
409
|
+
}
|
|
410
|
+
addHiddenRule(logic) {
|
|
411
|
+
this.logic.hidden.push(setBoundPathDepthForResolution(logic, this.depth));
|
|
412
|
+
}
|
|
413
|
+
addDisabledReasonRule(logic) {
|
|
414
|
+
this.logic.disabledReasons.push(setBoundPathDepthForResolution(logic, this.depth));
|
|
415
|
+
}
|
|
416
|
+
addReadonlyRule(logic) {
|
|
417
|
+
this.logic.readonly.push(setBoundPathDepthForResolution(logic, this.depth));
|
|
418
|
+
}
|
|
419
|
+
addSyncErrorRule(logic) {
|
|
420
|
+
this.logic.syncErrors.push(setBoundPathDepthForResolution(logic, this.depth));
|
|
421
|
+
}
|
|
422
|
+
addSyncTreeErrorRule(logic) {
|
|
423
|
+
this.logic.syncTreeErrors.push(setBoundPathDepthForResolution(logic, this.depth));
|
|
424
|
+
}
|
|
425
|
+
addAsyncErrorRule(logic) {
|
|
426
|
+
this.logic.asyncErrors.push(setBoundPathDepthForResolution(logic, this.depth));
|
|
427
|
+
}
|
|
428
|
+
addAggregateMetadataRule(key, logic) {
|
|
429
|
+
this.logic.getAggregateMetadata(key).push(setBoundPathDepthForResolution(logic, this.depth));
|
|
430
|
+
}
|
|
431
|
+
addMetadataFactory(key, factory) {
|
|
432
|
+
this.logic.addMetadataFactory(key, setBoundPathDepthForResolution(factory, this.depth));
|
|
433
|
+
}
|
|
434
|
+
getChild(key) {
|
|
435
|
+
if (!this.children.has(key)) {
|
|
436
|
+
this.children.set(key, new LogicNodeBuilder(this.depth + 1));
|
|
437
|
+
}
|
|
438
|
+
return this.children.get(key);
|
|
439
|
+
}
|
|
440
|
+
hasLogic(builder) {
|
|
441
|
+
return this === builder;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
class LeafLogicNode {
|
|
445
|
+
builder;
|
|
446
|
+
predicates;
|
|
447
|
+
depth;
|
|
448
|
+
logic;
|
|
449
|
+
constructor(builder, predicates, depth) {
|
|
450
|
+
this.builder = builder;
|
|
451
|
+
this.predicates = predicates;
|
|
452
|
+
this.depth = depth;
|
|
453
|
+
this.logic = builder ? createLogic(builder, predicates, depth) : new LogicContainer([]);
|
|
454
|
+
}
|
|
455
|
+
getChild(key) {
|
|
456
|
+
const childBuilders = this.builder ? getAllChildBuilders(this.builder, key) : [];
|
|
457
|
+
if (childBuilders.length === 0) {
|
|
458
|
+
return new LeafLogicNode(undefined, [], this.depth + 1);
|
|
459
|
+
} else if (childBuilders.length === 1) {
|
|
460
|
+
const {
|
|
461
|
+
builder,
|
|
462
|
+
predicates
|
|
463
|
+
} = childBuilders[0];
|
|
464
|
+
return new LeafLogicNode(builder, [...this.predicates, ...predicates.map(p => bindLevel(p, this.depth))], this.depth + 1);
|
|
465
|
+
} else {
|
|
466
|
+
const builtNodes = childBuilders.map(({
|
|
467
|
+
builder,
|
|
468
|
+
predicates
|
|
469
|
+
}) => new LeafLogicNode(builder, [...this.predicates, ...predicates.map(p => bindLevel(p, this.depth))], this.depth + 1));
|
|
470
|
+
return new CompositeLogicNode(builtNodes);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
hasLogic(builder) {
|
|
474
|
+
return this.builder?.hasLogic(builder) ?? false;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
class CompositeLogicNode {
|
|
478
|
+
all;
|
|
479
|
+
logic;
|
|
480
|
+
constructor(all) {
|
|
481
|
+
this.all = all;
|
|
482
|
+
this.logic = new LogicContainer([]);
|
|
483
|
+
for (const node of all) {
|
|
484
|
+
this.logic.mergeIn(node.logic);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
getChild(key) {
|
|
488
|
+
return new CompositeLogicNode(this.all.flatMap(child => child.getChild(key)));
|
|
489
|
+
}
|
|
490
|
+
hasLogic(builder) {
|
|
491
|
+
return this.all.some(node => node.hasLogic(builder));
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
function getAllChildBuilders(builder, key) {
|
|
495
|
+
if (builder instanceof LogicNodeBuilder) {
|
|
496
|
+
return builder.all.flatMap(({
|
|
497
|
+
builder,
|
|
498
|
+
predicate
|
|
499
|
+
}) => {
|
|
500
|
+
const children = getAllChildBuilders(builder, key);
|
|
501
|
+
if (predicate) {
|
|
502
|
+
return children.map(({
|
|
503
|
+
builder,
|
|
504
|
+
predicates
|
|
505
|
+
}) => ({
|
|
506
|
+
builder,
|
|
507
|
+
predicates: [...predicates, predicate]
|
|
508
|
+
}));
|
|
509
|
+
}
|
|
510
|
+
return children;
|
|
511
|
+
});
|
|
512
|
+
} else if (builder instanceof NonMergeableLogicNodeBuilder) {
|
|
513
|
+
if (builder.children.has(key)) {
|
|
514
|
+
return [{
|
|
515
|
+
builder: builder.children.get(key),
|
|
516
|
+
predicates: []
|
|
517
|
+
}];
|
|
518
|
+
}
|
|
519
|
+
} else {
|
|
520
|
+
throw new Error('Unknown LogicNodeBuilder type');
|
|
521
|
+
}
|
|
522
|
+
return [];
|
|
523
|
+
}
|
|
524
|
+
function createLogic(builder, predicates, depth) {
|
|
525
|
+
const logic = new LogicContainer(predicates);
|
|
526
|
+
if (builder instanceof LogicNodeBuilder) {
|
|
527
|
+
const builtNodes = builder.all.map(({
|
|
528
|
+
builder,
|
|
529
|
+
predicate
|
|
530
|
+
}) => new LeafLogicNode(builder, predicate ? [...predicates, bindLevel(predicate, depth)] : predicates, depth));
|
|
531
|
+
for (const node of builtNodes) {
|
|
532
|
+
logic.mergeIn(node.logic);
|
|
533
|
+
}
|
|
534
|
+
} else if (builder instanceof NonMergeableLogicNodeBuilder) {
|
|
535
|
+
logic.mergeIn(builder.logic);
|
|
536
|
+
} else {
|
|
537
|
+
throw new Error('Unknown LogicNodeBuilder type');
|
|
538
|
+
}
|
|
539
|
+
return logic;
|
|
540
|
+
}
|
|
541
|
+
function bindLevel(predicate, depth) {
|
|
542
|
+
return {
|
|
543
|
+
...predicate,
|
|
544
|
+
depth: depth
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const PATH = Symbol('PATH');
|
|
549
|
+
class FieldPathNode {
|
|
550
|
+
keys;
|
|
551
|
+
parent;
|
|
552
|
+
keyInParent;
|
|
553
|
+
root;
|
|
554
|
+
children = new Map();
|
|
555
|
+
fieldPathProxy = new Proxy(this, FIELD_PATH_PROXY_HANDLER);
|
|
556
|
+
logicBuilder;
|
|
557
|
+
constructor(keys, root, parent, keyInParent) {
|
|
558
|
+
this.keys = keys;
|
|
559
|
+
this.parent = parent;
|
|
560
|
+
this.keyInParent = keyInParent;
|
|
561
|
+
this.root = root ?? this;
|
|
562
|
+
if (!parent) {
|
|
563
|
+
this.logicBuilder = LogicNodeBuilder.newRoot();
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
get builder() {
|
|
567
|
+
if (this.logicBuilder) {
|
|
568
|
+
return this.logicBuilder;
|
|
569
|
+
}
|
|
570
|
+
return this.parent.builder.getChild(this.keyInParent);
|
|
571
|
+
}
|
|
572
|
+
get element() {
|
|
573
|
+
return this.getChild(DYNAMIC);
|
|
574
|
+
}
|
|
575
|
+
getChild(key) {
|
|
576
|
+
if (!this.children.has(key)) {
|
|
577
|
+
this.children.set(key, new FieldPathNode([...this.keys, key], this.root, this, key));
|
|
578
|
+
}
|
|
579
|
+
return this.children.get(key);
|
|
580
|
+
}
|
|
581
|
+
mergeIn(other, predicate) {
|
|
582
|
+
const path = other.compile();
|
|
583
|
+
this.builder.mergeIn(path.builder, predicate);
|
|
584
|
+
}
|
|
585
|
+
static unwrapFieldPath(formPath) {
|
|
586
|
+
return formPath[PATH];
|
|
587
|
+
}
|
|
588
|
+
static newRoot() {
|
|
589
|
+
return new FieldPathNode([], undefined, undefined, undefined);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
const FIELD_PATH_PROXY_HANDLER = {
|
|
593
|
+
get(node, property) {
|
|
594
|
+
if (property === PATH) {
|
|
595
|
+
return node;
|
|
596
|
+
}
|
|
597
|
+
return node.getChild(property).fieldPathProxy;
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
|
|
601
|
+
let currentCompilingNode = undefined;
|
|
602
|
+
const compiledSchemas = new Map();
|
|
603
|
+
class SchemaImpl {
|
|
604
|
+
schemaFn;
|
|
605
|
+
constructor(schemaFn) {
|
|
606
|
+
this.schemaFn = schemaFn;
|
|
607
|
+
}
|
|
608
|
+
compile() {
|
|
609
|
+
if (compiledSchemas.has(this)) {
|
|
610
|
+
return compiledSchemas.get(this);
|
|
611
|
+
}
|
|
612
|
+
const path = FieldPathNode.newRoot();
|
|
613
|
+
compiledSchemas.set(this, path);
|
|
614
|
+
let prevCompilingNode = currentCompilingNode;
|
|
615
|
+
try {
|
|
616
|
+
currentCompilingNode = path;
|
|
617
|
+
this.schemaFn(path.fieldPathProxy);
|
|
618
|
+
} finally {
|
|
619
|
+
currentCompilingNode = prevCompilingNode;
|
|
620
|
+
}
|
|
621
|
+
return path;
|
|
622
|
+
}
|
|
623
|
+
static create(schema) {
|
|
624
|
+
if (schema instanceof SchemaImpl) {
|
|
625
|
+
return schema;
|
|
626
|
+
}
|
|
627
|
+
return new SchemaImpl(schema);
|
|
628
|
+
}
|
|
629
|
+
static rootCompile(schema) {
|
|
630
|
+
try {
|
|
631
|
+
compiledSchemas.clear();
|
|
632
|
+
if (schema === undefined) {
|
|
633
|
+
return FieldPathNode.newRoot();
|
|
634
|
+
}
|
|
635
|
+
if (schema instanceof SchemaImpl) {
|
|
636
|
+
return schema.compile();
|
|
637
|
+
}
|
|
638
|
+
return new SchemaImpl(schema).compile();
|
|
639
|
+
} finally {
|
|
640
|
+
compiledSchemas.clear();
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
function isSchemaOrSchemaFn(value) {
|
|
645
|
+
return value instanceof SchemaImpl || typeof value === 'function';
|
|
646
|
+
}
|
|
647
|
+
function assertPathIsCurrent(path) {
|
|
648
|
+
if (currentCompilingNode !== FieldPathNode.unwrapFieldPath(path).root) {
|
|
649
|
+
throw new Error(`A FieldPath can only be used directly within the Schema that owns it,` + ` **not** outside of it or within a sub-schema.`);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
class MetadataKey {
|
|
654
|
+
brand;
|
|
655
|
+
constructor() {}
|
|
656
|
+
}
|
|
657
|
+
function createMetadataKey() {
|
|
658
|
+
return new MetadataKey();
|
|
659
|
+
}
|
|
660
|
+
class AggregateMetadataKey {
|
|
661
|
+
reduce;
|
|
662
|
+
getInitial;
|
|
663
|
+
brand;
|
|
664
|
+
constructor(reduce, getInitial) {
|
|
665
|
+
this.reduce = reduce;
|
|
666
|
+
this.getInitial = getInitial;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
function reducedMetadataKey(reduce, getInitial) {
|
|
670
|
+
return new AggregateMetadataKey(reduce, getInitial);
|
|
671
|
+
}
|
|
672
|
+
function listMetadataKey() {
|
|
673
|
+
return reducedMetadataKey((acc, item) => item === undefined ? acc : [...acc, item], () => []);
|
|
674
|
+
}
|
|
675
|
+
function minMetadataKey() {
|
|
676
|
+
return reducedMetadataKey((prev, next) => {
|
|
677
|
+
if (prev === undefined) {
|
|
678
|
+
return next;
|
|
679
|
+
}
|
|
680
|
+
if (next === undefined) {
|
|
681
|
+
return prev;
|
|
682
|
+
}
|
|
683
|
+
return Math.min(prev, next);
|
|
684
|
+
}, () => undefined);
|
|
685
|
+
}
|
|
686
|
+
function maxMetadataKey() {
|
|
687
|
+
return reducedMetadataKey((prev, next) => {
|
|
688
|
+
if (prev === undefined) {
|
|
689
|
+
return next;
|
|
690
|
+
}
|
|
691
|
+
if (next === undefined) {
|
|
692
|
+
return prev;
|
|
693
|
+
}
|
|
694
|
+
return Math.max(prev, next);
|
|
695
|
+
}, () => undefined);
|
|
696
|
+
}
|
|
697
|
+
function orMetadataKey() {
|
|
698
|
+
return reducedMetadataKey((prev, next) => prev || next, () => false);
|
|
699
|
+
}
|
|
700
|
+
function andMetadataKey() {
|
|
701
|
+
return reducedMetadataKey((prev, next) => prev && next, () => true);
|
|
702
|
+
}
|
|
703
|
+
const REQUIRED = orMetadataKey();
|
|
704
|
+
const MIN = maxMetadataKey();
|
|
705
|
+
const MAX = minMetadataKey();
|
|
706
|
+
const MIN_LENGTH = maxMetadataKey();
|
|
707
|
+
const MAX_LENGTH = minMetadataKey();
|
|
708
|
+
const PATTERN = listMetadataKey();
|
|
709
|
+
|
|
710
|
+
function requiredError(options) {
|
|
711
|
+
return new RequiredValidationError(options);
|
|
712
|
+
}
|
|
713
|
+
function minError(min, options) {
|
|
714
|
+
return new MinValidationError(min, options);
|
|
715
|
+
}
|
|
716
|
+
function maxError(max, options) {
|
|
717
|
+
return new MaxValidationError(max, options);
|
|
718
|
+
}
|
|
719
|
+
function minLengthError(minLength, options) {
|
|
720
|
+
return new MinLengthValidationError(minLength, options);
|
|
721
|
+
}
|
|
722
|
+
function maxLengthError(maxLength, options) {
|
|
723
|
+
return new MaxLengthValidationError(maxLength, options);
|
|
724
|
+
}
|
|
725
|
+
function patternError(pattern, options) {
|
|
726
|
+
return new PatternValidationError(pattern, options);
|
|
727
|
+
}
|
|
728
|
+
function emailError(options) {
|
|
729
|
+
return new EmailValidationError(options);
|
|
730
|
+
}
|
|
731
|
+
function standardSchemaError(issue, options) {
|
|
732
|
+
return new StandardSchemaValidationError(issue, options);
|
|
733
|
+
}
|
|
734
|
+
function customError(obj) {
|
|
735
|
+
return new CustomValidationError(obj);
|
|
736
|
+
}
|
|
737
|
+
class CustomValidationError {
|
|
738
|
+
__brand = undefined;
|
|
739
|
+
kind = '';
|
|
740
|
+
field;
|
|
741
|
+
message;
|
|
742
|
+
constructor(options) {
|
|
743
|
+
if (options) {
|
|
744
|
+
Object.assign(this, options);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
class _NgValidationError {
|
|
749
|
+
__brand = undefined;
|
|
750
|
+
kind = '';
|
|
751
|
+
field;
|
|
752
|
+
message;
|
|
753
|
+
constructor(options) {
|
|
754
|
+
if (options) {
|
|
755
|
+
Object.assign(this, options);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
class RequiredValidationError extends _NgValidationError {
|
|
760
|
+
kind = 'required';
|
|
761
|
+
}
|
|
762
|
+
class MinValidationError extends _NgValidationError {
|
|
763
|
+
min;
|
|
764
|
+
kind = 'min';
|
|
765
|
+
constructor(min, options) {
|
|
766
|
+
super(options);
|
|
767
|
+
this.min = min;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
class MaxValidationError extends _NgValidationError {
|
|
771
|
+
max;
|
|
772
|
+
kind = 'max';
|
|
773
|
+
constructor(max, options) {
|
|
774
|
+
super(options);
|
|
775
|
+
this.max = max;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
class MinLengthValidationError extends _NgValidationError {
|
|
779
|
+
minLength;
|
|
780
|
+
kind = 'minLength';
|
|
781
|
+
constructor(minLength, options) {
|
|
782
|
+
super(options);
|
|
783
|
+
this.minLength = minLength;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
class MaxLengthValidationError extends _NgValidationError {
|
|
787
|
+
maxLength;
|
|
788
|
+
kind = 'maxLength';
|
|
789
|
+
constructor(maxLength, options) {
|
|
790
|
+
super(options);
|
|
791
|
+
this.maxLength = maxLength;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
class PatternValidationError extends _NgValidationError {
|
|
795
|
+
pattern;
|
|
796
|
+
kind = 'pattern';
|
|
797
|
+
constructor(pattern, options) {
|
|
798
|
+
super(options);
|
|
799
|
+
this.pattern = pattern;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
class EmailValidationError extends _NgValidationError {
|
|
803
|
+
kind = 'email';
|
|
804
|
+
}
|
|
805
|
+
class StandardSchemaValidationError extends _NgValidationError {
|
|
806
|
+
issue;
|
|
807
|
+
kind = 'standardSchema';
|
|
808
|
+
constructor(issue, options) {
|
|
809
|
+
super(options);
|
|
810
|
+
this.issue = issue;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
const NgValidationError = _NgValidationError;
|
|
814
|
+
|
|
815
|
+
function getLengthOrSize(value) {
|
|
816
|
+
const v = value;
|
|
817
|
+
return typeof v.length === 'number' ? v.length : v.size;
|
|
818
|
+
}
|
|
819
|
+
function getOption(opt, ctx) {
|
|
820
|
+
return opt instanceof Function ? opt(ctx) : opt;
|
|
821
|
+
}
|
|
822
|
+
function isEmpty(value) {
|
|
823
|
+
if (typeof value === 'number') {
|
|
824
|
+
return isNaN(value);
|
|
825
|
+
}
|
|
826
|
+
return value === '' || value === false || value == null;
|
|
827
|
+
}
|
|
828
|
+
function isPlainError(error) {
|
|
829
|
+
return typeof error === 'object' && (Object.getPrototypeOf(error) === Object.prototype || Object.getPrototypeOf(error) === null);
|
|
830
|
+
}
|
|
831
|
+
function ensureCustomValidationError(error) {
|
|
832
|
+
if (isPlainError(error)) {
|
|
833
|
+
return customError(error);
|
|
834
|
+
}
|
|
835
|
+
return error;
|
|
836
|
+
}
|
|
837
|
+
function ensureCustomValidationResult(result) {
|
|
838
|
+
if (result === null || result === undefined) {
|
|
839
|
+
return result;
|
|
840
|
+
}
|
|
841
|
+
if (isArray(result)) {
|
|
842
|
+
return result.map(ensureCustomValidationError);
|
|
843
|
+
}
|
|
844
|
+
return ensureCustomValidationError(result);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
function disabled(path, logic) {
|
|
848
|
+
assertPathIsCurrent(path);
|
|
849
|
+
const pathNode = FieldPathNode.unwrapFieldPath(path);
|
|
850
|
+
pathNode.builder.addDisabledReasonRule(ctx => {
|
|
851
|
+
let result = true;
|
|
852
|
+
if (typeof logic === 'string') {
|
|
853
|
+
result = logic;
|
|
854
|
+
} else if (logic) {
|
|
855
|
+
result = logic(ctx);
|
|
856
|
+
}
|
|
857
|
+
if (typeof result === 'string') {
|
|
858
|
+
return {
|
|
859
|
+
field: ctx.field,
|
|
860
|
+
message: result
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
return result ? {
|
|
864
|
+
field: ctx.field
|
|
865
|
+
} : undefined;
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
function readonly(path, logic = () => true) {
|
|
869
|
+
assertPathIsCurrent(path);
|
|
870
|
+
const pathNode = FieldPathNode.unwrapFieldPath(path);
|
|
871
|
+
pathNode.builder.addReadonlyRule(logic);
|
|
872
|
+
}
|
|
873
|
+
function hidden(path, logic) {
|
|
874
|
+
assertPathIsCurrent(path);
|
|
875
|
+
const pathNode = FieldPathNode.unwrapFieldPath(path);
|
|
876
|
+
pathNode.builder.addHiddenRule(logic);
|
|
877
|
+
}
|
|
878
|
+
function validate(path, logic) {
|
|
879
|
+
assertPathIsCurrent(path);
|
|
880
|
+
const pathNode = FieldPathNode.unwrapFieldPath(path);
|
|
881
|
+
pathNode.builder.addSyncErrorRule(ctx => {
|
|
882
|
+
return ensureCustomValidationResult(addDefaultField(logic(ctx), ctx.field));
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
function validateTree(path, logic) {
|
|
886
|
+
assertPathIsCurrent(path);
|
|
887
|
+
const pathNode = FieldPathNode.unwrapFieldPath(path);
|
|
888
|
+
pathNode.builder.addSyncTreeErrorRule(ctx => addDefaultField(logic(ctx), ctx.field));
|
|
889
|
+
}
|
|
890
|
+
function aggregateMetadata(path, key, logic) {
|
|
891
|
+
assertPathIsCurrent(path);
|
|
892
|
+
const pathNode = FieldPathNode.unwrapFieldPath(path);
|
|
893
|
+
pathNode.builder.addAggregateMetadataRule(key, logic);
|
|
894
|
+
}
|
|
895
|
+
function metadata(path, ...rest) {
|
|
896
|
+
assertPathIsCurrent(path);
|
|
897
|
+
let key;
|
|
898
|
+
let factory;
|
|
899
|
+
if (rest.length === 2) {
|
|
900
|
+
[key, factory] = rest;
|
|
901
|
+
} else {
|
|
902
|
+
[factory] = rest;
|
|
903
|
+
}
|
|
904
|
+
key ??= createMetadataKey();
|
|
905
|
+
const pathNode = FieldPathNode.unwrapFieldPath(path);
|
|
906
|
+
pathNode.builder.addMetadataFactory(key, factory);
|
|
907
|
+
return key;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
function validateAsync(path, opts) {
|
|
911
|
+
assertPathIsCurrent(path);
|
|
912
|
+
const pathNode = FieldPathNode.unwrapFieldPath(path);
|
|
913
|
+
const RESOURCE = metadata(path, ctx => {
|
|
914
|
+
const params = computed(() => {
|
|
915
|
+
const node = ctx.stateOf(path);
|
|
916
|
+
const validationState = node.validationState;
|
|
917
|
+
if (validationState.shouldSkipValidation() || !validationState.syncValid()) {
|
|
918
|
+
return undefined;
|
|
919
|
+
}
|
|
920
|
+
return opts.params(ctx);
|
|
921
|
+
}, ...(ngDevMode ? [{
|
|
922
|
+
debugName: "params"
|
|
923
|
+
}] : []));
|
|
924
|
+
return opts.factory(params);
|
|
925
|
+
});
|
|
926
|
+
pathNode.builder.addAsyncErrorRule(ctx => {
|
|
927
|
+
const res = ctx.state.metadata(RESOURCE);
|
|
928
|
+
let errors;
|
|
929
|
+
switch (res.status()) {
|
|
930
|
+
case 'idle':
|
|
931
|
+
return undefined;
|
|
932
|
+
case 'loading':
|
|
933
|
+
case 'reloading':
|
|
934
|
+
return 'pending';
|
|
935
|
+
case 'resolved':
|
|
936
|
+
case 'local':
|
|
937
|
+
if (!res.hasValue()) {
|
|
938
|
+
return undefined;
|
|
939
|
+
}
|
|
940
|
+
errors = opts.onSuccess(res.value(), ctx);
|
|
941
|
+
return addDefaultField(errors, ctx.field);
|
|
942
|
+
case 'error':
|
|
943
|
+
errors = opts.onError(res.error(), ctx);
|
|
944
|
+
return addDefaultField(errors, ctx.field);
|
|
945
|
+
}
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
function validateHttp(path, opts) {
|
|
949
|
+
validateAsync(path, {
|
|
950
|
+
params: opts.request,
|
|
951
|
+
factory: request => httpResource(request, opts.options),
|
|
952
|
+
onSuccess: opts.onSuccess,
|
|
953
|
+
onError: opts.onError
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
class InteropNgControl {
|
|
958
|
+
field;
|
|
959
|
+
constructor(field) {
|
|
960
|
+
this.field = field;
|
|
961
|
+
}
|
|
962
|
+
control = this;
|
|
963
|
+
get value() {
|
|
964
|
+
return this.field().value();
|
|
965
|
+
}
|
|
966
|
+
get valid() {
|
|
967
|
+
return this.field().valid();
|
|
968
|
+
}
|
|
969
|
+
get invalid() {
|
|
970
|
+
return this.field().invalid();
|
|
971
|
+
}
|
|
972
|
+
get pending() {
|
|
973
|
+
return this.field().pending();
|
|
974
|
+
}
|
|
975
|
+
get disabled() {
|
|
976
|
+
return this.field().disabled();
|
|
977
|
+
}
|
|
978
|
+
get enabled() {
|
|
979
|
+
return !this.field().disabled();
|
|
980
|
+
}
|
|
981
|
+
get errors() {
|
|
982
|
+
const errors = this.field().errors();
|
|
983
|
+
if (errors.length === 0) {
|
|
984
|
+
return null;
|
|
985
|
+
}
|
|
986
|
+
const errObj = {};
|
|
987
|
+
for (const error of errors) {
|
|
988
|
+
errObj[error.kind] = error;
|
|
989
|
+
}
|
|
990
|
+
return errObj;
|
|
991
|
+
}
|
|
992
|
+
get pristine() {
|
|
993
|
+
return !this.field().dirty();
|
|
994
|
+
}
|
|
995
|
+
get dirty() {
|
|
996
|
+
return this.field().dirty();
|
|
997
|
+
}
|
|
998
|
+
get touched() {
|
|
999
|
+
return this.field().touched();
|
|
1000
|
+
}
|
|
1001
|
+
get untouched() {
|
|
1002
|
+
return !this.field().touched();
|
|
1003
|
+
}
|
|
1004
|
+
get status() {
|
|
1005
|
+
if (this.field().disabled()) {
|
|
1006
|
+
return 'DISABLED';
|
|
1007
|
+
}
|
|
1008
|
+
if (this.field().valid()) {
|
|
1009
|
+
return 'VALID';
|
|
1010
|
+
}
|
|
1011
|
+
if (this.field().invalid()) {
|
|
1012
|
+
return 'INVALID';
|
|
1013
|
+
}
|
|
1014
|
+
if (this.field().pending()) {
|
|
1015
|
+
return 'PENDING';
|
|
1016
|
+
}
|
|
1017
|
+
throw Error('AssertionError: unknown form control status');
|
|
1018
|
+
}
|
|
1019
|
+
valueAccessor = null;
|
|
1020
|
+
hasValidator(validator) {
|
|
1021
|
+
if (validator === Validators.required) {
|
|
1022
|
+
return this.field().metadata(REQUIRED)();
|
|
1023
|
+
}
|
|
1024
|
+
return false;
|
|
1025
|
+
}
|
|
1026
|
+
updateValueAndValidity() {}
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
const FIELD = new InjectionToken(typeof ngDevMode !== undefined && ngDevMode ? 'FIELD' : '');
|
|
1030
|
+
class Field {
|
|
1031
|
+
injector = inject(Injector);
|
|
1032
|
+
field = input.required(...(ngDevMode ? [{
|
|
1033
|
+
debugName: "field"
|
|
1034
|
+
}] : []));
|
|
1035
|
+
state = computed(() => this.field()(), ...(ngDevMode ? [{
|
|
1036
|
+
debugName: "state"
|
|
1037
|
+
}] : []));
|
|
1038
|
+
[_CONTROL] = undefined;
|
|
1039
|
+
controlValueAccessors = inject(NG_VALUE_ACCESSOR, {
|
|
1040
|
+
optional: true,
|
|
1041
|
+
self: true
|
|
1042
|
+
});
|
|
1043
|
+
interopNgControl;
|
|
1044
|
+
get controlValueAccessor() {
|
|
1045
|
+
return this.controlValueAccessors?.[0] ?? this.interopNgControl?.valueAccessor ?? undefined;
|
|
1046
|
+
}
|
|
1047
|
+
get ɵhasInteropControl() {
|
|
1048
|
+
return this.controlValueAccessor !== undefined;
|
|
1049
|
+
}
|
|
1050
|
+
ɵgetOrCreateNgControl() {
|
|
1051
|
+
return this.interopNgControl ??= new InteropNgControl(this.state);
|
|
1052
|
+
}
|
|
1053
|
+
ɵinteropControlCreate() {
|
|
1054
|
+
const controlValueAccessor = this.controlValueAccessor;
|
|
1055
|
+
controlValueAccessor.registerOnChange(value => {
|
|
1056
|
+
const state = this.state();
|
|
1057
|
+
state.value.set(value);
|
|
1058
|
+
state.markAsDirty();
|
|
1059
|
+
});
|
|
1060
|
+
controlValueAccessor.registerOnTouched(() => this.state().markAsTouched());
|
|
1061
|
+
}
|
|
1062
|
+
ɵinteropControlUpdate() {
|
|
1063
|
+
const controlValueAccessor = this.controlValueAccessor;
|
|
1064
|
+
const value = this.state().value();
|
|
1065
|
+
const disabled = this.state().disabled();
|
|
1066
|
+
untracked(() => {
|
|
1067
|
+
controlValueAccessor.writeValue(value);
|
|
1068
|
+
controlValueAccessor.setDisabledState?.(disabled);
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
ɵregister() {
|
|
1072
|
+
effect(onCleanup => {
|
|
1073
|
+
const fieldNode = this.state();
|
|
1074
|
+
fieldNode.nodeState.fieldBindings.update(controls => [...controls, this]);
|
|
1075
|
+
onCleanup(() => {
|
|
1076
|
+
fieldNode.nodeState.fieldBindings.update(controls => controls.filter(c => c !== this));
|
|
1077
|
+
});
|
|
1078
|
+
}, {
|
|
1079
|
+
injector: this.injector
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
1083
|
+
minVersion: "12.0.0",
|
|
1084
|
+
version: "21.0.0-next.10",
|
|
1085
|
+
ngImport: i0,
|
|
1086
|
+
type: Field,
|
|
1087
|
+
deps: [],
|
|
1088
|
+
target: i0.ɵɵFactoryTarget.Directive
|
|
1089
|
+
});
|
|
1090
|
+
static ɵdir = i0.ɵɵngDeclareDirective({
|
|
1091
|
+
minVersion: "17.1.0",
|
|
1092
|
+
version: "21.0.0-next.10",
|
|
1093
|
+
type: Field,
|
|
1094
|
+
isStandalone: true,
|
|
1095
|
+
selector: "[field]",
|
|
1096
|
+
inputs: {
|
|
1097
|
+
field: {
|
|
1098
|
+
classPropertyName: "field",
|
|
1099
|
+
publicName: "field",
|
|
1100
|
+
isSignal: true,
|
|
1101
|
+
isRequired: true,
|
|
1102
|
+
transformFunction: null
|
|
1103
|
+
}
|
|
1104
|
+
},
|
|
1105
|
+
providers: [{
|
|
1106
|
+
provide: FIELD,
|
|
1107
|
+
useExisting: Field
|
|
1108
|
+
}, {
|
|
1109
|
+
provide: NgControl,
|
|
1110
|
+
useFactory: () => inject(Field).ɵgetOrCreateNgControl()
|
|
1111
|
+
}],
|
|
1112
|
+
ngImport: i0
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
1116
|
+
minVersion: "12.0.0",
|
|
1117
|
+
version: "21.0.0-next.10",
|
|
1118
|
+
ngImport: i0,
|
|
1119
|
+
type: Field,
|
|
1120
|
+
decorators: [{
|
|
1121
|
+
type: Directive,
|
|
1122
|
+
args: [{
|
|
1123
|
+
selector: '[field]',
|
|
1124
|
+
providers: [{
|
|
1125
|
+
provide: FIELD,
|
|
1126
|
+
useExisting: Field
|
|
1127
|
+
}, {
|
|
1128
|
+
provide: NgControl,
|
|
1129
|
+
useFactory: () => inject(Field).ɵgetOrCreateNgControl()
|
|
1130
|
+
}]
|
|
1131
|
+
}]
|
|
1132
|
+
}],
|
|
1133
|
+
propDecorators: {
|
|
1134
|
+
field: [{
|
|
1135
|
+
type: i0.Input,
|
|
1136
|
+
args: [{
|
|
1137
|
+
isSignal: true,
|
|
1138
|
+
alias: "field",
|
|
1139
|
+
required: true
|
|
1140
|
+
}]
|
|
1141
|
+
}]
|
|
1142
|
+
}
|
|
1143
|
+
});
|
|
1144
|
+
|
|
1145
|
+
class FieldNodeContext {
|
|
1146
|
+
node;
|
|
1147
|
+
cache = new WeakMap();
|
|
1148
|
+
constructor(node) {
|
|
1149
|
+
this.node = node;
|
|
1150
|
+
}
|
|
1151
|
+
resolve(target) {
|
|
1152
|
+
if (!this.cache.has(target)) {
|
|
1153
|
+
const resolver = computed(() => {
|
|
1154
|
+
const targetPathNode = FieldPathNode.unwrapFieldPath(target);
|
|
1155
|
+
let field = this.node;
|
|
1156
|
+
let stepsRemaining = getBoundPathDepth();
|
|
1157
|
+
while (stepsRemaining > 0 || !field.structure.logic.hasLogic(targetPathNode.root.builder)) {
|
|
1158
|
+
stepsRemaining--;
|
|
1159
|
+
field = field.structure.parent;
|
|
1160
|
+
if (field === undefined) {
|
|
1161
|
+
throw new Error('Path is not part of this field tree.');
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
for (let key of targetPathNode.keys) {
|
|
1165
|
+
field = field.structure.getChild(key);
|
|
1166
|
+
if (field === undefined) {
|
|
1167
|
+
throw new Error(`Cannot resolve path .${targetPathNode.keys.join('.')} relative to field ${['<root>', ...this.node.structure.pathKeys()].join('.')}.`);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
return field.fieldProxy;
|
|
1171
|
+
}, ...(ngDevMode ? [{
|
|
1172
|
+
debugName: "resolver"
|
|
1173
|
+
}] : []));
|
|
1174
|
+
this.cache.set(target, resolver);
|
|
1175
|
+
}
|
|
1176
|
+
return this.cache.get(target)();
|
|
1177
|
+
}
|
|
1178
|
+
get field() {
|
|
1179
|
+
return this.node.fieldProxy;
|
|
1180
|
+
}
|
|
1181
|
+
get state() {
|
|
1182
|
+
return this.node;
|
|
1183
|
+
}
|
|
1184
|
+
get value() {
|
|
1185
|
+
return this.node.structure.value;
|
|
1186
|
+
}
|
|
1187
|
+
get key() {
|
|
1188
|
+
return this.node.structure.keyInParent;
|
|
1189
|
+
}
|
|
1190
|
+
index = computed(() => {
|
|
1191
|
+
const key = this.key();
|
|
1192
|
+
if (!isArray(untracked(this.node.structure.parent.value))) {
|
|
1193
|
+
throw new Error(`RuntimeError: cannot access index, parent field is not an array`);
|
|
1194
|
+
}
|
|
1195
|
+
return Number(key);
|
|
1196
|
+
}, ...(ngDevMode ? [{
|
|
1197
|
+
debugName: "index"
|
|
1198
|
+
}] : []));
|
|
1199
|
+
fieldOf = p => this.resolve(p);
|
|
1200
|
+
stateOf = p => this.resolve(p)();
|
|
1201
|
+
valueOf = p => this.resolve(p)().value();
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
class FieldMetadataState {
|
|
1205
|
+
node;
|
|
1206
|
+
metadata = new Map();
|
|
1207
|
+
constructor(node) {
|
|
1208
|
+
this.node = node;
|
|
1209
|
+
untracked(() => runInInjectionContext(this.node.structure.injector, () => {
|
|
1210
|
+
for (const [key, factory] of this.node.logicNode.logic.getMetadataFactoryEntries()) {
|
|
1211
|
+
this.metadata.set(key, factory(this.node.context));
|
|
1212
|
+
}
|
|
1213
|
+
}));
|
|
1214
|
+
}
|
|
1215
|
+
get(key) {
|
|
1216
|
+
if (key instanceof MetadataKey) {
|
|
1217
|
+
return this.metadata.get(key);
|
|
1218
|
+
}
|
|
1219
|
+
if (!this.metadata.has(key)) {
|
|
1220
|
+
const logic = this.node.logicNode.logic.getAggregateMetadata(key);
|
|
1221
|
+
const result = computed(() => logic.compute(this.node.context), ...(ngDevMode ? [{
|
|
1222
|
+
debugName: "result"
|
|
1223
|
+
}] : []));
|
|
1224
|
+
this.metadata.set(key, result);
|
|
1225
|
+
}
|
|
1226
|
+
return this.metadata.get(key);
|
|
1227
|
+
}
|
|
1228
|
+
has(key) {
|
|
1229
|
+
if (key instanceof AggregateMetadataKey) {
|
|
1230
|
+
return this.node.logicNode.logic.hasAggregateMetadata(key);
|
|
1231
|
+
} else {
|
|
1232
|
+
return this.metadata.has(key);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
const FIELD_PROXY_HANDLER = {
|
|
1238
|
+
get(getTgt, p) {
|
|
1239
|
+
const tgt = getTgt();
|
|
1240
|
+
const child = tgt.structure.getChild(p);
|
|
1241
|
+
if (child !== undefined) {
|
|
1242
|
+
return child.fieldProxy;
|
|
1243
|
+
}
|
|
1244
|
+
const value = untracked(tgt.value);
|
|
1245
|
+
if (isArray(value)) {
|
|
1246
|
+
if (p === 'length') {
|
|
1247
|
+
return tgt.value().length;
|
|
1248
|
+
}
|
|
1249
|
+
if (p === Symbol.iterator) {
|
|
1250
|
+
return Array.prototype[p];
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
return undefined;
|
|
1254
|
+
}
|
|
1255
|
+
};
|
|
1256
|
+
|
|
1257
|
+
function deepSignal(source, prop) {
|
|
1258
|
+
const read = computed(() => source()[prop()]);
|
|
1259
|
+
read[SIGNAL] = source[SIGNAL];
|
|
1260
|
+
read.set = value => {
|
|
1261
|
+
source.update(current => valueForWrite(current, value, prop()));
|
|
1262
|
+
};
|
|
1263
|
+
read.update = fn => {
|
|
1264
|
+
read.set(fn(untracked(read)));
|
|
1265
|
+
};
|
|
1266
|
+
read.asReadonly = () => read;
|
|
1267
|
+
return read;
|
|
1268
|
+
}
|
|
1269
|
+
function valueForWrite(sourceValue, newPropValue, prop) {
|
|
1270
|
+
if (isArray(sourceValue)) {
|
|
1271
|
+
const newValue = [...sourceValue];
|
|
1272
|
+
newValue[prop] = newPropValue;
|
|
1273
|
+
return newValue;
|
|
1274
|
+
} else {
|
|
1275
|
+
return {
|
|
1276
|
+
...sourceValue,
|
|
1277
|
+
[prop]: newPropValue
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
class FieldNodeStructure {
|
|
1283
|
+
logic;
|
|
1284
|
+
identitySymbol = Symbol();
|
|
1285
|
+
_injector = undefined;
|
|
1286
|
+
get injector() {
|
|
1287
|
+
this._injector ??= Injector.create({
|
|
1288
|
+
providers: [],
|
|
1289
|
+
parent: this.fieldManager.injector
|
|
1290
|
+
});
|
|
1291
|
+
return this._injector;
|
|
1292
|
+
}
|
|
1293
|
+
constructor(logic) {
|
|
1294
|
+
this.logic = logic;
|
|
1295
|
+
}
|
|
1296
|
+
children() {
|
|
1297
|
+
return this.childrenMap()?.values() ?? [];
|
|
1298
|
+
}
|
|
1299
|
+
getChild(key) {
|
|
1300
|
+
const map = this.childrenMap();
|
|
1301
|
+
const value = this.value();
|
|
1302
|
+
if (!map || !isObject(value)) {
|
|
1303
|
+
return undefined;
|
|
1304
|
+
}
|
|
1305
|
+
if (isArray(value)) {
|
|
1306
|
+
const childValue = value[key];
|
|
1307
|
+
if (isObject(childValue) && childValue.hasOwnProperty(this.identitySymbol)) {
|
|
1308
|
+
key = childValue[this.identitySymbol];
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
return map.get(typeof key === 'number' ? key.toString() : key);
|
|
1312
|
+
}
|
|
1313
|
+
destroy() {
|
|
1314
|
+
this.injector.destroy();
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
class RootFieldNodeStructure extends FieldNodeStructure {
|
|
1318
|
+
node;
|
|
1319
|
+
fieldManager;
|
|
1320
|
+
value;
|
|
1321
|
+
get parent() {
|
|
1322
|
+
return undefined;
|
|
1323
|
+
}
|
|
1324
|
+
get root() {
|
|
1325
|
+
return this.node;
|
|
1326
|
+
}
|
|
1327
|
+
get pathKeys() {
|
|
1328
|
+
return ROOT_PATH_KEYS;
|
|
1329
|
+
}
|
|
1330
|
+
get keyInParent() {
|
|
1331
|
+
return ROOT_KEY_IN_PARENT;
|
|
1332
|
+
}
|
|
1333
|
+
childrenMap;
|
|
1334
|
+
constructor(node, pathNode, logic, fieldManager, value, adapter, createChildNode) {
|
|
1335
|
+
super(logic);
|
|
1336
|
+
this.node = node;
|
|
1337
|
+
this.fieldManager = fieldManager;
|
|
1338
|
+
this.value = value;
|
|
1339
|
+
this.childrenMap = makeChildrenMapSignal(node, value, this.identitySymbol, pathNode, logic, adapter, createChildNode);
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
class ChildFieldNodeStructure extends FieldNodeStructure {
|
|
1343
|
+
parent;
|
|
1344
|
+
root;
|
|
1345
|
+
pathKeys;
|
|
1346
|
+
keyInParent;
|
|
1347
|
+
value;
|
|
1348
|
+
childrenMap;
|
|
1349
|
+
get fieldManager() {
|
|
1350
|
+
return this.root.structure.fieldManager;
|
|
1351
|
+
}
|
|
1352
|
+
constructor(node, pathNode, logic, parent, identityInParent, initialKeyInParent, adapter, createChildNode) {
|
|
1353
|
+
super(logic);
|
|
1354
|
+
this.parent = parent;
|
|
1355
|
+
this.root = this.parent.structure.root;
|
|
1356
|
+
this.pathKeys = computed(() => [...parent.structure.pathKeys(), this.keyInParent()], ...(ngDevMode ? [{
|
|
1357
|
+
debugName: "pathKeys"
|
|
1358
|
+
}] : []));
|
|
1359
|
+
if (identityInParent === undefined) {
|
|
1360
|
+
const key = initialKeyInParent;
|
|
1361
|
+
this.keyInParent = computed(() => {
|
|
1362
|
+
if (parent.structure.childrenMap()?.get(key) !== node) {
|
|
1363
|
+
throw new Error(`RuntimeError: orphan field, looking for property '${key}' of ${getDebugName(parent)}`);
|
|
1364
|
+
}
|
|
1365
|
+
return key;
|
|
1366
|
+
}, ...(ngDevMode ? [{
|
|
1367
|
+
debugName: "keyInParent"
|
|
1368
|
+
}] : []));
|
|
1369
|
+
} else {
|
|
1370
|
+
let lastKnownKey = initialKeyInParent;
|
|
1371
|
+
this.keyInParent = computed(() => {
|
|
1372
|
+
const parentValue = parent.structure.value();
|
|
1373
|
+
if (!isArray(parentValue)) {
|
|
1374
|
+
throw new Error(`RuntimeError: orphan field, expected ${getDebugName(parent)} to be an array`);
|
|
1375
|
+
}
|
|
1376
|
+
const data = parentValue[lastKnownKey];
|
|
1377
|
+
if (isObject(data) && data.hasOwnProperty(parent.structure.identitySymbol) && data[parent.structure.identitySymbol] === identityInParent) {
|
|
1378
|
+
return lastKnownKey;
|
|
1379
|
+
}
|
|
1380
|
+
for (let i = 0; i < parentValue.length; i++) {
|
|
1381
|
+
const data = parentValue[i];
|
|
1382
|
+
if (isObject(data) && data.hasOwnProperty(parent.structure.identitySymbol) && data[parent.structure.identitySymbol] === identityInParent) {
|
|
1383
|
+
return lastKnownKey = i.toString();
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
throw new Error(`RuntimeError: orphan field, can't find element in array ${getDebugName(parent)}`);
|
|
1387
|
+
}, ...(ngDevMode ? [{
|
|
1388
|
+
debugName: "keyInParent"
|
|
1389
|
+
}] : []));
|
|
1390
|
+
}
|
|
1391
|
+
this.value = deepSignal(this.parent.structure.value, this.keyInParent);
|
|
1392
|
+
this.childrenMap = makeChildrenMapSignal(node, this.value, this.identitySymbol, pathNode, logic, adapter, createChildNode);
|
|
1393
|
+
this.fieldManager.structures.add(this);
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
let globalId = 0;
|
|
1397
|
+
const ROOT_PATH_KEYS = computed(() => [], ...(ngDevMode ? [{
|
|
1398
|
+
debugName: "ROOT_PATH_KEYS"
|
|
1399
|
+
}] : []));
|
|
1400
|
+
const ROOT_KEY_IN_PARENT = computed(() => {
|
|
1401
|
+
throw new Error(`RuntimeError: the top-level field in the form has no parent`);
|
|
1402
|
+
}, ...(ngDevMode ? [{
|
|
1403
|
+
debugName: "ROOT_KEY_IN_PARENT"
|
|
1404
|
+
}] : []));
|
|
1405
|
+
function makeChildrenMapSignal(node, valueSignal, identitySymbol, pathNode, logic, adapter, createChildNode) {
|
|
1406
|
+
return linkedSignal({
|
|
1407
|
+
source: valueSignal,
|
|
1408
|
+
computation: (value, previous) => {
|
|
1409
|
+
let childrenMap = previous?.value;
|
|
1410
|
+
if (!isObject(value)) {
|
|
1411
|
+
return undefined;
|
|
1412
|
+
}
|
|
1413
|
+
const isValueArray = isArray(value);
|
|
1414
|
+
if (childrenMap !== undefined) {
|
|
1415
|
+
let oldKeys = undefined;
|
|
1416
|
+
if (isValueArray) {
|
|
1417
|
+
oldKeys = new Set(childrenMap.keys());
|
|
1418
|
+
for (let i = 0; i < value.length; i++) {
|
|
1419
|
+
const childValue = value[i];
|
|
1420
|
+
if (isObject(childValue) && childValue.hasOwnProperty(identitySymbol)) {
|
|
1421
|
+
oldKeys.delete(childValue[identitySymbol]);
|
|
1422
|
+
} else {
|
|
1423
|
+
oldKeys.delete(i.toString());
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
for (const key of oldKeys) {
|
|
1427
|
+
childrenMap.delete(key);
|
|
1428
|
+
}
|
|
1429
|
+
} else {
|
|
1430
|
+
for (let key of childrenMap.keys()) {
|
|
1431
|
+
if (!value.hasOwnProperty(key)) {
|
|
1432
|
+
childrenMap.delete(key);
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
for (let key of Object.keys(value)) {
|
|
1438
|
+
let trackingId = undefined;
|
|
1439
|
+
const childValue = value[key];
|
|
1440
|
+
if (childValue === undefined) {
|
|
1441
|
+
childrenMap?.delete(key);
|
|
1442
|
+
continue;
|
|
1443
|
+
}
|
|
1444
|
+
if (isValueArray && isObject(childValue) && !isArray(childValue)) {
|
|
1445
|
+
trackingId = childValue[identitySymbol] ??= Symbol(ngDevMode ? `id:${globalId++}` : '');
|
|
1446
|
+
}
|
|
1447
|
+
const identity = trackingId ?? key;
|
|
1448
|
+
if (childrenMap?.has(identity)) {
|
|
1449
|
+
continue;
|
|
1450
|
+
}
|
|
1451
|
+
let childPath;
|
|
1452
|
+
let childLogic;
|
|
1453
|
+
if (isValueArray) {
|
|
1454
|
+
childPath = pathNode.getChild(DYNAMIC);
|
|
1455
|
+
childLogic = logic.getChild(DYNAMIC);
|
|
1456
|
+
} else {
|
|
1457
|
+
childPath = pathNode.getChild(key);
|
|
1458
|
+
childLogic = logic.getChild(key);
|
|
1459
|
+
}
|
|
1460
|
+
childrenMap ??= new Map();
|
|
1461
|
+
childrenMap.set(identity, createChildNode({
|
|
1462
|
+
kind: 'child',
|
|
1463
|
+
parent: node,
|
|
1464
|
+
pathNode: childPath,
|
|
1465
|
+
logic: childLogic,
|
|
1466
|
+
initialKeyInParent: key,
|
|
1467
|
+
identityInParent: trackingId,
|
|
1468
|
+
fieldAdapter: adapter
|
|
1469
|
+
}));
|
|
1470
|
+
}
|
|
1471
|
+
return childrenMap;
|
|
1472
|
+
},
|
|
1473
|
+
equal: () => false
|
|
1474
|
+
});
|
|
1475
|
+
}
|
|
1476
|
+
function getDebugName(node) {
|
|
1477
|
+
return `<root>.${node.structure.pathKeys().join('.')}`;
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
class FieldSubmitState {
|
|
1481
|
+
node;
|
|
1482
|
+
selfSubmitting = signal(false, ...(ngDevMode ? [{
|
|
1483
|
+
debugName: "selfSubmitting"
|
|
1484
|
+
}] : []));
|
|
1485
|
+
serverErrors;
|
|
1486
|
+
constructor(node) {
|
|
1487
|
+
this.node = node;
|
|
1488
|
+
this.serverErrors = linkedSignal(...(ngDevMode ? [{
|
|
1489
|
+
debugName: "serverErrors",
|
|
1490
|
+
source: this.node.structure.value,
|
|
1491
|
+
computation: () => []
|
|
1492
|
+
}] : [{
|
|
1493
|
+
source: this.node.structure.value,
|
|
1494
|
+
computation: () => []
|
|
1495
|
+
}]));
|
|
1496
|
+
}
|
|
1497
|
+
submitting = computed(() => {
|
|
1498
|
+
return this.selfSubmitting() || (this.node.structure.parent?.submitting() ?? false);
|
|
1499
|
+
}, ...(ngDevMode ? [{
|
|
1500
|
+
debugName: "submitting"
|
|
1501
|
+
}] : []));
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
class FieldNode {
|
|
1505
|
+
structure;
|
|
1506
|
+
validationState;
|
|
1507
|
+
metadataState;
|
|
1508
|
+
nodeState;
|
|
1509
|
+
submitState;
|
|
1510
|
+
_context = undefined;
|
|
1511
|
+
fieldAdapter;
|
|
1512
|
+
get context() {
|
|
1513
|
+
return this._context ??= new FieldNodeContext(this);
|
|
1514
|
+
}
|
|
1515
|
+
fieldProxy = new Proxy(() => this, FIELD_PROXY_HANDLER);
|
|
1516
|
+
constructor(options) {
|
|
1517
|
+
this.fieldAdapter = options.fieldAdapter;
|
|
1518
|
+
this.structure = this.fieldAdapter.createStructure(this, options);
|
|
1519
|
+
this.validationState = this.fieldAdapter.createValidationState(this, options);
|
|
1520
|
+
this.nodeState = this.fieldAdapter.createNodeState(this, options);
|
|
1521
|
+
this.metadataState = new FieldMetadataState(this);
|
|
1522
|
+
this.submitState = new FieldSubmitState(this);
|
|
1523
|
+
}
|
|
1524
|
+
get logicNode() {
|
|
1525
|
+
return this.structure.logic;
|
|
1526
|
+
}
|
|
1527
|
+
get value() {
|
|
1528
|
+
return this.structure.value;
|
|
1529
|
+
}
|
|
1530
|
+
get keyInParent() {
|
|
1531
|
+
return this.structure.keyInParent;
|
|
1532
|
+
}
|
|
1533
|
+
get errors() {
|
|
1534
|
+
return this.validationState.errors;
|
|
1535
|
+
}
|
|
1536
|
+
get errorSummary() {
|
|
1537
|
+
return this.validationState.errorSummary;
|
|
1538
|
+
}
|
|
1539
|
+
get pending() {
|
|
1540
|
+
return this.validationState.pending;
|
|
1541
|
+
}
|
|
1542
|
+
get valid() {
|
|
1543
|
+
return this.validationState.valid;
|
|
1544
|
+
}
|
|
1545
|
+
get invalid() {
|
|
1546
|
+
return this.validationState.invalid;
|
|
1547
|
+
}
|
|
1548
|
+
get dirty() {
|
|
1549
|
+
return this.nodeState.dirty;
|
|
1550
|
+
}
|
|
1551
|
+
get touched() {
|
|
1552
|
+
return this.nodeState.touched;
|
|
1553
|
+
}
|
|
1554
|
+
get disabled() {
|
|
1555
|
+
return this.nodeState.disabled;
|
|
1556
|
+
}
|
|
1557
|
+
get disabledReasons() {
|
|
1558
|
+
return this.nodeState.disabledReasons;
|
|
1559
|
+
}
|
|
1560
|
+
get hidden() {
|
|
1561
|
+
return this.nodeState.hidden;
|
|
1562
|
+
}
|
|
1563
|
+
get readonly() {
|
|
1564
|
+
return this.nodeState.readonly;
|
|
1565
|
+
}
|
|
1566
|
+
get fieldBindings() {
|
|
1567
|
+
return this.nodeState.fieldBindings;
|
|
1568
|
+
}
|
|
1569
|
+
get submitting() {
|
|
1570
|
+
return this.submitState.submitting;
|
|
1571
|
+
}
|
|
1572
|
+
get name() {
|
|
1573
|
+
return this.nodeState.name;
|
|
1574
|
+
}
|
|
1575
|
+
metadataOrUndefined(key) {
|
|
1576
|
+
return this.hasMetadata(key) ? this.metadata(key) : undefined;
|
|
1577
|
+
}
|
|
1578
|
+
get max() {
|
|
1579
|
+
return this.metadataOrUndefined(MAX);
|
|
1580
|
+
}
|
|
1581
|
+
get maxLength() {
|
|
1582
|
+
return this.metadataOrUndefined(MAX_LENGTH);
|
|
1583
|
+
}
|
|
1584
|
+
get min() {
|
|
1585
|
+
return this.metadataOrUndefined(MIN);
|
|
1586
|
+
}
|
|
1587
|
+
get minLength() {
|
|
1588
|
+
return this.metadataOrUndefined(MIN_LENGTH);
|
|
1589
|
+
}
|
|
1590
|
+
get pattern() {
|
|
1591
|
+
return this.metadataOrUndefined(PATTERN);
|
|
1592
|
+
}
|
|
1593
|
+
get required() {
|
|
1594
|
+
return this.metadataOrUndefined(REQUIRED);
|
|
1595
|
+
}
|
|
1596
|
+
metadata(key) {
|
|
1597
|
+
return this.metadataState.get(key);
|
|
1598
|
+
}
|
|
1599
|
+
hasMetadata(key) {
|
|
1600
|
+
return this.metadataState.has(key);
|
|
1601
|
+
}
|
|
1602
|
+
markAsTouched() {
|
|
1603
|
+
this.nodeState.markAsTouched();
|
|
1604
|
+
}
|
|
1605
|
+
markAsDirty() {
|
|
1606
|
+
this.nodeState.markAsDirty();
|
|
1607
|
+
}
|
|
1608
|
+
reset() {
|
|
1609
|
+
this.nodeState.markAsUntouched();
|
|
1610
|
+
this.nodeState.markAsPristine();
|
|
1611
|
+
for (const child of this.structure.children()) {
|
|
1612
|
+
child.reset();
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
static newRoot(fieldManager, value, pathNode, adapter) {
|
|
1616
|
+
return adapter.newRoot(fieldManager, value, pathNode, adapter);
|
|
1617
|
+
}
|
|
1618
|
+
static newChild(options) {
|
|
1619
|
+
return options.fieldAdapter.newChild(options);
|
|
1620
|
+
}
|
|
1621
|
+
createStructure(options) {
|
|
1622
|
+
return options.kind === 'root' ? new RootFieldNodeStructure(this, options.pathNode, options.logic, options.fieldManager, options.value, options.fieldAdapter, FieldNode.newChild) : new ChildFieldNodeStructure(this, options.pathNode, options.logic, options.parent, options.identityInParent, options.initialKeyInParent, options.fieldAdapter, FieldNode.newChild);
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
class FieldNodeState {
|
|
1627
|
+
node;
|
|
1628
|
+
selfTouched = signal(false, ...(ngDevMode ? [{
|
|
1629
|
+
debugName: "selfTouched"
|
|
1630
|
+
}] : []));
|
|
1631
|
+
selfDirty = signal(false, ...(ngDevMode ? [{
|
|
1632
|
+
debugName: "selfDirty"
|
|
1633
|
+
}] : []));
|
|
1634
|
+
markAsTouched() {
|
|
1635
|
+
this.selfTouched.set(true);
|
|
1636
|
+
}
|
|
1637
|
+
markAsDirty() {
|
|
1638
|
+
this.selfDirty.set(true);
|
|
1639
|
+
}
|
|
1640
|
+
markAsPristine() {
|
|
1641
|
+
this.selfDirty.set(false);
|
|
1642
|
+
}
|
|
1643
|
+
markAsUntouched() {
|
|
1644
|
+
this.selfTouched.set(false);
|
|
1645
|
+
}
|
|
1646
|
+
fieldBindings = signal([], ...(ngDevMode ? [{
|
|
1647
|
+
debugName: "fieldBindings"
|
|
1648
|
+
}] : []));
|
|
1649
|
+
constructor(node) {
|
|
1650
|
+
this.node = node;
|
|
1651
|
+
}
|
|
1652
|
+
dirty = computed(() => {
|
|
1653
|
+
const selfDirtyValue = this.selfDirty() && !this.isNonInteractive();
|
|
1654
|
+
return reduceChildren(this.node, selfDirtyValue, (child, value) => value || child.nodeState.dirty(), shortCircuitTrue);
|
|
1655
|
+
}, ...(ngDevMode ? [{
|
|
1656
|
+
debugName: "dirty"
|
|
1657
|
+
}] : []));
|
|
1658
|
+
touched = computed(() => {
|
|
1659
|
+
const selfTouchedValue = this.selfTouched() && !this.isNonInteractive();
|
|
1660
|
+
return reduceChildren(this.node, selfTouchedValue, (child, value) => value || child.nodeState.touched(), shortCircuitTrue);
|
|
1661
|
+
}, ...(ngDevMode ? [{
|
|
1662
|
+
debugName: "touched"
|
|
1663
|
+
}] : []));
|
|
1664
|
+
disabledReasons = computed(() => [...(this.node.structure.parent?.nodeState.disabledReasons() ?? []), ...this.node.logicNode.logic.disabledReasons.compute(this.node.context)], ...(ngDevMode ? [{
|
|
1665
|
+
debugName: "disabledReasons"
|
|
1666
|
+
}] : []));
|
|
1667
|
+
disabled = computed(() => !!this.disabledReasons().length, ...(ngDevMode ? [{
|
|
1668
|
+
debugName: "disabled"
|
|
1669
|
+
}] : []));
|
|
1670
|
+
readonly = computed(() => (this.node.structure.parent?.nodeState.readonly() || this.node.logicNode.logic.readonly.compute(this.node.context)) ?? false, ...(ngDevMode ? [{
|
|
1671
|
+
debugName: "readonly"
|
|
1672
|
+
}] : []));
|
|
1673
|
+
hidden = computed(() => (this.node.structure.parent?.nodeState.hidden() || this.node.logicNode.logic.hidden.compute(this.node.context)) ?? false, ...(ngDevMode ? [{
|
|
1674
|
+
debugName: "hidden"
|
|
1675
|
+
}] : []));
|
|
1676
|
+
name = computed(() => {
|
|
1677
|
+
const parent = this.node.structure.parent;
|
|
1678
|
+
if (!parent) {
|
|
1679
|
+
return this.node.structure.fieldManager.rootName;
|
|
1680
|
+
}
|
|
1681
|
+
return `${parent.name()}.${this.node.structure.keyInParent()}`;
|
|
1682
|
+
}, ...(ngDevMode ? [{
|
|
1683
|
+
debugName: "name"
|
|
1684
|
+
}] : []));
|
|
1685
|
+
isNonInteractive = computed(() => this.hidden() || this.disabled() || this.readonly(), ...(ngDevMode ? [{
|
|
1686
|
+
debugName: "isNonInteractive"
|
|
1687
|
+
}] : []));
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
class BasicFieldAdapter {
|
|
1691
|
+
newRoot(fieldManager, value, pathNode, adapter) {
|
|
1692
|
+
return new FieldNode({
|
|
1693
|
+
kind: 'root',
|
|
1694
|
+
fieldManager,
|
|
1695
|
+
value,
|
|
1696
|
+
pathNode,
|
|
1697
|
+
logic: pathNode.builder.build(),
|
|
1698
|
+
fieldAdapter: adapter
|
|
1699
|
+
});
|
|
1700
|
+
}
|
|
1701
|
+
newChild(options) {
|
|
1702
|
+
return new FieldNode(options);
|
|
1703
|
+
}
|
|
1704
|
+
createNodeState(node) {
|
|
1705
|
+
return new FieldNodeState(node);
|
|
1706
|
+
}
|
|
1707
|
+
createValidationState(node) {
|
|
1708
|
+
return new FieldValidationState(node);
|
|
1709
|
+
}
|
|
1710
|
+
createStructure(node, options) {
|
|
1711
|
+
return node.createStructure(options);
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
class FormFieldManager {
|
|
1716
|
+
injector;
|
|
1717
|
+
rootName;
|
|
1718
|
+
constructor(injector, rootName) {
|
|
1719
|
+
this.injector = injector;
|
|
1720
|
+
this.rootName = rootName ?? `${this.injector.get(APP_ID)}.form${nextFormId++}`;
|
|
1721
|
+
}
|
|
1722
|
+
structures = new Set();
|
|
1723
|
+
createFieldManagementEffect(root) {
|
|
1724
|
+
effect(() => {
|
|
1725
|
+
const liveStructures = new Set();
|
|
1726
|
+
this.markStructuresLive(root, liveStructures);
|
|
1727
|
+
for (const structure of this.structures) {
|
|
1728
|
+
if (!liveStructures.has(structure)) {
|
|
1729
|
+
this.structures.delete(structure);
|
|
1730
|
+
untracked(() => structure.destroy());
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
}, {
|
|
1734
|
+
injector: this.injector
|
|
1735
|
+
});
|
|
1736
|
+
}
|
|
1737
|
+
markStructuresLive(structure, liveStructures) {
|
|
1738
|
+
liveStructures.add(structure);
|
|
1739
|
+
for (const child of structure.children()) {
|
|
1740
|
+
this.markStructuresLive(child.structure, liveStructures);
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
let nextFormId = 0;
|
|
1745
|
+
|
|
1746
|
+
function normalizeFormArgs(args) {
|
|
1747
|
+
let model;
|
|
1748
|
+
let schema;
|
|
1749
|
+
let options;
|
|
1750
|
+
if (args.length === 3) {
|
|
1751
|
+
[model, schema, options] = args;
|
|
1752
|
+
} else if (args.length === 2) {
|
|
1753
|
+
if (isSchemaOrSchemaFn(args[1])) {
|
|
1754
|
+
[model, schema] = args;
|
|
1755
|
+
} else {
|
|
1756
|
+
[model, options] = args;
|
|
1757
|
+
}
|
|
1758
|
+
} else {
|
|
1759
|
+
[model] = args;
|
|
1760
|
+
}
|
|
1761
|
+
return [model, schema, options];
|
|
1762
|
+
}
|
|
1763
|
+
function form(...args) {
|
|
1764
|
+
const [model, schema, options] = normalizeFormArgs(args);
|
|
1765
|
+
const injector = options?.injector ?? inject(Injector);
|
|
1766
|
+
const pathNode = runInInjectionContext(injector, () => SchemaImpl.rootCompile(schema));
|
|
1767
|
+
const fieldManager = new FormFieldManager(injector, options?.name);
|
|
1768
|
+
const adapter = options?.adapter ?? new BasicFieldAdapter();
|
|
1769
|
+
const fieldRoot = FieldNode.newRoot(fieldManager, model, pathNode, adapter);
|
|
1770
|
+
fieldManager.createFieldManagementEffect(fieldRoot.structure);
|
|
1771
|
+
return fieldRoot.fieldProxy;
|
|
1772
|
+
}
|
|
1773
|
+
function applyEach(path, schema) {
|
|
1774
|
+
assertPathIsCurrent(path);
|
|
1775
|
+
const elementPath = FieldPathNode.unwrapFieldPath(path).element.fieldPathProxy;
|
|
1776
|
+
apply(elementPath, schema);
|
|
1777
|
+
}
|
|
1778
|
+
function apply(path, schema) {
|
|
1779
|
+
assertPathIsCurrent(path);
|
|
1780
|
+
const pathNode = FieldPathNode.unwrapFieldPath(path);
|
|
1781
|
+
pathNode.mergeIn(SchemaImpl.create(schema));
|
|
1782
|
+
}
|
|
1783
|
+
function applyWhen(path, logic, schema) {
|
|
1784
|
+
assertPathIsCurrent(path);
|
|
1785
|
+
const pathNode = FieldPathNode.unwrapFieldPath(path);
|
|
1786
|
+
pathNode.mergeIn(SchemaImpl.create(schema), {
|
|
1787
|
+
fn: logic,
|
|
1788
|
+
path
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1791
|
+
function applyWhenValue(path, predicate, schema) {
|
|
1792
|
+
applyWhen(path, ({
|
|
1793
|
+
value
|
|
1794
|
+
}) => predicate(value()), schema);
|
|
1795
|
+
}
|
|
1796
|
+
async function submit(form, action) {
|
|
1797
|
+
const node = form();
|
|
1798
|
+
markAllAsTouched(node);
|
|
1799
|
+
if (node.invalid()) {
|
|
1800
|
+
return;
|
|
1801
|
+
}
|
|
1802
|
+
node.submitState.selfSubmitting.set(true);
|
|
1803
|
+
try {
|
|
1804
|
+
const errors = await action(form);
|
|
1805
|
+
errors && setServerErrors(node, errors);
|
|
1806
|
+
} finally {
|
|
1807
|
+
node.submitState.selfSubmitting.set(false);
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
function setServerErrors(submittedField, errors) {
|
|
1811
|
+
if (!isArray(errors)) {
|
|
1812
|
+
errors = [errors];
|
|
1813
|
+
}
|
|
1814
|
+
const errorsByField = new Map();
|
|
1815
|
+
for (const error of errors) {
|
|
1816
|
+
const errorWithField = addDefaultField(error, submittedField.fieldProxy);
|
|
1817
|
+
const field = errorWithField.field();
|
|
1818
|
+
let fieldErrors = errorsByField.get(field);
|
|
1819
|
+
if (!fieldErrors) {
|
|
1820
|
+
fieldErrors = [];
|
|
1821
|
+
errorsByField.set(field, fieldErrors);
|
|
1822
|
+
}
|
|
1823
|
+
fieldErrors.push(errorWithField);
|
|
1824
|
+
}
|
|
1825
|
+
for (const [field, fieldErrors] of errorsByField) {
|
|
1826
|
+
field.submitState.serverErrors.set(fieldErrors);
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
function schema(fn) {
|
|
1830
|
+
return SchemaImpl.create(fn);
|
|
1831
|
+
}
|
|
1832
|
+
function markAllAsTouched(node) {
|
|
1833
|
+
node.markAsTouched();
|
|
1834
|
+
for (const child of node.structure.children()) {
|
|
1835
|
+
markAllAsTouched(child);
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
const EMAIL_REGEXP = /^(?=.{1,254}$)(?=.{1,64}@)[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
|
1840
|
+
function email(path, config) {
|
|
1841
|
+
validate(path, ctx => {
|
|
1842
|
+
if (isEmpty(ctx.value())) {
|
|
1843
|
+
return undefined;
|
|
1844
|
+
}
|
|
1845
|
+
if (!EMAIL_REGEXP.test(ctx.value())) {
|
|
1846
|
+
if (config?.error) {
|
|
1847
|
+
return getOption(config.error, ctx);
|
|
1848
|
+
} else {
|
|
1849
|
+
return emailError({
|
|
1850
|
+
message: getOption(config?.message, ctx)
|
|
1851
|
+
});
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
return undefined;
|
|
1855
|
+
});
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
function max(path, maxValue, config) {
|
|
1859
|
+
const MAX_MEMO = metadata(path, ctx => computed(() => typeof maxValue === 'number' ? maxValue : maxValue(ctx)));
|
|
1860
|
+
aggregateMetadata(path, MAX, ({
|
|
1861
|
+
state
|
|
1862
|
+
}) => state.metadata(MAX_MEMO)());
|
|
1863
|
+
validate(path, ctx => {
|
|
1864
|
+
if (isEmpty(ctx.value())) {
|
|
1865
|
+
return undefined;
|
|
1866
|
+
}
|
|
1867
|
+
const max = ctx.state.metadata(MAX_MEMO)();
|
|
1868
|
+
if (max === undefined || Number.isNaN(max)) {
|
|
1869
|
+
return undefined;
|
|
1870
|
+
}
|
|
1871
|
+
if (ctx.value() > max) {
|
|
1872
|
+
if (config?.error) {
|
|
1873
|
+
return getOption(config.error, ctx);
|
|
1874
|
+
} else {
|
|
1875
|
+
return maxError(max, {
|
|
1876
|
+
message: getOption(config?.message, ctx)
|
|
1877
|
+
});
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
return undefined;
|
|
1881
|
+
});
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
function maxLength(path, maxLength, config) {
|
|
1885
|
+
const MAX_LENGTH_MEMO = metadata(path, ctx => computed(() => typeof maxLength === 'number' ? maxLength : maxLength(ctx)));
|
|
1886
|
+
aggregateMetadata(path, MAX_LENGTH, ({
|
|
1887
|
+
state
|
|
1888
|
+
}) => state.metadata(MAX_LENGTH_MEMO)());
|
|
1889
|
+
validate(path, ctx => {
|
|
1890
|
+
if (isEmpty(ctx.value())) {
|
|
1891
|
+
return undefined;
|
|
1892
|
+
}
|
|
1893
|
+
const maxLength = ctx.state.metadata(MAX_LENGTH_MEMO)();
|
|
1894
|
+
if (maxLength === undefined) {
|
|
1895
|
+
return undefined;
|
|
1896
|
+
}
|
|
1897
|
+
if (getLengthOrSize(ctx.value()) > maxLength) {
|
|
1898
|
+
if (config?.error) {
|
|
1899
|
+
return getOption(config.error, ctx);
|
|
1900
|
+
} else {
|
|
1901
|
+
return maxLengthError(maxLength, {
|
|
1902
|
+
message: getOption(config?.message, ctx)
|
|
1903
|
+
});
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
return undefined;
|
|
1907
|
+
});
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
function min(path, minValue, config) {
|
|
1911
|
+
const MIN_MEMO = metadata(path, ctx => computed(() => typeof minValue === 'number' ? minValue : minValue(ctx)));
|
|
1912
|
+
aggregateMetadata(path, MIN, ({
|
|
1913
|
+
state
|
|
1914
|
+
}) => state.metadata(MIN_MEMO)());
|
|
1915
|
+
validate(path, ctx => {
|
|
1916
|
+
if (isEmpty(ctx.value())) {
|
|
1917
|
+
return undefined;
|
|
1918
|
+
}
|
|
1919
|
+
const min = ctx.state.metadata(MIN_MEMO)();
|
|
1920
|
+
if (min === undefined || Number.isNaN(min)) {
|
|
1921
|
+
return undefined;
|
|
1922
|
+
}
|
|
1923
|
+
if (ctx.value() < min) {
|
|
1924
|
+
if (config?.error) {
|
|
1925
|
+
return getOption(config.error, ctx);
|
|
1926
|
+
} else {
|
|
1927
|
+
return minError(min, {
|
|
1928
|
+
message: getOption(config?.message, ctx)
|
|
1929
|
+
});
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
return undefined;
|
|
1933
|
+
});
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
function minLength(path, minLength, config) {
|
|
1937
|
+
const MIN_LENGTH_MEMO = metadata(path, ctx => computed(() => typeof minLength === 'number' ? minLength : minLength(ctx)));
|
|
1938
|
+
aggregateMetadata(path, MIN_LENGTH, ({
|
|
1939
|
+
state
|
|
1940
|
+
}) => state.metadata(MIN_LENGTH_MEMO)());
|
|
1941
|
+
validate(path, ctx => {
|
|
1942
|
+
if (isEmpty(ctx.value())) {
|
|
1943
|
+
return undefined;
|
|
1944
|
+
}
|
|
1945
|
+
const minLength = ctx.state.metadata(MIN_LENGTH_MEMO)();
|
|
1946
|
+
if (minLength === undefined) {
|
|
1947
|
+
return undefined;
|
|
1948
|
+
}
|
|
1949
|
+
if (getLengthOrSize(ctx.value()) < minLength) {
|
|
1950
|
+
if (config?.error) {
|
|
1951
|
+
return getOption(config.error, ctx);
|
|
1952
|
+
} else {
|
|
1953
|
+
return minLengthError(minLength, {
|
|
1954
|
+
message: getOption(config?.message, ctx)
|
|
1955
|
+
});
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
return undefined;
|
|
1959
|
+
});
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
function pattern(path, pattern, config) {
|
|
1963
|
+
const PATTERN_MEMO = metadata(path, ctx => computed(() => pattern instanceof RegExp ? pattern : pattern(ctx)));
|
|
1964
|
+
aggregateMetadata(path, PATTERN, ({
|
|
1965
|
+
state
|
|
1966
|
+
}) => state.metadata(PATTERN_MEMO)());
|
|
1967
|
+
validate(path, ctx => {
|
|
1968
|
+
if (isEmpty(ctx.value())) {
|
|
1969
|
+
return undefined;
|
|
1970
|
+
}
|
|
1971
|
+
const pattern = ctx.state.metadata(PATTERN_MEMO)();
|
|
1972
|
+
if (pattern === undefined) {
|
|
1973
|
+
return undefined;
|
|
1974
|
+
}
|
|
1975
|
+
if (!pattern.test(ctx.value())) {
|
|
1976
|
+
if (config?.error) {
|
|
1977
|
+
return getOption(config.error, ctx);
|
|
1978
|
+
} else {
|
|
1979
|
+
return patternError(pattern, {
|
|
1980
|
+
message: getOption(config?.message, ctx)
|
|
1981
|
+
});
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
return undefined;
|
|
1985
|
+
});
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
function required(path, config) {
|
|
1989
|
+
const REQUIRED_MEMO = metadata(path, ctx => computed(() => config?.when ? config.when(ctx) : true));
|
|
1990
|
+
aggregateMetadata(path, REQUIRED, ({
|
|
1991
|
+
state
|
|
1992
|
+
}) => state.metadata(REQUIRED_MEMO)());
|
|
1993
|
+
validate(path, ctx => {
|
|
1994
|
+
if (ctx.state.metadata(REQUIRED_MEMO)() && isEmpty(ctx.value())) {
|
|
1995
|
+
if (config?.error) {
|
|
1996
|
+
return getOption(config.error, ctx);
|
|
1997
|
+
} else {
|
|
1998
|
+
return requiredError({
|
|
1999
|
+
message: getOption(config?.message, ctx)
|
|
2000
|
+
});
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
return undefined;
|
|
2004
|
+
});
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
function validateStandardSchema(path, schema) {
|
|
2008
|
+
const VALIDATOR_MEMO = metadata(path, ({
|
|
2009
|
+
value
|
|
2010
|
+
}) => {
|
|
2011
|
+
return computed(() => schema['~standard'].validate(value()));
|
|
2012
|
+
});
|
|
2013
|
+
validateTree(path, ({
|
|
2014
|
+
state,
|
|
2015
|
+
fieldOf
|
|
2016
|
+
}) => {
|
|
2017
|
+
const result = state.metadata(VALIDATOR_MEMO)();
|
|
2018
|
+
if (_isPromise(result)) {
|
|
2019
|
+
return [];
|
|
2020
|
+
}
|
|
2021
|
+
return result.issues?.map(issue => standardIssueToFormTreeError(fieldOf(path), issue)) ?? [];
|
|
2022
|
+
});
|
|
2023
|
+
validateAsync(path, {
|
|
2024
|
+
params: ({
|
|
2025
|
+
state
|
|
2026
|
+
}) => {
|
|
2027
|
+
const result = state.metadata(VALIDATOR_MEMO)();
|
|
2028
|
+
return _isPromise(result) ? result : undefined;
|
|
2029
|
+
},
|
|
2030
|
+
factory: params => {
|
|
2031
|
+
return resource({
|
|
2032
|
+
params,
|
|
2033
|
+
loader: async ({
|
|
2034
|
+
params
|
|
2035
|
+
}) => (await params)?.issues ?? []
|
|
2036
|
+
});
|
|
2037
|
+
},
|
|
2038
|
+
onSuccess: (issues, {
|
|
2039
|
+
fieldOf
|
|
2040
|
+
}) => {
|
|
2041
|
+
return issues.map(issue => standardIssueToFormTreeError(fieldOf(path), issue));
|
|
2042
|
+
},
|
|
2043
|
+
onError: () => {}
|
|
2044
|
+
});
|
|
2045
|
+
}
|
|
2046
|
+
function standardIssueToFormTreeError(field, issue) {
|
|
2047
|
+
let target = field;
|
|
2048
|
+
for (const pathPart of issue.path ?? []) {
|
|
2049
|
+
const pathKey = typeof pathPart === 'object' ? pathPart.key : pathPart;
|
|
2050
|
+
target = target[pathKey];
|
|
2051
|
+
}
|
|
2052
|
+
return addDefaultField(standardSchemaError(issue), target);
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
export { AggregateMetadataKey, CustomValidationError, EmailValidationError, FIELD, Field, MAX, MAX_LENGTH, MIN, MIN_LENGTH, MaxLengthValidationError, MaxValidationError, MetadataKey, MinLengthValidationError, MinValidationError, NgValidationError, PATTERN, PatternValidationError, REQUIRED, RequiredValidationError, StandardSchemaValidationError, aggregateMetadata, andMetadataKey, apply, applyEach, applyWhen, applyWhenValue, createMetadataKey, customError, disabled, email, emailError, form, hidden, listMetadataKey, max, maxError, maxLength, maxLengthError, maxMetadataKey, metadata, min, minError, minLength, minLengthError, minMetadataKey, orMetadataKey, pattern, patternError, readonly, reducedMetadataKey, required, requiredError, schema, standardSchemaError, submit, validate, validateAsync, validateHttp, validateStandardSchema, validateTree };
|
|
2056
|
+
//# sourceMappingURL=signals.mjs.map
|