@projectcaluma/ember-form 10.0.2 → 11.0.0-beta.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. package/CHANGELOG.md +1112 -0
  2. package/addon/components/cf-content.hbs +36 -39
  3. package/addon/components/cf-content.js +48 -29
  4. package/addon/components/cf-field/info.hbs +1 -1
  5. package/addon/components/cf-field/input/action-button.hbs +1 -1
  6. package/addon/components/cf-field/input/action-button.js +9 -7
  7. package/addon/components/cf-field/input/checkbox.hbs +6 -2
  8. package/addon/components/cf-field/input/checkbox.js +9 -29
  9. package/addon/components/cf-field/input/file.hbs +1 -1
  10. package/addon/components/cf-field/input/file.js +8 -9
  11. package/addon/components/cf-field/input/float.hbs +4 -4
  12. package/addon/components/cf-field/input/integer.hbs +5 -5
  13. package/addon/components/cf-field/input/radio.hbs +4 -1
  14. package/addon/components/cf-field/input/table.hbs +7 -7
  15. package/addon/components/cf-field/input/table.js +12 -10
  16. package/addon/components/cf-field/input/text.hbs +5 -5
  17. package/addon/components/cf-field/input/textarea.hbs +6 -5
  18. package/addon/components/cf-field/input.hbs +10 -1
  19. package/addon/components/cf-field/input.js +1 -1
  20. package/addon/components/cf-field/label.hbs +1 -1
  21. package/addon/components/cf-field-value.hbs +1 -1
  22. package/addon/components/cf-field-value.js +8 -13
  23. package/addon/components/cf-field.hbs +2 -2
  24. package/addon/components/cf-field.js +2 -3
  25. package/addon/components/cf-navigation-item.hbs +2 -2
  26. package/addon/components/cf-navigation.hbs +4 -1
  27. package/addon/components/document-validity.js +1 -1
  28. package/addon/gql/fragments/field.graphql +22 -0
  29. package/addon/gql/mutations/save-document-table-answer.graphql +1 -1
  30. package/addon/gql/mutations/save-document.graphql +1 -0
  31. package/addon/gql/queries/{get-document-answers.graphql → document-answers.graphql} +2 -1
  32. package/addon/gql/queries/{get-document-forms.graphql → document-forms.graphql} +2 -1
  33. package/addon/gql/queries/{get-document-used-dynamic-options.graphql → document-used-dynamic-options.graphql} +2 -1
  34. package/addon/gql/queries/{get-dynamic-options.graphql → dynamic-options.graphql} +2 -1
  35. package/addon/gql/queries/{get-fileanswer-info.graphql → fileanswer-info.graphql} +2 -1
  36. package/addon/helpers/get-widget.js +50 -0
  37. package/addon/lib/answer.js +108 -72
  38. package/addon/lib/base.js +32 -23
  39. package/addon/lib/dependencies.js +36 -71
  40. package/addon/lib/document.js +92 -96
  41. package/addon/lib/field.js +334 -401
  42. package/addon/lib/fieldset.js +46 -47
  43. package/addon/lib/form.js +27 -15
  44. package/addon/lib/navigation.js +211 -192
  45. package/addon/lib/question.js +103 -94
  46. package/addon/services/caluma-store.js +10 -6
  47. package/app/helpers/get-widget.js +4 -0
  48. package/blueprints/@projectcaluma/ember-form/index.js +1 -0
  49. package/package.json +27 -23
  50. package/addon/components/cf-navigation.js +0 -9
@@ -3,7 +3,7 @@ import Component from "@glimmer/component";
3
3
  import { queryManager } from "ember-apollo-client";
4
4
  import moment from "moment";
5
5
 
6
- import getFileAnswerInfoQuery from "@projectcaluma/ember-form/gql/queries/get-fileanswer-info.graphql";
6
+ import getFileAnswerInfoQuery from "@projectcaluma/ember-form/gql/queries/fileanswer-info.graphql";
7
7
 
8
8
  export default class CfFieldValueComponent extends Component {
9
9
  @queryManager apollo;
@@ -11,7 +11,7 @@ export default class CfFieldValueComponent extends Component {
11
11
  get value() {
12
12
  const field = this.args.field;
13
13
 
14
- switch (field.question.__typename) {
14
+ switch (field.questionType) {
15
15
  case "ChoiceQuestion":
16
16
  case "DynamicChoiceQuestion": {
17
17
  return field.selected;
@@ -23,8 +23,8 @@ export default class CfFieldValueComponent extends Component {
23
23
  case "FileQuestion": {
24
24
  const answerValue = field.answer.value;
25
25
  return {
26
- fileAnswerId: answerValue && field.answer.id,
27
- label: answerValue && answerValue.name,
26
+ fileAnswerId: answerValue && field.answer.raw.id,
27
+ label: answerValue?.name,
28
28
  };
29
29
  }
30
30
  case "DateQuestion": {
@@ -34,22 +34,17 @@ export default class CfFieldValueComponent extends Component {
34
34
  }
35
35
 
36
36
  default:
37
- return {
38
- label: field.answer.value,
39
- };
37
+ return { label: field.answer.value };
40
38
  }
41
39
  }
42
40
 
43
41
  @action
44
- async download(fileAnswerId) {
42
+ async download(id) {
45
43
  const { downloadUrl } = await this.apollo.query(
46
- {
47
- query: getFileAnswerInfoQuery,
48
- variables: { id: fileAnswerId },
49
- fetchPolicy: "cache-and-network",
50
- },
44
+ { query: getFileAnswerInfoQuery, variables: { id } },
51
45
  "node.fileValue"
52
46
  );
47
+
53
48
  window.open(downloadUrl, "_blank");
54
49
  }
55
50
  }
@@ -16,8 +16,8 @@
16
16
  {{/let}}
17
17
  </div>
18
18
 
19
- {{#if (and @field.question.infoText this.infoTextVisible)}}
20
- <CfField::info @text={{@field.question.infoText}} />
19
+ {{#if (and @field.question.raw.infoText this.infoTextVisible)}}
20
+ <CfField::info @text={{@field.question.raw.infoText}} />
21
21
  {{/if}}
22
22
 
23
23
  {{#if this.saveIndicatorVisible}}
@@ -1,8 +1,7 @@
1
1
  import { getOwner } from "@ember/application";
2
2
  import { set } from "@ember/object";
3
3
  import Component from "@glimmer/component";
4
- import { timeout } from "ember-concurrency";
5
- import { restartableTask } from "ember-concurrency-decorators";
4
+ import { timeout, restartableTask } from "ember-concurrency";
6
5
 
7
6
  import { hasQuestionType } from "@projectcaluma/ember-core/helpers/has-question-type";
8
7
 
@@ -28,7 +27,7 @@ export default class CfFieldComponent extends Component {
28
27
 
29
28
  get labelVisible() {
30
29
  return (
31
- !this.args.field?.question.meta.hideLabel &&
30
+ !this.args.field?.question.raw.meta.hideLabel &&
32
31
  !hasQuestionType(this.args.field?.question, "static", "action-button")
33
32
  );
34
33
  }
@@ -1,6 +1,6 @@
1
1
  <li
2
2
  class="cf-navigation__item uk-width-auto
3
- {{if (or @item.active @item.childrenActive) "uk-active"}}"
3
+ {{if (or @item.active @item.childrenActive) 'uk-active'}}"
4
4
  >
5
5
  <LinkTo @query={{hash displayedForm=@item.slug}}>
6
6
  {{#if (and @useAsHeading this.active)}}
@@ -19,7 +19,7 @@
19
19
  {{/if}}
20
20
  <span
21
21
  class="cf-navigation__item__icon cf-navigation__item__icon--{{@item.state}}
22
- {{if @item.dirty "cf-navigation__item__icon--dirty"}}"
22
+ {{if @item.dirty 'cf-navigation__item__icon--dirty'}}"
23
23
  ></span>
24
24
  </LinkTo>
25
25
 
@@ -1,4 +1,7 @@
1
- <ul class="uk-tab uk-tab-left uk-width-auto" {{did-insert this.goToNextItem}}>
1
+ <ul
2
+ class="uk-tab uk-tab-left uk-width-auto"
3
+ {{did-insert @navigation.goToNextItemIfNonNavigable}}
4
+ >
2
5
  {{#each @navigation.rootItems as |item|}}
3
6
  <CfNavigationItem
4
7
  @item={{item}}
@@ -1,6 +1,6 @@
1
1
  import { action } from "@ember/object";
2
2
  import Component from "@glimmer/component";
3
- import { restartableTask } from "ember-concurrency-decorators";
3
+ import { restartableTask } from "ember-concurrency";
4
4
 
5
5
  /**
6
6
  * Component to check the validity of a document
@@ -4,6 +4,7 @@
4
4
  # When changing this file the other must also receive the same changes.
5
5
 
6
6
  fragment SimpleQuestion on Question {
7
+ id
7
8
  slug
8
9
  label
9
10
  isRequired
@@ -14,6 +15,7 @@ fragment SimpleQuestion on Question {
14
15
  textMinLength: minLength
15
16
  textMaxLength: maxLength
16
17
  textDefaultAnswer: defaultAnswer {
18
+ id
17
19
  value
18
20
  }
19
21
  placeholder
@@ -22,6 +24,7 @@ fragment SimpleQuestion on Question {
22
24
  textareaMinLength: minLength
23
25
  textareaMaxLength: maxLength
24
26
  textareaDefaultAnswer: defaultAnswer {
27
+ id
25
28
  value
26
29
  }
27
30
  placeholder
@@ -30,6 +33,7 @@ fragment SimpleQuestion on Question {
30
33
  integerMinValue: minValue
31
34
  integerMaxValue: maxValue
32
35
  integerDefaultAnswer: defaultAnswer {
36
+ id
33
37
  value
34
38
  }
35
39
  placeholder
@@ -38,6 +42,7 @@ fragment SimpleQuestion on Question {
38
42
  floatMinValue: minValue
39
43
  floatMaxValue: maxValue
40
44
  floatDefaultAnswer: defaultAnswer {
45
+ id
41
46
  value
42
47
  }
43
48
  placeholder
@@ -46,6 +51,7 @@ fragment SimpleQuestion on Question {
46
51
  choiceOptions: options {
47
52
  edges {
48
53
  node {
54
+ id
49
55
  slug
50
56
  label
51
57
  isArchived
@@ -53,6 +59,7 @@ fragment SimpleQuestion on Question {
53
59
  }
54
60
  }
55
61
  choiceDefaultAnswer: defaultAnswer {
62
+ id
56
63
  value
57
64
  }
58
65
  }
@@ -60,6 +67,7 @@ fragment SimpleQuestion on Question {
60
67
  multipleChoiceOptions: options {
61
68
  edges {
62
69
  node {
70
+ id
63
71
  slug
64
72
  label
65
73
  isArchived
@@ -67,11 +75,13 @@ fragment SimpleQuestion on Question {
67
75
  }
68
76
  }
69
77
  multipleChoiceDefaultAnswer: defaultAnswer {
78
+ id
70
79
  value
71
80
  }
72
81
  }
73
82
  ... on DateQuestion {
74
83
  dateDefaultAnswer: defaultAnswer {
84
+ id
75
85
  value
76
86
  }
77
87
  }
@@ -89,8 +99,10 @@ fragment SimpleQuestion on Question {
89
99
  }
90
100
 
91
101
  fragment FieldTableQuestion on Question {
102
+ id
92
103
  ... on TableQuestion {
93
104
  rowForm {
105
+ id
94
106
  slug
95
107
  questions {
96
108
  edges {
@@ -101,6 +113,7 @@ fragment FieldTableQuestion on Question {
101
113
  }
102
114
  }
103
115
  tableDefaultAnswer: defaultAnswer {
116
+ id
104
117
  value {
105
118
  id
106
119
  answers {
@@ -108,6 +121,7 @@ fragment FieldTableQuestion on Question {
108
121
  node {
109
122
  id
110
123
  question {
124
+ id
111
125
  slug
112
126
  }
113
127
  ... on StringAnswer {
@@ -134,10 +148,12 @@ fragment FieldTableQuestion on Question {
134
148
  }
135
149
 
136
150
  fragment FieldQuestion on Question {
151
+ id
137
152
  ...SimpleQuestion
138
153
  ...FieldTableQuestion
139
154
  ... on FormQuestion {
140
155
  subForm {
156
+ id
141
157
  slug
142
158
  name
143
159
  questions {
@@ -145,10 +161,12 @@ fragment FieldQuestion on Question {
145
161
  node {
146
162
  # This part here limits our query to 2 level deep nested forms. This
147
163
  # has to be solved in another way!
164
+ id
148
165
  ...SimpleQuestion
149
166
  ...FieldTableQuestion
150
167
  ... on FormQuestion {
151
168
  subForm {
169
+ id
152
170
  slug
153
171
  name
154
172
  questions {
@@ -171,6 +189,7 @@ fragment FieldQuestion on Question {
171
189
  fragment SimpleAnswer on Answer {
172
190
  id
173
191
  question {
192
+ id
174
193
  slug
175
194
  }
176
195
  ... on StringAnswer {
@@ -187,6 +206,7 @@ fragment SimpleAnswer on Answer {
187
206
  }
188
207
  ... on FileAnswer {
189
208
  fileValue: value {
209
+ id
190
210
  uploadUrl
191
211
  downloadUrl
192
212
  metadata
@@ -199,11 +219,13 @@ fragment SimpleAnswer on Answer {
199
219
  }
200
220
 
201
221
  fragment FieldAnswer on Answer {
222
+ id
202
223
  ...SimpleAnswer
203
224
  ... on TableAnswer {
204
225
  tableValue: value {
205
226
  id
206
227
  form {
228
+ id
207
229
  slug
208
230
  questions {
209
231
  edges {
@@ -1,6 +1,6 @@
1
1
  #import * from '../fragments/field.graphql'
2
2
 
3
- mutation saveDocumentTableAnswer($input: SaveDocumentTableAnswerInput!) {
3
+ mutation SaveDocumentTableAnswer($input: SaveDocumentTableAnswerInput!) {
4
4
  saveDocumentTableAnswer(input: $input) {
5
5
  answer {
6
6
  ...FieldAnswer
@@ -12,6 +12,7 @@ mutation SaveDocument($input: SaveDocumentInput!) {
12
12
  }
13
13
  }
14
14
  form {
15
+ id
15
16
  slug
16
17
  questions {
17
18
  edges {
@@ -1,11 +1,12 @@
1
1
  #import * from '../fragments/field.graphql'
2
2
 
3
- query GetDocumentAnswers($id: ID!) {
3
+ query DocumentAnswers($id: ID!) {
4
4
  allDocuments(filter: [{ id: $id }]) {
5
5
  edges {
6
6
  node {
7
7
  id
8
8
  form {
9
+ id
9
10
  slug
10
11
  }
11
12
  workItem {
@@ -1,9 +1,10 @@
1
1
  #import FieldQuestion, FieldTableQuestion, SimpleQuestion from '../fragments/field.graphql'
2
2
 
3
- query GetDocumentForms($slug: String!) {
3
+ query DocumentForms($slug: String!) {
4
4
  allForms(filter: [{ slug: $slug }]) {
5
5
  edges {
6
6
  node {
7
+ id
7
8
  slug
8
9
  name
9
10
  meta
@@ -1,9 +1,10 @@
1
- query GetDocumentUsedDynamicOptions($document: ID!, $question: ID!) {
1
+ query DocumentUsedDynamicOptions($document: ID!, $question: ID!) {
2
2
  allUsedDynamicOptions(
3
3
  filter: [{ document: $document }, { question: $question }]
4
4
  ) {
5
5
  edges {
6
6
  node {
7
+ id
7
8
  slug
8
9
  label
9
10
  }
@@ -1,7 +1,8 @@
1
- query GetDynamicOptions($question: String!) {
1
+ query DynamicOptions($question: String!) {
2
2
  allQuestions(filter: [{ slug: $question }], first: 1) {
3
3
  edges {
4
4
  node {
5
+ id
5
6
  slug
6
7
  ... on DynamicChoiceQuestion {
7
8
  dynamicChoiceOptions: options {
@@ -1,8 +1,9 @@
1
- query GetFileAnswerInfo($id: ID!) {
1
+ query FileAnswerInfo($id: ID!) {
2
2
  node(id: $id) {
3
3
  id
4
4
  ... on FileAnswer {
5
5
  fileValue: value {
6
+ id
6
7
  uploadUrl
7
8
  downloadUrl
8
9
  metadata
@@ -0,0 +1,50 @@
1
+ import Helper from "@ember/component/helper";
2
+ import { warn } from "@ember/debug";
3
+ import { inject as service } from "@ember/service";
4
+
5
+ /**
6
+ * Helper for getting the right widget.
7
+ *
8
+ * This helper expects n objects as positional parameters. It checks if the
9
+ * object has a widget override in it's metadata. If one exists it checks if
10
+ * said widget was registered in the caluma options service and then returns
11
+ * the widget name. If it doesn't have a valid widget, the next object will be
12
+ * checked. If no object returns a valid widget, the passed default widget will
13
+ * be used.
14
+ *
15
+ * ```hbs
16
+ * {{component (get-widget field.question someobject default="cf-form") foo=bar}}
17
+ * ```
18
+ *
19
+ * @function getWidget
20
+ * @param {Array} params
21
+ * @param {Object} [options]
22
+ * @param {String} [options.default]
23
+ */
24
+ export default class GetWidgetHelper extends Helper {
25
+ @service calumaOptions;
26
+
27
+ compute(params, { default: defaultWidget = "cf-field/input" }) {
28
+ for (const obj of params) {
29
+ const widget = obj?.raw?.meta?.widgetOverride;
30
+ if (!widget) {
31
+ continue;
32
+ }
33
+ const override =
34
+ widget &&
35
+ this.calumaOptions
36
+ .getComponentOverrides()
37
+ .find(({ component }) => component === widget);
38
+
39
+ warn(
40
+ `Widget override "${widget}" is not registered. Please register it by calling \`calumaOptions.registerComponentOverride\``,
41
+ override,
42
+ { id: "ember-caluma.unregistered-override" }
43
+ );
44
+
45
+ if (override) return widget;
46
+ }
47
+
48
+ return defaultWidget;
49
+ }
50
+ }
@@ -1,130 +1,166 @@
1
1
  import { getOwner } from "@ember/application";
2
2
  import { assert } from "@ember/debug";
3
- import { computed } from "@ember/object";
4
- import { inject as service } from "@ember/service";
5
3
  import { camelize } from "@ember/string";
6
4
  import { isEmpty } from "@ember/utils";
5
+ import { dedupeTracked, cached } from "tracked-toolbox";
7
6
 
8
7
  import { decodeId } from "@projectcaluma/ember-core/helpers/decode-id";
9
8
  import Base from "@projectcaluma/ember-form/lib/base";
10
9
  import { parseDocument } from "@projectcaluma/ember-form/lib/parsers";
11
10
 
11
+ /**
12
+ * Class that automatically defines all keys of the passed object as deduped
13
+ * tracked property and assigns the initial value.
14
+ *
15
+ * @class DedupedTrackedObject
16
+ * @private
17
+ */
18
+ class DedupedTrackedObject {
19
+ constructor(obj) {
20
+ Object.entries(obj).forEach(([key, value]) => {
21
+ Object.defineProperty(
22
+ this,
23
+ key,
24
+ dedupeTracked(this, key, { initializer: () => value })
25
+ );
26
+ });
27
+ }
28
+ }
29
+
12
30
  /**
13
31
  * Object which represents an answer in context of a field
14
32
  *
15
33
  * @class Answer
16
34
  */
17
- export default Base.extend({
18
- calumaStore: service(),
35
+ export default class Answer extends Base {
36
+ constructor({ raw, field, ...args }) {
37
+ assert("`field` must be passed as an argument", field);
19
38
 
20
- init(...args) {
21
39
  assert(
22
40
  "A graphql answer `raw` must be passed",
23
- this.raw && /Answer$/.test(this.raw.__typename)
41
+ /Answer$/.test(raw?.__typename)
24
42
  );
25
43
 
26
- if (this.raw.id) {
27
- this.set("pk", `Answer:${decodeId(this.raw.id)}`);
28
- }
44
+ super({ raw, ...args });
45
+
46
+ this.field = field;
47
+ this.raw = new DedupedTrackedObject(raw);
29
48
 
30
- this._super(...args);
49
+ this.pushIntoStore();
50
+ }
51
+
52
+ /**
53
+ * The field this answer originates from
54
+ *
55
+ * @property {Field} field
56
+ */
57
+ field = null;
58
+
59
+ /**
60
+ * The raw data of the answer. This is the only `raw` property that is tracked
61
+ * since only the answers properties are changed while rendering the form.
62
+ *
63
+ * @property {DedupedTrackedObject} raw
64
+ */
65
+ raw = {};
31
66
 
32
- this.setProperties(this.raw);
33
- },
67
+ /**
68
+ * The primary key of the answer.
69
+ *
70
+ * @property {String} pk
71
+ */
72
+ @cached
73
+ get pk() {
74
+ return this.uuid && `Answer:${this.uuid}`;
75
+ }
34
76
 
35
77
  /**
36
78
  * The uuid of the answer
37
79
  *
38
80
  * @property {String} uuid
39
- * @accessor
40
81
  */
41
- uuid: computed("raw.id", function () {
82
+ @cached
83
+ get uuid() {
42
84
  return this.raw.id ? decodeId(this.raw.id) : null;
43
- }),
85
+ }
44
86
 
45
- isNew: computed("uuid", "value", function () {
87
+ /**
88
+ * Whether the answer is new. This is true when there is no object from the
89
+ * backend or the value is empty.
90
+ *
91
+ * @property {Boolean} isNew
92
+ */
93
+ @cached
94
+ get isNew() {
46
95
  return !this.uuid || isEmpty(this.value);
47
- }),
96
+ }
48
97
 
49
98
  /**
50
99
  * The name of the property in which the value is stored. This depends on the
51
100
  * type of the answer.
52
101
  *
53
- *
54
102
  * @property {String} _valueKey
55
- * @accessor
56
103
  * @private
57
104
  */
58
- _valueKey: computed("__typename", function () {
105
+ @cached
106
+ get _valueKey() {
59
107
  return (
60
- this.__typename && camelize(this.__typename.replace(/Answer$/, "Value"))
108
+ this.raw.__typename &&
109
+ camelize(this.raw.__typename.replace(/Answer$/, "Value"))
61
110
  );
62
- }),
111
+ }
63
112
 
64
113
  /**
65
114
  * The value of the answer, the type of this value depends on the type of the
66
115
  * answer. For table answers this returns an array of documents.
67
116
  *
68
117
  * @property {String|Number|String[]|Document[]} value
69
- * @computed
70
118
  */
71
- value: computed(
72
- "field.document",
73
- "_valueKey",
74
- "dateValue",
75
- "fileValue",
76
- "floatValue",
77
- "integerValue",
78
- "listValue.[]",
79
- "stringValue",
80
- "tableValue.[]",
81
- {
82
- get() {
83
- const value = this.get(this._valueKey);
84
-
85
- if (this._valueKey === "tableValue" && value) {
86
- return value.map((document) => {
87
- const existing = this.calumaStore.find(
88
- `Document:${decodeId(document.id)}`
89
- );
90
-
91
- return (
92
- existing ||
93
- getOwner(this)
94
- .factoryFor("caluma-model:document")
95
- .create({
96
- raw: parseDocument(document),
97
- parentDocument: this.field.document,
98
- })
99
- );
100
- });
101
- }
102
-
103
- return value;
104
- },
105
- set(_, value) {
106
- value = [undefined, ""].includes(value) ? null : value;
107
-
108
- if (this._valueKey) {
109
- this.set(this._valueKey, value);
110
- }
111
-
112
- return value;
113
- },
119
+ @cached
120
+ get value() {
121
+ const value = this.raw[this._valueKey];
122
+
123
+ if (this._valueKey === "tableValue" && value) {
124
+ const owner = getOwner(this);
125
+ const Document = owner.factoryFor("caluma-model:document").class;
126
+
127
+ return value.map((document) => {
128
+ if (document instanceof Document) return document;
129
+
130
+ const existing = this.calumaStore.find(
131
+ `Document:${decodeId(document.id)}`
132
+ );
133
+
134
+ return (
135
+ existing ||
136
+ new Document({
137
+ raw: parseDocument(document),
138
+ parentDocument: this.field.document,
139
+ owner,
140
+ })
141
+ );
142
+ });
143
+ }
144
+
145
+ return value;
146
+ }
147
+
148
+ set value(value) {
149
+ if (this._valueKey) {
150
+ this.raw[this._valueKey] = [undefined, ""].includes(value) ? null : value;
114
151
  }
115
- ),
152
+ }
116
153
 
117
154
  /**
118
155
  * The value serialized for a backend request.
119
156
  *
120
157
  * @property {String|Number|String[]} serializedValue
121
- * @accessor
122
158
  */
123
- serializedValue: computed("__typename", "value", function () {
124
- if (this.__typename === "TableAnswer") {
159
+ get serializedValue() {
160
+ if (this.raw.__typename === "TableAnswer") {
125
161
  return (this.value || []).map(({ uuid }) => uuid);
126
162
  }
127
163
 
128
164
  return this.value;
129
- }),
130
- });
165
+ }
166
+ }