@projectcaluma/ember-form 10.0.2 → 11.0.0-beta.11
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/CHANGELOG.md +1194 -0
- package/addon/components/cf-content.hbs +38 -41
- package/addon/components/cf-content.js +48 -29
- package/addon/components/cf-field/info.hbs +2 -2
- package/addon/components/cf-field/info.js +0 -15
- package/addon/components/cf-field/input/action-button.hbs +17 -19
- package/addon/components/cf-field/input/action-button.js +6 -10
- package/addon/components/cf-field/input/checkbox.hbs +6 -2
- package/addon/components/cf-field/input/checkbox.js +9 -29
- package/addon/components/cf-field/input/date.hbs +9 -5
- package/addon/components/cf-field/input/date.js +31 -10
- package/addon/components/cf-field/input/file.hbs +2 -2
- package/addon/components/cf-field/input/file.js +10 -11
- package/addon/components/cf-field/input/float.hbs +4 -4
- package/addon/components/cf-field/input/integer.hbs +5 -5
- package/addon/components/cf-field/input/radio.hbs +4 -1
- package/addon/components/cf-field/input/static.hbs +1 -1
- package/addon/components/cf-field/input/table.hbs +24 -24
- package/addon/components/cf-field/input/table.js +12 -10
- package/addon/components/cf-field/input/text.hbs +5 -5
- package/addon/components/cf-field/input/textarea.hbs +6 -5
- package/addon/components/cf-field/input.hbs +10 -1
- package/addon/components/cf-field/input.js +1 -1
- package/addon/components/cf-field/label.hbs +1 -1
- package/addon/components/cf-field-value.hbs +22 -7
- package/addon/components/cf-field-value.js +8 -38
- package/addon/components/cf-field.hbs +14 -6
- package/addon/components/cf-field.js +22 -8
- package/addon/components/cf-navigation-item.hbs +2 -2
- package/addon/components/cf-navigation.hbs +4 -1
- package/addon/components/document-validity.js +17 -2
- package/addon/gql/fragments/field.graphql +40 -0
- package/addon/gql/mutations/save-document-table-answer.graphql +1 -1
- package/addon/gql/mutations/save-document.graphql +1 -0
- package/addon/gql/queries/{get-document-answers.graphql → document-answers.graphql} +2 -1
- package/addon/gql/queries/{get-document-forms.graphql → document-forms.graphql} +2 -1
- package/addon/gql/queries/{get-document-used-dynamic-options.graphql → document-used-dynamic-options.graphql} +2 -1
- package/addon/gql/queries/{get-dynamic-options.graphql → dynamic-options.graphql} +2 -1
- package/addon/gql/queries/{get-fileanswer-info.graphql → fileanswer-info.graphql} +2 -1
- package/addon/helpers/get-widget.js +50 -0
- package/addon/lib/answer.js +108 -72
- package/addon/lib/base.js +32 -23
- package/addon/lib/dependencies.js +36 -71
- package/addon/lib/document.js +92 -96
- package/addon/lib/field.js +374 -407
- package/addon/lib/fieldset.js +46 -47
- package/addon/lib/form.js +27 -15
- package/addon/lib/navigation.js +211 -192
- package/addon/lib/question.js +103 -94
- package/addon/services/caluma-store.js +10 -6
- package/app/helpers/get-widget.js +4 -0
- package/blueprints/@projectcaluma/ember-form/index.js +1 -0
- package/package.json +30 -25
- package/addon/components/cf-navigation.js +0 -9
- package/addon/instance-initializers/setup-pikaday-i18n.js +0 -35
package/addon/lib/field.js
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
import { getOwner } from "@ember/application";
|
2
2
|
import { assert } from "@ember/debug";
|
3
|
-
import {
|
4
|
-
import { equal, not, reads } from "@ember/object/computed";
|
3
|
+
import { associateDestroyableChild } from "@ember/destroyable";
|
5
4
|
import { inject as service } from "@ember/service";
|
6
5
|
import { camelize } from "@ember/string";
|
6
|
+
import { isEmpty } from "@ember/utils";
|
7
|
+
import { tracked } from "@glimmer/tracking";
|
7
8
|
import { queryManager } from "ember-apollo-client";
|
8
|
-
import {
|
9
|
+
import { restartableTask, lastValue, dropTask } from "ember-concurrency";
|
9
10
|
import { validate } from "ember-validators";
|
10
|
-
import cloneDeep from "lodash.clonedeep";
|
11
11
|
import isEqual from "lodash.isequal";
|
12
|
-
import {
|
12
|
+
import { cached } from "tracked-toolbox";
|
13
13
|
|
14
14
|
import { decodeId } from "@projectcaluma/ember-core/helpers/decode-id";
|
15
15
|
import saveDocumentDateAnswerMutation from "@projectcaluma/ember-form/gql/mutations/save-document-date-answer.graphql";
|
@@ -19,12 +19,9 @@ import saveDocumentIntegerAnswerMutation from "@projectcaluma/ember-form/gql/mut
|
|
19
19
|
import saveDocumentListAnswerMutation from "@projectcaluma/ember-form/gql/mutations/save-document-list-answer.graphql";
|
20
20
|
import saveDocumentStringAnswerMutation from "@projectcaluma/ember-form/gql/mutations/save-document-string-answer.graphql";
|
21
21
|
import saveDocumentTableAnswerMutation from "@projectcaluma/ember-form/gql/mutations/save-document-table-answer.graphql";
|
22
|
-
import getDocumentUsedDynamicOptionsQuery from "@projectcaluma/ember-form/gql/queries/
|
22
|
+
import getDocumentUsedDynamicOptionsQuery from "@projectcaluma/ember-form/gql/queries/document-used-dynamic-options.graphql";
|
23
23
|
import Base from "@projectcaluma/ember-form/lib/base";
|
24
|
-
import
|
25
|
-
nestedDependencyParents,
|
26
|
-
dependencies,
|
27
|
-
} from "@projectcaluma/ember-form/lib/dependencies";
|
24
|
+
import dependencies from "@projectcaluma/ember-form/lib/dependencies";
|
28
25
|
|
29
26
|
export const TYPE_MAP = {
|
30
27
|
TextQuestion: "StringAnswer",
|
@@ -42,10 +39,20 @@ export const TYPE_MAP = {
|
|
42
39
|
DateQuestion: "DateAnswer",
|
43
40
|
};
|
44
41
|
|
45
|
-
const
|
42
|
+
const MUTATION_MAP = {
|
43
|
+
FloatAnswer: saveDocumentFloatAnswerMutation,
|
44
|
+
IntegerAnswer: saveDocumentIntegerAnswerMutation,
|
45
|
+
StringAnswer: saveDocumentStringAnswerMutation,
|
46
|
+
ListAnswer: saveDocumentListAnswerMutation,
|
47
|
+
FileAnswer: saveDocumentFileAnswerMutation,
|
48
|
+
DateAnswer: saveDocumentDateAnswerMutation,
|
49
|
+
TableAnswer: saveDocumentTableAnswerMutation,
|
50
|
+
};
|
51
|
+
|
52
|
+
const fieldIsHiddenOrEmpty = (field) => {
|
46
53
|
return (
|
47
54
|
field.hidden ||
|
48
|
-
(field.question.
|
55
|
+
(!field.question.isTable &&
|
49
56
|
(field.answer.value === null || field.answer.value === undefined))
|
50
57
|
);
|
51
58
|
};
|
@@ -55,61 +62,44 @@ const fieldIsHidden = (field) => {
|
|
55
62
|
*
|
56
63
|
* @class Field
|
57
64
|
*/
|
58
|
-
export default Base
|
59
|
-
|
60
|
-
saveDocumentIntegerAnswerMutation,
|
61
|
-
saveDocumentStringAnswerMutation,
|
62
|
-
saveDocumentListAnswerMutation,
|
63
|
-
saveDocumentFileAnswerMutation,
|
64
|
-
saveDocumentDateAnswerMutation,
|
65
|
-
saveDocumentTableAnswerMutation,
|
66
|
-
|
67
|
-
intl: service(),
|
68
|
-
calumaStore: service(),
|
69
|
-
validator: service(),
|
70
|
-
|
71
|
-
apollo: queryManager(),
|
72
|
-
|
73
|
-
init(...args) {
|
74
|
-
assert("A document must be passed", this.document);
|
75
|
-
|
76
|
-
defineProperty(this, "pk", {
|
77
|
-
writable: false,
|
78
|
-
value: `${this.document.pk}:Question:${this.raw.question.slug}`,
|
79
|
-
});
|
65
|
+
export default class Field extends Base {
|
66
|
+
@service intl;
|
80
67
|
|
81
|
-
|
68
|
+
@queryManager apollo;
|
82
69
|
|
83
|
-
|
84
|
-
|
70
|
+
constructor({ fieldset, ...args }) {
|
71
|
+
assert("`fieldset` must be passed as an argument", fieldset);
|
85
72
|
|
86
|
-
|
87
|
-
},
|
73
|
+
super({ fieldset, ...args });
|
88
74
|
|
89
|
-
|
90
|
-
this._super(...args);
|
75
|
+
this.fieldset = fieldset;
|
91
76
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
77
|
+
this.pushIntoStore();
|
78
|
+
|
79
|
+
this._createQuestion();
|
80
|
+
this._createAnswer();
|
81
|
+
}
|
96
82
|
|
97
83
|
_createQuestion() {
|
84
|
+
const owner = getOwner(this);
|
85
|
+
|
98
86
|
const question =
|
99
87
|
this.calumaStore.find(`Question:${this.raw.question.slug}`) ||
|
100
|
-
|
101
|
-
.
|
102
|
-
|
88
|
+
new (owner.factoryFor("caluma-model:question").class)({
|
89
|
+
raw: this.raw.question,
|
90
|
+
owner,
|
91
|
+
});
|
103
92
|
|
104
93
|
if (question.isDynamic) {
|
105
94
|
question.loadDynamicOptions.perform();
|
106
95
|
}
|
107
96
|
|
108
|
-
this.
|
109
|
-
}
|
97
|
+
this.question = question;
|
98
|
+
}
|
110
99
|
|
111
100
|
_createAnswer() {
|
112
|
-
const
|
101
|
+
const owner = getOwner(this);
|
102
|
+
const Answer = owner.factoryFor("caluma-model:answer").class;
|
113
103
|
let answer;
|
114
104
|
|
115
105
|
// no answer passed, create an empty one
|
@@ -121,182 +111,188 @@ export default Base.extend({
|
|
121
111
|
return;
|
122
112
|
}
|
123
113
|
|
124
|
-
answer =
|
114
|
+
answer = new Answer({
|
125
115
|
raw: {
|
116
|
+
id: null,
|
126
117
|
__typename: answerType,
|
127
118
|
question: { slug: this.raw.question.slug },
|
128
119
|
[camelize(answerType.replace(/Answer$/, "Value"))]: null,
|
129
120
|
},
|
130
121
|
field: this,
|
122
|
+
owner,
|
131
123
|
});
|
132
124
|
} else {
|
133
125
|
answer =
|
134
126
|
this.calumaStore.find(`Answer:${decodeId(this.raw.answer.id)}`) ||
|
135
|
-
|
127
|
+
new Answer({ raw: this.raw.answer, field: this, owner });
|
136
128
|
}
|
137
129
|
|
138
|
-
this.
|
139
|
-
}
|
130
|
+
this.answer = associateDestroyableChild(this, answer);
|
131
|
+
}
|
140
132
|
|
141
133
|
/**
|
142
134
|
* The question to this field
|
143
135
|
*
|
144
136
|
* @property {Question} question
|
145
|
-
* @accessor
|
146
137
|
*/
|
147
|
-
question
|
138
|
+
question = null;
|
148
139
|
|
149
140
|
/**
|
150
141
|
* The answer to this field. It is possible for this to be `null` if the
|
151
142
|
* question is of the static question type.
|
152
143
|
*
|
153
144
|
* @property {Answer} answer
|
154
|
-
* @accessor
|
155
145
|
*/
|
156
|
-
answer
|
146
|
+
answer = null;
|
147
|
+
|
148
|
+
/**
|
149
|
+
* The raw error objects which are later translated to readable messages.
|
150
|
+
*
|
151
|
+
* @property {Object[]} _errors
|
152
|
+
* @private
|
153
|
+
*/
|
154
|
+
@tracked _errors = [];
|
155
|
+
|
156
|
+
/**
|
157
|
+
* Currently rendered field components that use this field. This is used in
|
158
|
+
* the document validity component to await all current save tasks before
|
159
|
+
* validating.
|
160
|
+
*
|
161
|
+
* @property {Set} _components
|
162
|
+
* @private
|
163
|
+
*/
|
164
|
+
_components = new Set();
|
165
|
+
|
166
|
+
/**
|
167
|
+
* The primary key of the field. Consists of the document and question primary
|
168
|
+
* keys.
|
169
|
+
*
|
170
|
+
* @property {String} pk
|
171
|
+
*/
|
172
|
+
@cached
|
173
|
+
get pk() {
|
174
|
+
return `${this.document.pk}:Question:${this.raw.question.slug}`;
|
175
|
+
}
|
157
176
|
|
158
177
|
/**
|
159
178
|
* Whether the field is valid.
|
160
179
|
*
|
161
180
|
* @property {Boolean} isValid
|
162
|
-
* @accessor
|
163
181
|
*/
|
164
|
-
isValid
|
182
|
+
get isValid() {
|
183
|
+
return this.errors.length === 0;
|
184
|
+
}
|
165
185
|
|
166
186
|
/**
|
167
187
|
* Whether the field is invalid.
|
168
188
|
*
|
169
189
|
* @property {Boolean} isInvalid
|
170
|
-
* @accessor
|
171
190
|
*/
|
172
|
-
isInvalid
|
191
|
+
get isInvalid() {
|
192
|
+
return !this.isValid;
|
193
|
+
}
|
173
194
|
|
174
195
|
/**
|
175
196
|
* Whether the field is new (never saved to the backend service or empty)
|
176
197
|
*
|
177
198
|
* @property {Boolean} isNew
|
178
|
-
* @accessor
|
179
199
|
*/
|
180
|
-
|
200
|
+
get isNew() {
|
181
201
|
return !this.answer || this.answer.isNew;
|
182
|
-
}
|
202
|
+
}
|
183
203
|
|
184
204
|
/**
|
185
205
|
* Only table values, this is used for certain computed property keys.
|
186
206
|
*
|
187
207
|
* @property {Document[]} tableValue
|
188
|
-
* @accessor
|
189
208
|
*/
|
190
|
-
tableValue
|
209
|
+
get tableValue() {
|
191
210
|
return this.question.isTable ? this.value : [];
|
192
|
-
}
|
211
|
+
}
|
193
212
|
|
194
213
|
/**
|
195
214
|
* Whether the field has the defined default answer of the question as value.
|
196
215
|
*
|
197
216
|
* @property {Boolean} isDefault
|
198
|
-
* @accessor
|
199
217
|
*/
|
200
|
-
isDefault
|
201
|
-
|
202
|
-
"tableValue.@each.flatAnswerMap",
|
203
|
-
"question.{isTable,defaultValue}",
|
204
|
-
function () {
|
205
|
-
if (!this.value || !this.question.defaultValue) return false;
|
218
|
+
get isDefault() {
|
219
|
+
if (!this.value || !this.question.defaultValue) return false;
|
206
220
|
|
207
|
-
|
208
|
-
|
209
|
-
|
221
|
+
const value = this.question.isTable
|
222
|
+
? this.tableValue.map((doc) => doc.flatAnswerMap)
|
223
|
+
: this.value;
|
210
224
|
|
211
|
-
|
212
|
-
|
213
|
-
),
|
225
|
+
return isEqual(value, this.question.defaultValue);
|
226
|
+
}
|
214
227
|
|
215
228
|
/**
|
216
229
|
* Whether the field is dirty. This will be true, if there is a value on the
|
217
230
|
* answer which differs from the default value of the question.
|
218
231
|
*
|
219
232
|
* @property {Boolean} isDirty
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
"isNew",
|
225
|
-
"question.isCalculated",
|
226
|
-
"validate.lastSuccessful",
|
227
|
-
function () {
|
228
|
-
if (this.question.isCalculated || this.isDefault) {
|
229
|
-
return false;
|
230
|
-
}
|
231
|
-
|
232
|
-
return Boolean(this.validate.lastSuccessful) || !this.isNew;
|
233
|
+
*/
|
234
|
+
get isDirty() {
|
235
|
+
if (this.question.isCalculated || this.isDefault) {
|
236
|
+
return false;
|
233
237
|
}
|
234
|
-
|
238
|
+
|
239
|
+
return Boolean(this.validate.lastSuccessful) || !this.isNew;
|
240
|
+
}
|
235
241
|
|
236
242
|
/**
|
237
243
|
* The type of the question
|
238
244
|
*
|
239
245
|
* @property {String} questionType
|
240
|
-
* @accessor
|
241
246
|
*/
|
242
|
-
questionType
|
247
|
+
get questionType() {
|
248
|
+
return this.question.raw.__typename;
|
249
|
+
}
|
243
250
|
|
244
251
|
/**
|
245
252
|
* The document this field belongs to
|
246
253
|
*
|
247
254
|
* @property {Document} document
|
248
|
-
* @accessor
|
249
255
|
*/
|
250
|
-
document
|
256
|
+
get document() {
|
257
|
+
return this.fieldset.document;
|
258
|
+
}
|
251
259
|
|
252
260
|
/**
|
253
261
|
* The value of the field
|
254
262
|
*
|
255
263
|
* @property {*} value
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
"calculatedValue",
|
261
|
-
"answer.value",
|
262
|
-
function () {
|
263
|
-
if (this.question.isCalculated) {
|
264
|
-
return this.calculatedValue;
|
265
|
-
}
|
266
|
-
|
267
|
-
return this.answer?.value;
|
264
|
+
*/
|
265
|
+
get value() {
|
266
|
+
if (this.question.isCalculated) {
|
267
|
+
return this.calculatedValue;
|
268
268
|
}
|
269
|
-
|
269
|
+
|
270
|
+
return this.answer?.value;
|
271
|
+
}
|
270
272
|
|
271
273
|
/**
|
272
|
-
* The computed value of a
|
274
|
+
* The computed value of a calculated float question
|
273
275
|
*
|
274
276
|
* @property {*} calculatedValue
|
275
|
-
|
276
|
-
|
277
|
-
calculatedValue
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
!this.question.isCalculated ||
|
285
|
-
!this.calculatedDependencies.every((field) => !field.hidden)
|
286
|
-
) {
|
287
|
-
return null;
|
288
|
-
}
|
277
|
+
*/
|
278
|
+
@cached
|
279
|
+
get calculatedValue() {
|
280
|
+
if (
|
281
|
+
!this.question.isCalculated ||
|
282
|
+
!this.calculatedDependencies.every((field) => !field.hidden)
|
283
|
+
) {
|
284
|
+
return null;
|
285
|
+
}
|
289
286
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
}
|
287
|
+
try {
|
288
|
+
return this.document.jexl.evalSync(
|
289
|
+
this.question.raw.calcExpression,
|
290
|
+
this.jexlContext
|
291
|
+
);
|
292
|
+
} catch (error) {
|
293
|
+
return null;
|
298
294
|
}
|
299
|
-
|
295
|
+
}
|
300
296
|
|
301
297
|
/**
|
302
298
|
* Fetch all formerly used dynamic options for this question. This will be
|
@@ -306,7 +302,8 @@ export default Base.extend({
|
|
306
302
|
* @return {Object[]} Formerly used dynamic options
|
307
303
|
* @private
|
308
304
|
*/
|
309
|
-
|
305
|
+
@dropTask
|
306
|
+
*_fetchUsedDynamicOptions() {
|
310
307
|
if (!this.question.isDynamic) return null;
|
311
308
|
|
312
309
|
const edges = yield this.apollo.query(
|
@@ -325,16 +322,15 @@ export default Base.extend({
|
|
325
322
|
slug,
|
326
323
|
label,
|
327
324
|
}));
|
328
|
-
}
|
325
|
+
}
|
329
326
|
|
330
327
|
/**
|
331
328
|
* The formerly used dynamic options for this question.
|
332
329
|
*
|
333
330
|
* @property {Object[]} usedDynamicOptions
|
334
|
-
* @accessor
|
335
|
-
*
|
336
331
|
*/
|
337
|
-
|
332
|
+
@lastValue("_fetchUsedDynamicOptions")
|
333
|
+
usedDynamicOptions;
|
338
334
|
|
339
335
|
/**
|
340
336
|
* The available options for choice questions. This only works for the
|
@@ -348,79 +344,67 @@ export default Base.extend({
|
|
348
344
|
* This will also return the disabled state of the option. An option can only
|
349
345
|
* be disabled, if it is an old value used in a dynamic question.
|
350
346
|
*
|
351
|
-
* @property {
|
352
|
-
|
353
|
-
|
354
|
-
options
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
"value",
|
359
|
-
function () {
|
360
|
-
if (!this.question.isChoice && !this.question.isMultipleChoice) {
|
361
|
-
return null;
|
362
|
-
}
|
363
|
-
|
364
|
-
const selected =
|
365
|
-
(this.question.isMultipleChoice ? this.value : [this.value]) || [];
|
347
|
+
* @property {null|Object[]} options
|
348
|
+
*/
|
349
|
+
@cached
|
350
|
+
get options() {
|
351
|
+
if (!this.question.isChoice && !this.question.isMultipleChoice) {
|
352
|
+
return null;
|
353
|
+
}
|
366
354
|
|
367
|
-
|
368
|
-
|
369
|
-
);
|
355
|
+
const selected =
|
356
|
+
(this.question.isMultipleChoice ? this.value : [this.value]) || [];
|
370
357
|
|
371
|
-
|
372
|
-
|
373
|
-
|
358
|
+
const options = this.question.options.filter(
|
359
|
+
(option) => !option.disabled || selected.includes(option.slug)
|
360
|
+
);
|
374
361
|
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
this._fetchUsedDynamicOptions.perform();
|
379
|
-
}
|
362
|
+
const hasUnknownValue = !selected.every((slug) =>
|
363
|
+
options.find((option) => option.slug === slug)
|
364
|
+
);
|
380
365
|
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
return (
|
386
|
-
selected.includes(used.slug) &&
|
387
|
-
!options.find((option) => option.slug === used.slug)
|
388
|
-
);
|
389
|
-
})
|
390
|
-
.map((used) => ({ ...used, disabled: true })),
|
391
|
-
];
|
366
|
+
if (this.question.isDynamic && hasUnknownValue) {
|
367
|
+
if (!this._fetchUsedDynamicOptions.lastSuccessful) {
|
368
|
+
// Fetch used dynamic options if not done yet
|
369
|
+
this._fetchUsedDynamicOptions.perform();
|
392
370
|
}
|
393
371
|
|
394
|
-
return
|
372
|
+
return [
|
373
|
+
...options,
|
374
|
+
...(this.usedDynamicOptions || [])
|
375
|
+
.filter((used) => {
|
376
|
+
return (
|
377
|
+
selected.includes(used.slug) &&
|
378
|
+
!options.find((option) => option.slug === used.slug)
|
379
|
+
);
|
380
|
+
})
|
381
|
+
.map((used) => ({ ...used, disabled: true })),
|
382
|
+
];
|
395
383
|
}
|
396
|
-
|
384
|
+
|
385
|
+
return options;
|
386
|
+
}
|
397
387
|
|
398
388
|
/**
|
399
389
|
* The currently selected option. This property is only used for choice
|
400
390
|
* questions. It can either return null if no value is selected yet, an
|
401
391
|
* object for single choices or an array of objects for multiple choices.
|
402
392
|
*
|
403
|
-
* @property {
|
404
|
-
* @accessor
|
393
|
+
* @property {null|Object|Object[]} selected
|
405
394
|
*/
|
406
|
-
selected
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
function () {
|
411
|
-
if (!this.question.isChoice && !this.question.isMultipleChoice) {
|
412
|
-
return null;
|
413
|
-
}
|
395
|
+
get selected() {
|
396
|
+
if (!this.question.isChoice && !this.question.isMultipleChoice) {
|
397
|
+
return null;
|
398
|
+
}
|
414
399
|
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
400
|
+
const selected = this.options.filter(({ slug }) =>
|
401
|
+
this.question.isMultipleChoice
|
402
|
+
? (this.value || []).includes(slug)
|
403
|
+
: this.value === slug
|
404
|
+
);
|
420
405
|
|
421
|
-
|
422
|
-
|
423
|
-
),
|
406
|
+
return this.question.isMultipleChoice ? selected : selected[0];
|
407
|
+
}
|
424
408
|
|
425
409
|
/**
|
426
410
|
* The field's JEXL context.
|
@@ -435,33 +419,25 @@ export default Base.extend({
|
|
435
419
|
* - `info.root.formMeta`: The new property for the root form meta.
|
436
420
|
*
|
437
421
|
* @property {Object} jexlContext
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
form: parent.slug,
|
458
|
-
formMeta: parent.meta,
|
459
|
-
}
|
460
|
-
: null,
|
461
|
-
},
|
462
|
-
};
|
463
|
-
}
|
464
|
-
),
|
422
|
+
*/
|
423
|
+
get jexlContext() {
|
424
|
+
const parent = this.fieldset.field?.fieldset.form;
|
425
|
+
|
426
|
+
return {
|
427
|
+
...this.document.jexlContext,
|
428
|
+
info: {
|
429
|
+
...this.document.jexlContext.info,
|
430
|
+
form: this.fieldset.form.slug,
|
431
|
+
formMeta: this.fieldset.form.raw.meta,
|
432
|
+
parent: parent
|
433
|
+
? {
|
434
|
+
form: parent.slug,
|
435
|
+
formMeta: parent.raw.meta,
|
436
|
+
}
|
437
|
+
: null,
|
438
|
+
},
|
439
|
+
};
|
440
|
+
}
|
465
441
|
|
466
442
|
/**
|
467
443
|
* Fields that are referenced in the `calcExpression` JEXL expression
|
@@ -470,14 +446,8 @@ export default Base.extend({
|
|
470
446
|
* expression needs to be re-evaluated.
|
471
447
|
*
|
472
448
|
* @property {Field[]} calculatedDependencies
|
473
|
-
* @accessor
|
474
449
|
*/
|
475
|
-
|
476
|
-
"question.calcExpression"
|
477
|
-
),
|
478
|
-
calculatedDependencies: dependencies("question.calcExpression", {
|
479
|
-
nestedParentsPath: "_calculatedNestedDependencyParents",
|
480
|
-
}),
|
450
|
+
@dependencies("question.raw.calcExpression") calculatedDependencies;
|
481
451
|
|
482
452
|
/**
|
483
453
|
* Fields that are referenced in the `isHidden` JEXL expression
|
@@ -486,12 +456,8 @@ export default Base.extend({
|
|
486
456
|
* expression needs to be re-evaluated.
|
487
457
|
*
|
488
458
|
* @property {Field[]} hiddenDependencies
|
489
|
-
* @accessor
|
490
459
|
*/
|
491
|
-
|
492
|
-
hiddenDependencies: dependencies("question.isHidden", {
|
493
|
-
nestedParentsPath: "_hiddenNestedDependencyParents",
|
494
|
-
}),
|
460
|
+
@dependencies("question.raw.isHidden") hiddenDependencies;
|
495
461
|
|
496
462
|
/**
|
497
463
|
* Fields that are referenced in the `isRequired` JEXL expression
|
@@ -500,14 +466,8 @@ export default Base.extend({
|
|
500
466
|
* expression needs to be re-evaluated.
|
501
467
|
*
|
502
468
|
* @property {Field[]} optionalDependencies
|
503
|
-
* @accessor
|
504
469
|
*/
|
505
|
-
|
506
|
-
"question.isRequired"
|
507
|
-
),
|
508
|
-
optionalDependencies: dependencies("question.isRequired", {
|
509
|
-
nestedParentsPath: "_optionalNestedDependencyParents",
|
510
|
-
}),
|
470
|
+
@dependencies("question.raw.isRequired") optionalDependencies;
|
511
471
|
|
512
472
|
/**
|
513
473
|
* The field's hidden state
|
@@ -515,99 +475,85 @@ export default Base.extend({
|
|
515
475
|
* A question is hidden if:
|
516
476
|
* - The form question field of the fieldset is hidden
|
517
477
|
* - All depending field (used in the expression) are hidden
|
518
|
-
* - The evaluated `question.isHidden` expression returns `true`
|
478
|
+
* - The evaluated `question.raw.isHidden` expression returns `true`
|
519
479
|
*
|
520
480
|
* @property {Boolean} hidden
|
521
481
|
*/
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
this.fieldset.field?.hidden ||
|
532
|
-
(this.hiddenDependencies.length &&
|
533
|
-
this.hiddenDependencies.every(fieldIsHidden))
|
534
|
-
) {
|
535
|
-
return true;
|
536
|
-
}
|
482
|
+
@cached
|
483
|
+
get hidden() {
|
484
|
+
if (
|
485
|
+
this.fieldset.field?.hidden ||
|
486
|
+
(this.hiddenDependencies.length &&
|
487
|
+
this.hiddenDependencies.every(fieldIsHiddenOrEmpty))
|
488
|
+
) {
|
489
|
+
return true;
|
490
|
+
}
|
537
491
|
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
}
|
492
|
+
try {
|
493
|
+
return this.document.jexl.evalSync(
|
494
|
+
this.question.raw.isHidden,
|
495
|
+
this.jexlContext
|
496
|
+
);
|
497
|
+
} catch (error) {
|
498
|
+
throw new Error(
|
499
|
+
`Error while evaluating \`isHidden\` expression on field \`${this.pk}\`: ${error.message}`
|
500
|
+
);
|
548
501
|
}
|
549
|
-
|
502
|
+
}
|
550
503
|
|
551
504
|
/**
|
552
505
|
* The field's optional state
|
553
506
|
*
|
554
507
|
* The field is optional if:
|
508
|
+
* - The question is of the type form or calculated float
|
555
509
|
* - The form question field of the fieldset is hidden
|
556
510
|
* - All depending fields (used in the expression) are hidden
|
557
|
-
* - The evaluated `question.isRequired` expression returns `false`
|
511
|
+
* - The evaluated `question.raw.isRequired` expression returns `false`
|
558
512
|
* - The question type is FormQuestion or CalculatedFloatQuestion
|
559
513
|
*
|
560
514
|
* @property {Boolean} optional
|
561
515
|
*/
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
["FormQuestion", "CalculatedFloatQuestion"].includes(
|
573
|
-
this.question.__typename
|
574
|
-
) ||
|
575
|
-
(this.optionalDependencies.length &&
|
576
|
-
this.optionalDependencies.every(fieldIsHidden))
|
577
|
-
) {
|
578
|
-
return true;
|
579
|
-
}
|
516
|
+
@cached
|
517
|
+
get optional() {
|
518
|
+
if (
|
519
|
+
["FormQuestion", "CalculatedFloatQuestion"].includes(this.questionType) ||
|
520
|
+
this.fieldset.field?.hidden ||
|
521
|
+
(this.optionalDependencies.length &&
|
522
|
+
this.optionalDependencies.every(fieldIsHiddenOrEmpty))
|
523
|
+
) {
|
524
|
+
return true;
|
525
|
+
}
|
580
526
|
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
}
|
527
|
+
try {
|
528
|
+
return !this.document.jexl.evalSync(
|
529
|
+
this.question.raw.isRequired,
|
530
|
+
this.jexlContext
|
531
|
+
);
|
532
|
+
} catch (error) {
|
533
|
+
throw new Error(
|
534
|
+
`Error while evaluating \`isRequired\` expression on field \`${this.pk}\`: ${error.message}`
|
535
|
+
);
|
591
536
|
}
|
592
|
-
|
537
|
+
}
|
593
538
|
|
594
539
|
/**
|
595
540
|
* Task to save a field. This uses a different mutation for every answer
|
596
541
|
* type.
|
597
542
|
*
|
598
|
-
* @method save
|
543
|
+
* @method save
|
599
544
|
* @return {Object} The response from the server
|
600
545
|
*/
|
601
|
-
|
546
|
+
@restartableTask
|
547
|
+
*save() {
|
602
548
|
if (this.question.isCalculated) {
|
603
549
|
return;
|
604
550
|
}
|
605
551
|
|
606
|
-
const type = this.
|
552
|
+
const type = this.answer.raw.__typename;
|
607
553
|
|
608
554
|
const response = yield this.apollo.mutate(
|
609
555
|
{
|
610
|
-
mutation:
|
556
|
+
mutation: MUTATION_MAP[type],
|
611
557
|
variables: {
|
612
558
|
input: {
|
613
559
|
question: this.question.slug,
|
@@ -621,49 +567,47 @@ export default Base.extend({
|
|
621
567
|
`saveDocument${type}.answer`
|
622
568
|
);
|
623
569
|
|
624
|
-
|
625
|
-
// if the answer was new we need to set a pk an push the answer to the
|
626
|
-
// store
|
627
|
-
this.answer.set("pk", `Answer:${decodeId(response.id)}`);
|
628
|
-
|
629
|
-
this.calumaStore.push(this.answer);
|
630
|
-
}
|
570
|
+
const wasNew = this.isNew;
|
631
571
|
|
632
|
-
|
633
|
-
|
572
|
+
Object.entries(response).forEach(([key, value]) => {
|
573
|
+
this.answer.raw[key] = value;
|
574
|
+
});
|
634
575
|
|
635
|
-
|
636
|
-
|
576
|
+
if (wasNew) {
|
577
|
+
this.answer.pushIntoStore();
|
578
|
+
}
|
637
579
|
|
638
580
|
return response;
|
639
|
-
}
|
581
|
+
}
|
640
582
|
|
641
583
|
/**
|
642
|
-
* The error messages
|
584
|
+
* The translated error messages
|
643
585
|
*
|
644
586
|
* @property {String[]} errors
|
645
|
-
* @accessor
|
646
587
|
*/
|
647
|
-
|
588
|
+
@cached
|
589
|
+
get errors() {
|
648
590
|
return this._errors.map(({ type, context, value }) => {
|
649
591
|
return this.intl.t(
|
650
592
|
`caluma.form.validation.${type}`,
|
651
593
|
Object.assign({}, context, { value })
|
652
594
|
);
|
653
595
|
});
|
654
|
-
}
|
596
|
+
}
|
655
597
|
|
656
598
|
/**
|
657
599
|
* Validate the field. Every field goes through the required validation and
|
658
600
|
* the validation for the given question type. This mutates the `errors` on
|
659
601
|
* the field.
|
660
602
|
*
|
661
|
-
* @method validate
|
603
|
+
* @method validate
|
662
604
|
*/
|
663
|
-
|
664
|
-
|
605
|
+
@restartableTask
|
606
|
+
*validate() {
|
607
|
+
const specificValidation = this[`_validate${this.questionType}`];
|
608
|
+
|
665
609
|
assert(
|
666
|
-
`Missing validation function for ${this.
|
610
|
+
`Missing validation function for ${this.questionType}`,
|
667
611
|
specificValidation
|
668
612
|
);
|
669
613
|
|
@@ -672,7 +616,7 @@ export default Base.extend({
|
|
672
616
|
specificValidation,
|
673
617
|
];
|
674
618
|
|
675
|
-
const errors = (yield all(
|
619
|
+
const errors = (yield Promise.all(
|
676
620
|
validationFns.map(async (fn) => {
|
677
621
|
const res = await fn.call(this);
|
678
622
|
|
@@ -682,121 +626,144 @@ export default Base.extend({
|
|
682
626
|
.reduce((arr, e) => [...arr, ...e], []) // flatten the array
|
683
627
|
.filter((e) => typeof e === "object");
|
684
628
|
|
685
|
-
this.
|
686
|
-
}
|
629
|
+
this._errors = errors;
|
630
|
+
}
|
631
|
+
|
632
|
+
/**
|
633
|
+
* Validate the value against the regexes of the given format validators.
|
634
|
+
*
|
635
|
+
* @method _validateFormatValidators
|
636
|
+
* @return {Array<Boolean|Object>} An array of error objects or `true`
|
637
|
+
* @private
|
638
|
+
*/
|
639
|
+
_validateFormatValidators() {
|
640
|
+
const validators =
|
641
|
+
this.question.raw.formatValidators?.edges.map((edge) => edge.node) ?? [];
|
642
|
+
const value = this.answer.value;
|
643
|
+
|
644
|
+
if (isEmpty(value)) {
|
645
|
+
// empty values should not be validated since they are handled by the
|
646
|
+
// requiredness validation
|
647
|
+
return validators.map(() => true);
|
648
|
+
}
|
649
|
+
|
650
|
+
return validators.map((validator) => {
|
651
|
+
return (
|
652
|
+
new RegExp(validator.regex).test(value) || {
|
653
|
+
type: "format",
|
654
|
+
context: { errorMsg: validator.errorMsg },
|
655
|
+
value,
|
656
|
+
}
|
657
|
+
);
|
658
|
+
});
|
659
|
+
}
|
687
660
|
|
688
661
|
/**
|
689
662
|
* Method to validate if a question is required or not.
|
690
663
|
*
|
691
664
|
* @method _validateRequired
|
692
|
-
* @return {
|
693
|
-
* @
|
665
|
+
* @return {Boolean|Object} Returns an object if invalid or true if valid
|
666
|
+
* @private
|
694
667
|
*/
|
695
|
-
|
668
|
+
_validateRequired() {
|
696
669
|
return (
|
697
670
|
this.optional ||
|
698
|
-
validate("presence", this.
|
671
|
+
validate("presence", this.answer.value, { presence: true })
|
699
672
|
);
|
700
|
-
}
|
673
|
+
}
|
701
674
|
|
702
675
|
/**
|
703
676
|
* Method to validate a text question. This checks if the value longer than
|
704
677
|
* predefined by the question.
|
705
678
|
*
|
706
679
|
* @method _validateTextQuestion
|
707
|
-
* @return {Object
|
708
|
-
* @
|
680
|
+
* @return {Array<Boolean|Object>} An array of error objects or `true`
|
681
|
+
* @private
|
709
682
|
*/
|
710
|
-
|
683
|
+
_validateTextQuestion() {
|
711
684
|
return [
|
712
|
-
...
|
713
|
-
|
714
|
-
this.
|
715
|
-
|
716
|
-
validate("length", this.get("answer.value"), {
|
717
|
-
min: this.get("question.textMinLength") || 0,
|
718
|
-
max: this.get("question.textMaxLength") || Number.POSITIVE_INFINITY,
|
685
|
+
...this._validateFormatValidators(),
|
686
|
+
validate("length", this.answer.value, {
|
687
|
+
min: this.question.raw.textMinLength || 0,
|
688
|
+
max: this.question.raw.textMaxLength || Number.POSITIVE_INFINITY,
|
719
689
|
}),
|
720
690
|
];
|
721
|
-
}
|
691
|
+
}
|
722
692
|
|
723
693
|
/**
|
724
694
|
* Method to validate a textarea question. This checks if the value longer
|
725
695
|
* than predefined by the question.
|
726
696
|
*
|
727
697
|
* @method _validateTextareaQuestion
|
728
|
-
* @return {Object
|
729
|
-
* @
|
698
|
+
* @return {Array<Boolean|Object>} An array of error objects or `true`
|
699
|
+
* @private
|
730
700
|
*/
|
731
|
-
|
701
|
+
_validateTextareaQuestion() {
|
732
702
|
return [
|
733
|
-
...
|
734
|
-
|
735
|
-
this.
|
736
|
-
|
737
|
-
validate("length", this.get("answer.value"), {
|
738
|
-
min: this.get("question.textareaMinLength") || 0,
|
739
|
-
max: this.get("question.textareaMaxLength") || Number.POSITIVE_INFINITY,
|
703
|
+
...this._validateFormatValidators(),
|
704
|
+
validate("length", this.answer.value, {
|
705
|
+
min: this.question.raw.textareaMinLength || 0,
|
706
|
+
max: this.question.raw.textareaMaxLength || Number.POSITIVE_INFINITY,
|
740
707
|
}),
|
741
708
|
];
|
742
|
-
}
|
709
|
+
}
|
743
710
|
|
744
711
|
/**
|
745
712
|
* Method to validate an integer question. This checks if the value is bigger
|
746
713
|
* or less than the options provided by the question.
|
747
714
|
*
|
748
715
|
* @method _validateIntegerQuestion
|
749
|
-
* @return {Object
|
750
|
-
* @
|
716
|
+
* @return {Boolean|Object} Returns an object if invalid or true if valid
|
717
|
+
* @private
|
751
718
|
*/
|
752
719
|
_validateIntegerQuestion() {
|
753
|
-
return validate("number", this.
|
720
|
+
return validate("number", this.answer.value, {
|
754
721
|
integer: true,
|
755
|
-
gte: this.
|
756
|
-
lte: this.
|
722
|
+
gte: this.question.raw.integerMinValue || Number.NEGATIVE_INFINITY,
|
723
|
+
lte: this.question.raw.integerMaxValue || Number.POSITIVE_INFINITY,
|
757
724
|
});
|
758
|
-
}
|
725
|
+
}
|
759
726
|
|
760
727
|
/**
|
761
728
|
* Method to validate a float question. This checks if the value is bigger or
|
762
729
|
* less than the options provided by the question.
|
763
730
|
*
|
764
731
|
* @method _validateFloatQuestion
|
765
|
-
* @return {Object
|
766
|
-
* @
|
732
|
+
* @return {Boolean|Object} Returns an object if invalid or true if valid
|
733
|
+
* @private
|
767
734
|
*/
|
768
735
|
_validateFloatQuestion() {
|
769
|
-
return validate("number", this.
|
770
|
-
gte: this.
|
771
|
-
lte: this.
|
736
|
+
return validate("number", this.answer.value, {
|
737
|
+
gte: this.question.raw.floatMinValue || Number.NEGATIVE_INFINITY,
|
738
|
+
lte: this.question.raw.floatMaxValue || Number.POSITIVE_INFINITY,
|
772
739
|
});
|
773
|
-
}
|
740
|
+
}
|
774
741
|
|
775
742
|
/**
|
776
743
|
* Method to validate a radio question. This checks if the value is included
|
777
744
|
* in the provided options of the question.
|
778
745
|
*
|
779
746
|
* @method _validateChoiceQuestion
|
780
|
-
* @return {Object
|
781
|
-
* @
|
747
|
+
* @return {Boolean|Object} Returns an object if invalid or true if valid
|
748
|
+
* @private
|
782
749
|
*/
|
783
750
|
_validateChoiceQuestion() {
|
784
|
-
return validate("inclusion", this.
|
751
|
+
return validate("inclusion", this.answer.value, {
|
785
752
|
allowBlank: true,
|
786
753
|
in: (this.options || []).map(({ slug }) => slug),
|
787
754
|
});
|
788
|
-
}
|
755
|
+
}
|
789
756
|
|
790
757
|
/**
|
791
758
|
* Method to validate a checkbox question. This checks if the all of the
|
792
759
|
* values are included in the provided options of the question.
|
793
760
|
*
|
794
761
|
* @method _validateMultipleChoiceQuestion
|
795
|
-
* @return {
|
796
|
-
* @
|
762
|
+
* @return {Boolean|Object} Returns an object if invalid or true if valid
|
763
|
+
* @private
|
797
764
|
*/
|
798
765
|
_validateMultipleChoiceQuestion() {
|
799
|
-
const value = this.
|
766
|
+
const value = this.answer.value;
|
800
767
|
if (!value) {
|
801
768
|
return true;
|
802
769
|
}
|
@@ -805,34 +772,34 @@ export default Base.extend({
|
|
805
772
|
in: (this.options || []).map(({ slug }) => slug),
|
806
773
|
})
|
807
774
|
);
|
808
|
-
}
|
775
|
+
}
|
809
776
|
|
810
777
|
/**
|
811
778
|
* Method to validate a radio question. This checks if the value is included
|
812
779
|
* in the provided options of the question.
|
813
780
|
*
|
814
781
|
* @method _validateChoiceQuestion
|
815
|
-
* @return {Object
|
816
|
-
* @
|
782
|
+
* @return {Promise<Boolean|Object>} A promise which resolves into an object if invalid or true if valid
|
783
|
+
* @private
|
817
784
|
*/
|
818
785
|
async _validateDynamicChoiceQuestion() {
|
819
786
|
await this.question.loadDynamicOptions.perform();
|
820
787
|
|
821
|
-
return validate("inclusion", this.
|
788
|
+
return validate("inclusion", this.answer.value, {
|
822
789
|
in: (this.options || []).map(({ slug }) => slug),
|
823
790
|
});
|
824
|
-
}
|
791
|
+
}
|
825
792
|
|
826
793
|
/**
|
827
794
|
* Method to validate a checkbox question. This checks if the all of the
|
828
795
|
* values are included in the provided options of the question.
|
829
796
|
*
|
830
797
|
* @method _validateMultipleChoiceQuestion
|
831
|
-
* @return {
|
832
|
-
* @
|
798
|
+
* @return {Promise<Boolean[]|Object[]|Mixed[]>} A promise which resolves into an array of objects if invalid or true if valid
|
799
|
+
* @private
|
833
800
|
*/
|
834
801
|
async _validateDynamicMultipleChoiceQuestion() {
|
835
|
-
const value = this.
|
802
|
+
const value = this.answer.value;
|
836
803
|
|
837
804
|
if (!value) {
|
838
805
|
return true;
|
@@ -845,45 +812,45 @@ export default Base.extend({
|
|
845
812
|
in: (this.options || []).map(({ slug }) => slug),
|
846
813
|
});
|
847
814
|
});
|
848
|
-
}
|
815
|
+
}
|
849
816
|
|
850
817
|
/**
|
851
818
|
* Dummy method for the validation of file uploads.
|
852
819
|
*
|
853
820
|
* @method _validateFileQuestion
|
854
|
-
* @return {
|
821
|
+
* @return {Boolean} Always returns true
|
855
822
|
* @private
|
856
823
|
*/
|
857
824
|
_validateFileQuestion() {
|
858
|
-
return
|
859
|
-
}
|
825
|
+
return true;
|
826
|
+
}
|
860
827
|
|
861
828
|
/**
|
862
829
|
* Method to validate a date question.
|
863
830
|
*
|
864
831
|
* @method _validateDateQuestion
|
865
832
|
* @return {Object[]|Boolean[]|Mixed[]} Returns per value an object if invalid or true if valid
|
866
|
-
* @
|
833
|
+
* @private
|
867
834
|
*/
|
868
835
|
_validateDateQuestion() {
|
869
|
-
return validate("date", this.
|
836
|
+
return validate("date", this.answer.value, {
|
870
837
|
allowBlank: true,
|
871
838
|
});
|
872
|
-
}
|
839
|
+
}
|
873
840
|
|
874
841
|
/**
|
875
842
|
* Dummy method for the validation of table fields
|
876
843
|
*
|
877
844
|
* @method _validateTableQuestion
|
878
|
-
* @return {
|
845
|
+
* @return {Promise<Boolean|Object>} A promise which resolves into an object if invalid or true if valid
|
879
846
|
* @private
|
880
847
|
*/
|
881
848
|
async _validateTableQuestion() {
|
882
849
|
if (!this.value) return true;
|
883
850
|
|
884
|
-
const rowValidations = await all(
|
851
|
+
const rowValidations = await Promise.all(
|
885
852
|
this.value.map(async (row) => {
|
886
|
-
const validFields = await all(
|
853
|
+
const validFields = await Promise.all(
|
887
854
|
row.fields.map(async (field) => {
|
888
855
|
await field.validate.perform();
|
889
856
|
|
@@ -902,49 +869,49 @@ export default Base.extend({
|
|
902
869
|
value: null,
|
903
870
|
}
|
904
871
|
);
|
905
|
-
}
|
872
|
+
}
|
906
873
|
|
907
874
|
/**
|
908
875
|
* Dummy method for the validation of static fields
|
909
876
|
*
|
910
877
|
* @method _validateStaticQuestion
|
911
|
-
* @return {
|
878
|
+
* @return {Boolean} Always returns true
|
912
879
|
* @private
|
913
880
|
*/
|
914
881
|
_validateStaticQuestion() {
|
915
|
-
return
|
916
|
-
}
|
882
|
+
return true;
|
883
|
+
}
|
917
884
|
|
918
885
|
/**
|
919
886
|
* Dummy method for the validation of form fields
|
920
887
|
*
|
921
888
|
* @method _validateFormQuestion
|
922
|
-
* @return {
|
889
|
+
* @return {Boolean} Always returns true
|
923
890
|
* @private
|
924
891
|
*/
|
925
892
|
_validateFormQuestion() {
|
926
|
-
return
|
927
|
-
}
|
893
|
+
return true;
|
894
|
+
}
|
928
895
|
|
929
896
|
/**
|
930
897
|
* Dummy method for the validation of calculated float fields
|
931
898
|
*
|
932
899
|
* @method _validateCalculatedFloatQuestion
|
933
|
-
* @return {
|
900
|
+
* @return {Boolean} Always returns true
|
934
901
|
* @private
|
935
902
|
*/
|
936
903
|
_validateCalculatedFloatQuestion() {
|
937
|
-
return
|
938
|
-
}
|
904
|
+
return true;
|
905
|
+
}
|
939
906
|
|
940
907
|
/**
|
941
908
|
* Dummy method for the validation of work item button fields
|
942
909
|
*
|
943
910
|
* @method _validateActionButtonQuestion
|
944
|
-
* @return {
|
911
|
+
* @return {Boolean} Always returns true
|
945
912
|
* @private
|
946
913
|
*/
|
947
914
|
_validateActionButtonQuestion() {
|
948
|
-
return
|
949
|
-
}
|
950
|
-
}
|
915
|
+
return true;
|
916
|
+
}
|
917
|
+
}
|