@jinntec/fore 0.25.0 → 1.0.0-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +75 -22
  2. package/dist/fore-all.js +11 -11
  3. package/dist/fore.js +1 -1
  4. package/index.js +5 -1
  5. package/package.json +17 -6
  6. package/resources/fore.css +121 -4
  7. package/resources/toastify.css +3 -1
  8. package/src/DependencyNotifyingDomFacade.js +9 -1
  9. package/src/ForeElementMixin.js +83 -12
  10. package/src/actions/abstract-action.js +101 -27
  11. package/src/actions/fx-action.js +4 -2
  12. package/src/actions/fx-append.js +21 -18
  13. package/src/actions/fx-confirm.js +22 -0
  14. package/src/actions/fx-dispatch.js +10 -2
  15. package/src/actions/fx-insert.js +35 -30
  16. package/src/actions/fx-message.js +7 -1
  17. package/src/actions/fx-send.js +1 -1
  18. package/src/actions/fx-setvalue.js +2 -9
  19. package/src/dep_graph.js +9 -0
  20. package/src/drawdown.js +172 -0
  21. package/src/fore.js +126 -18
  22. package/src/functions/fx-function.js +2 -2
  23. package/src/fx-bind.js +11 -7
  24. package/src/fx-fore.js +283 -67
  25. package/src/fx-header.js +20 -0
  26. package/src/fx-instance.js +54 -10
  27. package/src/fx-model.js +175 -38
  28. package/src/fx-submission.js +235 -53
  29. package/src/getInScopeContext.js +2 -3
  30. package/src/ui/abstract-control.js +23 -44
  31. package/src/ui/fx-alert.js +20 -19
  32. package/src/ui/fx-container.js +9 -4
  33. package/src/ui/fx-control.js +92 -37
  34. package/src/ui/fx-inspector.js +44 -0
  35. package/src/ui/fx-items.js +104 -20
  36. package/src/ui/fx-output.js +92 -3
  37. package/src/ui/fx-repeat.js +87 -80
  38. package/src/ui/fx-repeatitem.js +38 -48
  39. package/src/ui/fx-trigger.js +49 -27
  40. package/src/xpath-evaluation.js +533 -235
  41. package/src/xpath-util.js +50 -12
@@ -1,7 +1,11 @@
1
+ import { Fore } from './fore.js';
1
2
  import { foreElementMixin } from './ForeElementMixin.js';
2
- import { evaluateXPathToString } from './xpath-evaluation.js';
3
+ import { evaluateXPathToString, evaluateXPath } from './xpath-evaluation.js';
3
4
  import getInScopeContext from './getInScopeContext.js';
4
5
 
6
+ /**
7
+ * todo: validate='false'
8
+ */
5
9
  export class FxSubmission extends foreElementMixin(HTMLElement) {
6
10
  constructor() {
7
11
  super();
@@ -9,16 +13,22 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
9
13
  }
10
14
 
11
15
  connectedCallback() {
12
- this.style.display = 'none';
16
+ // this.style.display = 'none';
17
+ this.methods = ['get', 'put', 'post', 'delete', 'head', 'urlencoded-post'];
18
+
13
19
  this.model = this.parentNode;
14
20
 
15
21
  // ### initialize properties with defaults
16
- if (!this.hasAttribute('id')) throw new Error('id is required');
22
+ // if (!this.hasAttribute('id')) throw new Error('id is required');
23
+ if (!this.hasAttribute('id')) console.warn('id is required');
17
24
  this.id = this.getAttribute('id');
18
25
 
19
26
  /** if present should be a existing instance id */
20
27
  this.instance = this.hasAttribute('instance') ? this.getAttribute('instance') : null;
21
28
 
29
+ /** if present will determine XPath where to insert a response into when mode is 'replace' */
30
+ this.into = this.hasAttribute('into') ? this.getAttribute('into') : null;
31
+
22
32
  /** http method */
23
33
  this.method = this.hasAttribute('method') ? this.getAttribute('method') : 'get';
24
34
 
@@ -30,7 +40,12 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
30
40
  /** replace might be 'all', 'instance' or 'none' */
31
41
  this.replace = this.hasAttribute('replace') ? this.getAttribute('replace') : 'all';
32
42
 
33
- if (!this.hasAttribute('url')) throw new Error(`url is required for submission: ${this.id}`);
43
+ this.serialization = this.hasAttribute('serialization')
44
+ ? this.getAttribute('serialization')
45
+ : 'xml';
46
+
47
+ // if (!this.hasAttribute('url')) throw new Error(`url is required for submission: ${this.id}`);
48
+ if (!this.hasAttribute('url')) console.warn(`url is required for submission: ${this.id}`);
34
49
  this.url = this.getAttribute('url');
35
50
 
36
51
  this.targetref = this.hasAttribute('targetref') ? this.getAttribute('targetref') : null;
@@ -40,10 +55,7 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
40
55
  : 'application/xml';
41
56
 
42
57
  this.validate = this.getAttribute('validate') ? this.getAttribute('validate') : 'true';
43
-
44
58
  this.shadowRoot.innerHTML = this.renderHTML();
45
-
46
- this.addEventListener('submit', () => this._submit());
47
59
  }
48
60
 
49
61
  // eslint-disable-next-line class-methods-use-this
@@ -53,17 +65,12 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
53
65
  `;
54
66
  }
55
67
 
56
- submit() {
57
- this.dispatchEvent(
58
- new CustomEvent('submit', {
59
- composed: true,
60
- bubbles: true,
61
- detail: {},
62
- }),
63
- );
68
+ async submit() {
69
+ await this.dispatch('submit', { submission: this });
70
+ this._submit();
64
71
  }
65
72
 
66
- _submit() {
73
+ async _submit() {
67
74
  console.log('submitting....');
68
75
  this.evalInContext();
69
76
  const model = this.getModel();
@@ -73,11 +80,12 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
73
80
  if (this.validate) {
74
81
  const valid = model.revalidate();
75
82
  if (!valid) {
83
+ console.log('validation failed. Bubmission stopped');
76
84
  return;
77
85
  }
78
86
  }
79
-
80
- this._serializeAndSend();
87
+ console.log('model updated....');
88
+ await this._serializeAndSend();
81
89
  }
82
90
 
83
91
  /**
@@ -96,7 +104,7 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
96
104
  const inscope = getInScopeContext(node, naked);
97
105
  const result = evaluateXPathToString(naked, inscope, this.getOwnerForm());
98
106
  const replaced = expr.replaceAll(match, result);
99
- console.log('result of replacing ', replaced);
107
+ console.log('replacing ', expr, ' with ', replaced);
100
108
  expr = replaced;
101
109
  });
102
110
  }
@@ -111,34 +119,74 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
111
119
  */
112
120
  async _serializeAndSend() {
113
121
  const resolvedUrl = this._evaluateAttributeTemplateExpression(this.url, this);
114
- const serializer = new XMLSerializer();
115
- let serialized = serializer.serializeToString(this.nodeset);
122
+
123
+ const instance = this.getInstance();
124
+ if (instance.type !== 'xml') {
125
+ console.error('JSON serialization is not supported yet');
126
+ return;
127
+ }
128
+
129
+ // let serialized = serializer.serializeToString(this.nodeset);
130
+ let serialized;
131
+ if (this.serialization === 'none') {
132
+ serialized = undefined;
133
+ } else {
134
+ const relevant = this.selectRelevant();
135
+ serialized = this._serialize(instance.type, relevant);
136
+ }
137
+
138
+ // let serialized = serializer.serializeToString(relevant);
116
139
  if (this.method.toLowerCase() === 'get') {
117
140
  serialized = undefined;
118
141
  }
142
+ // console.log('data being send', serialized);
143
+ // console.log('submitting data',serialized);
144
+
145
+ if (resolvedUrl === '#echo') {
146
+ let doc;
147
+ if (serialized) {
148
+ doc = new DOMParser().parseFromString(serialized, 'application/xml');
149
+ } else {
150
+ doc = undefined;
151
+ }
152
+ // const doc = new DOMParser().parseFromString(serialized, 'application/xml');
153
+ // const newDoc = doc.replaceChild(relevant, doc.firstElementChild);
154
+ this._handleResponse(doc);
155
+ this.dispatch('submit-done', {});
156
+ return;
157
+ }
158
+ // ### setting headers
159
+ const headers = this._getHeaders();
160
+ console.log('headers', headers);
161
+
162
+ // ### map urlencoded-post to post for fetch
163
+ if (this.method === 'urlencoded-post') {
164
+ this.method = 'post';
165
+ }
166
+
167
+ if (!this.methods.includes(this.method.toLowerCase())) {
168
+ this.dispatch('error', { message: `Unknown method ${this.method}` });
169
+ return;
170
+ }
119
171
  const response = await fetch(resolvedUrl, {
120
172
  method: this.method,
121
173
  mode: 'cors',
122
- credentials: 'same-origin',
123
- headers: {
124
- 'Content-type': 'application/xml; charset=UTF-8',
125
- },
174
+ credentials: 'include',
175
+ headers,
126
176
  body: serialized,
127
177
  });
128
178
 
129
- if (!response.ok) {
130
- this.dispatchEvent(
131
- new CustomEvent('submit-error', {
132
- composed: true,
133
- bubbles: true,
134
- detail: { message: `Error while submitting ${this.id}` },
135
- }),
136
- );
179
+ if (!response.ok || response.status > 400) {
180
+ this.dispatch('submit-error', { message: `Error while submitting ${this.id}` });
181
+ return;
137
182
  }
138
183
 
139
184
  const contentType = response.headers.get('content-type').toLowerCase();
140
-
141
- if (contentType.startsWith('text/plain') || contentType.startsWith('text/html')) {
185
+ if (
186
+ contentType.startsWith('text/plain') ||
187
+ contentType.startsWith('text/html') ||
188
+ contentType.startsWith('text/markdown')
189
+ ) {
142
190
  const text = await response.text();
143
191
  this._handleResponse(text);
144
192
  } else if (contentType.startsWith('application/json')) {
@@ -152,6 +200,53 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
152
200
  const blob = await response.blob();
153
201
  this._handleResponse(blob);
154
202
  }
203
+
204
+ this.dispatch('submit-done', {});
205
+ }
206
+
207
+ _serialize(instanceType, relevantNodes) {
208
+ if (this.method === 'urlencoded-post') {
209
+ // this.method = 'post';
210
+ const params = new URLSearchParams();
211
+ // console.log('nodes to serialize', relevantNodes);
212
+ Array.from(relevantNodes.children).forEach(child => {
213
+ params.append(child.nodeName, child.textContent);
214
+ });
215
+ return params;
216
+ }
217
+ if (instanceType === 'xml') {
218
+ const serializer = new XMLSerializer();
219
+ return serializer.serializeToString(relevantNodes);
220
+ }
221
+ /*
222
+ if(instanceType === 'json'){
223
+ console.warn('JSON serialization is not yet supported')
224
+ }
225
+ */
226
+ throw new Error('unknown instance type ', instanceType);
227
+ }
228
+
229
+ _getHeaders() {
230
+ const headers = new Headers();
231
+
232
+ // ### set content-type header according to type of instance
233
+ const instance = this.getInstance();
234
+ const contentType = Fore.getContentType(instance, this.method);
235
+ headers.append('Content-Type', contentType);
236
+ // ### needed to overwrite browsers' setting of 'Accept' header
237
+ if (headers.has('Accept')) {
238
+ headers.delete('Accept');
239
+ }
240
+ // headers.append('Accept', 'application/xml');
241
+
242
+ // ### add header defined by fx-header elements
243
+ const headerElems = this.querySelectorAll('fx-header');
244
+ Array.from(headerElems).forEach(header => {
245
+ const { name } = header;
246
+ const val = header.getValue();
247
+ headers.append(name, val);
248
+ });
249
+ return headers;
155
250
  }
156
251
 
157
252
  _getUrlExpr() {
@@ -176,9 +271,31 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
176
271
  if (this.replace === 'instance') {
177
272
  const targetInstance = this._getTargetInstance();
178
273
  if (targetInstance) {
179
- const instanceData = data;
180
- targetInstance.instanceData = instanceData;
181
- console.log('### replaced instance ', targetInstance.instanceData);
274
+ if (this.targetref) {
275
+ const theTarget = evaluateXPath(
276
+ this.targetref,
277
+ targetInstance.instanceData.firstElementChild,
278
+ this,
279
+ );
280
+ console.log('theTarget', theTarget);
281
+ const clone = data.firstElementChild;
282
+ const parent = theTarget.parentNode;
283
+ parent.replaceChild(clone, theTarget);
284
+ console.log('finally ', parent);
285
+ } else if (this.into) {
286
+ const theTarget = evaluateXPath(
287
+ this.into,
288
+ targetInstance.instanceData.firstElementChild,
289
+ this,
290
+ );
291
+ console.log('theTarget', theTarget);
292
+ theTarget.innerHTML = data;
293
+ } else {
294
+ const instanceData = data;
295
+ targetInstance.instanceData = instanceData;
296
+ console.log('### replaced instance ', targetInstance.instanceData);
297
+ }
298
+
182
299
  this.model.updateModel(); // force update
183
300
  // this.model.formElement.refresh();
184
301
  this.getOwnerForm().refresh();
@@ -199,24 +316,89 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
199
316
  window.location.href = data;
200
317
  }
201
318
 
202
- this.dispatchEvent(
203
- new CustomEvent('submit-done', {
204
- composed: true,
205
- bubbles: true,
206
- detail: {},
207
- }),
208
- );
319
+ /*
320
+ const event = new CustomEvent('submit-done', {
321
+ composed: true,
322
+ bubbles: true,
323
+ detail: {},
324
+ });
325
+ console.log('firing',event);
326
+ this.dispatchEvent(event);
327
+ */
328
+ // this.dispatch('submit-done', {});
329
+ }
330
+
331
+ /**
332
+ * select relevant nodes
333
+ *
334
+ * todo: support for 'empty'
335
+ * @returns {*}
336
+ */
337
+ selectRelevant() {
338
+ // ### no relevance selection - current nodeset is used 'as-is'
339
+ if (this.nonrelevant === 'keep') {
340
+ return this.nodeset;
341
+ }
342
+
343
+ // first check if nodeset of submission is relevant - otherwise bail out
344
+ const mi = this.getModel().getModelItem(this.nodeset);
345
+ if (mi && !mi.relevant) return null;
346
+
347
+ const doc = new DOMParser().parseFromString('<data></data>', 'application/xml');
348
+ const root = doc.firstElementChild;
349
+
350
+ if (this.nodeset.children.length === 0 && this._isRelevant(this.nodeset)) {
351
+ return this.nodeset;
352
+ }
353
+ const result = this._filterRelevant(this.nodeset, root);
354
+ return result;
355
+ }
356
+
357
+ _filterRelevant(node, result) {
358
+ const { childNodes } = node;
359
+ Array.from(childNodes).forEach(n => {
360
+ if (this._isRelevant(n)) {
361
+ const clone = n.cloneNode(false);
362
+ result.appendChild(clone);
363
+ const { attributes } = n;
364
+ if (attributes) {
365
+ Array.from(attributes).forEach(attr => {
366
+ if (this._isRelevant(attr)) {
367
+ clone.setAttribute(attr.nodeName, attr.value);
368
+ } else if (this.nonrelevant === 'empty') {
369
+ clone.setAttribute(attr.nodeName, '');
370
+ } else {
371
+ clone.removeAttribute(attr.nodeName);
372
+ }
373
+ });
374
+ }
375
+ return this._filterRelevant(n, clone);
376
+ }
377
+ return null;
378
+ });
379
+ return result;
380
+ }
381
+
382
+ _isRelevant(node) {
383
+ const mi = this.getModel().getModelItem(node);
384
+ if (!mi || mi.relevant) {
385
+ return true;
386
+ }
387
+ return false;
209
388
  }
210
389
 
211
390
  _handleError() {
212
- console.log('ERRRORRRRR');
213
- this.dispatchEvent(
214
- new CustomEvent('submit-error', {
215
- composed: true,
216
- bubbles: true,
217
- detail: {},
218
- }),
219
- );
391
+ this.dispatch('submit-error', {});
392
+ /*
393
+ console.log('ERRRORRRRR');
394
+ this.dispatchEvent(
395
+ new CustomEvent('submit-error', {
396
+ composed: true,
397
+ bubbles: true,
398
+ detail: {},
399
+ }),
400
+ );
401
+ */
220
402
  }
221
403
  }
222
404
 
@@ -1,5 +1,4 @@
1
- // import evaluateXPathToNodes from './xpath-evaluation.js';
2
- import { evaluateXPathToNodes, evaluateXPathToFirstNode } from './xpath-evaluation.js';
1
+ import { evaluateXPathToFirstNode } from './xpath-evaluation.js';
3
2
 
4
3
  import { XPathUtil } from './xpath-util.js';
5
4
 
@@ -32,7 +31,7 @@ function _getInitialContext(node, ref) {
32
31
  const instanceId = XPathUtil.getInstanceId(ref);
33
32
  return model.getInstance(instanceId).getDefaultContext();
34
33
  }
35
- if (model.getDefaultInstance() !== null) {
34
+ if (model.getDefaultInstance() !== null && model.inited) {
36
35
  return model.getDefaultInstance().getDefaultContext();
37
36
  }
38
37
  return [];
@@ -8,20 +8,6 @@ import { ModelItem } from '../modelitem.js';
8
8
  *
9
9
  */
10
10
  export default class AbstractControl extends foreElementMixin(HTMLElement) {
11
- /*
12
- static get properties() {
13
- return {
14
- ...super.properties,
15
- value: {
16
- type: String,
17
- },
18
- widget: {
19
- type: Object,
20
- },
21
- };
22
- }
23
- */
24
-
25
11
  constructor() {
26
12
  super();
27
13
  this.value = '';
@@ -34,14 +20,14 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
34
20
 
35
21
  // eslint-disable-next-line class-methods-use-this
36
22
  getWidget() {
37
- throw new Error('You have to implement the method updateWidgetValue!');
23
+ throw new Error('You have to implement the method getWidget!');
38
24
  }
39
25
 
40
26
  /**
41
27
  * (re)apply all modelItem state properties to this control. model -> UI
42
28
  */
43
- async refresh() {
44
- console.log('### AbstractControl.refresh on : ', this);
29
+ async refresh(force) {
30
+ // console.log('### AbstractControl.refresh on : ', this);
45
31
 
46
32
  const currentVal = this.value;
47
33
 
@@ -76,29 +62,31 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
76
62
  if (currentVal !== this.value) {
77
63
  // console.log('dispatching value-changed for ', this);
78
64
  // console.log('value-changed path ', this.modelItem.path);
79
- this.dispatchEvent(
80
- new CustomEvent('value-changed', {
81
- composed: true,
82
- bubbles: true,
83
- detail: { path: this.modelItem.path },
84
- }),
85
- );
65
+ this.dispatch('value-changed', { path: this.modelItem.path });
86
66
  }
87
67
  // this.requestUpdate();
88
68
  }
89
69
  }
70
+ // Fore.refreshChildren(this,force);
90
71
  // await this.updateComplete;
91
72
  }
92
73
 
74
+ /**
75
+ *
76
+ * @returns {Promise<void>}
77
+ */
93
78
  // eslint-disable-next-line class-methods-use-this
94
79
  async updateWidgetValue() {
95
80
  throw new Error('You have to implement the method updateWidgetValue!');
96
81
  }
97
82
 
98
83
  handleModelItemProperties() {
84
+ // console.log('form ready', this.getOwnerForm().ready);
99
85
  this.handleRequired();
100
86
  this.handleReadonly();
101
- this.handleValid();
87
+ if (this.getOwnerForm().ready) {
88
+ this.handleValid();
89
+ }
102
90
  this.handleRelevant();
103
91
  }
104
92
 
@@ -108,7 +96,7 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
108
96
 
109
97
  _dispatchEvent(event) {
110
98
  if (this.getOwnerForm().ready) {
111
- this.dispatchEvent(new CustomEvent(event, {}));
99
+ this.dispatch(event, {});
112
100
  }
113
101
  }
114
102
 
@@ -121,21 +109,13 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
121
109
  if (this.modelItem.required) {
122
110
  this.widget.setAttribute('required', 'required');
123
111
  this.classList.add('required');
124
- // this.dispatchEvent(new CustomEvent('required', {}));
125
-
126
- /*
127
- if(this.getOwnerForm().ready){
128
- this.dispatchEvent(new CustomEvent('required', {}));
129
- }
130
- */
131
112
  this._dispatchEvent('required');
132
113
  } else {
133
114
  this.widget.removeAttribute('required');
134
115
  this.required = false;
135
116
  // this.removeAttribute('required');
136
117
  this.classList.toggle('required');
137
-
138
- this.dispatchEvent(new CustomEvent('optional', {}));
118
+ this._dispatchEvent('optional');
139
119
  }
140
120
  }
141
121
  }
@@ -147,15 +127,13 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
147
127
  this.widget.setAttribute('readonly', 'readonly');
148
128
  // this.setAttribute('readonly','readonly');
149
129
  this.classList.toggle('readonly');
150
- // this.dispatchEvent(new CustomEvent('readonly', {}));
151
130
  this._dispatchEvent('readonly');
152
131
  }
153
132
  if (!this.modelItem.readonly) {
154
133
  this.widget.removeAttribute('readonly');
155
134
  // this.removeAttribute('readonly');
156
135
  this.classList.toggle('readonly');
157
-
158
- this.dispatchEvent(new CustomEvent('readwrite', {}));
136
+ this._dispatchEvent('readwrite');
159
137
  }
160
138
  }
161
139
  }
@@ -168,8 +146,8 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
168
146
  if (this.isValid() !== this.modelItem.constraint) {
169
147
  if (this.modelItem.constraint) {
170
148
  this.classList.remove('invalid');
171
- alert.style.display = 'none';
172
- this.dispatchEvent(new CustomEvent('valid', {}));
149
+ if (alert) alert.style.display = 'none';
150
+ this._dispatchEvent('valid');
173
151
  } else {
174
152
  // ### constraint is invalid - handle alerts
175
153
  this.classList.add('invalid');
@@ -201,12 +179,13 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
201
179
  // console.log('mip valid', this.modelItem.enabled);
202
180
  if (this.isEnabled() !== this.modelItem.relevant) {
203
181
  if (this.modelItem.relevant) {
204
- this.dispatchEvent(new CustomEvent('relevant', {}));
205
- this._fadeIn(this, this.display);
182
+ this._dispatchEvent('relevant');
183
+ // this._fadeIn(this, this.display);
184
+ this.style.display = this.display;
206
185
  } else {
207
- // this.dispatchEvent(new CustomEvent('nonrelevant', {}));
208
186
  this._dispatchEvent('nonrelevant');
209
- this._fadeOut(this);
187
+ // this._fadeOut(this);
188
+ this.style.display = 'none';
210
189
  }
211
190
  }
212
191
  }
@@ -1,12 +1,14 @@
1
- import { html, css } from 'lit-element';
1
+ import AbstractControl from './abstract-control.js';
2
2
 
3
- import XfAbstractControl from './abstract-control.js';
3
+ export class FxAlert extends AbstractControl {
4
+ constructor() {
5
+ super();
6
+ this.attachShadow({ mode: 'open' });
7
+ }
4
8
 
5
- export class FxAlert extends XfAbstractControl {
6
- static get styles() {
7
- return css`
9
+ connectedCallback() {
10
+ const style = `
8
11
  :host {
9
- display: block;
10
12
  height: auto;
11
13
  font-size: 0.8em;
12
14
  font-weight: 400;
@@ -14,23 +16,22 @@ export class FxAlert extends XfAbstractControl {
14
16
  display: none;
15
17
  }
16
18
  `;
17
- }
18
19
 
19
- constructor() {
20
- super();
21
- this.style.display = 'none';
22
- }
20
+ const html = `
21
+ <slot></slot>
22
+ `;
23
23
 
24
- static get properties() {
25
- return {
26
- ...super.properties,
27
- };
24
+ this.shadowRoot.innerHTML = `
25
+ <style>
26
+ ${style}
27
+ </style>
28
+ ${html}
29
+ `;
28
30
  }
29
31
 
30
- render() {
31
- return html`
32
- <slot></slot>
33
- `;
32
+ async updateWidgetValue() {
33
+ console.log('alert update', this);
34
+ this.innerHTML = this.value;
34
35
  }
35
36
  }
36
37
  customElements.define('fx-alert', FxAlert);
@@ -30,18 +30,21 @@ export class FxContainer extends foreElementMixin(HTMLElement) {
30
30
  </style>
31
31
  ${html}
32
32
  `;
33
+
34
+ this.getOwnerForm().registerLazyElement(this);
33
35
  }
34
36
 
35
37
  /**
36
38
  * (re)apply all state properties to this control.
37
39
  */
38
- refresh() {
39
- console.log('### FxContainer.refresh on : ', this);
40
+ refresh(force) {
41
+ if (!force && this.hasAttribute('refresh-on-view')) return;
42
+ // console.log('### FxContainer.refresh on : ', this);
40
43
 
41
44
  if (this.isBound()) {
42
45
  this.evalInContext();
43
46
  this.modelItem = this.getModelItem();
44
- this.value = this.modelItem.value;
47
+ // this.value = this.modelItem.value;
45
48
  }
46
49
 
47
50
  // await this.updateComplete;
@@ -50,7 +53,7 @@ export class FxContainer extends foreElementMixin(HTMLElement) {
50
53
  if (this._getForm().ready) {
51
54
  this.handleModelItemProperties();
52
55
  }
53
- Fore.refreshChildren(this);
56
+ Fore.refreshChildren(this, force);
54
57
  }
55
58
 
56
59
  handleModelItemProperties() {
@@ -78,6 +81,8 @@ export class FxContainer extends foreElementMixin(HTMLElement) {
78
81
 
79
82
  handleRelevant() {
80
83
  // console.log('mip valid', this.modelItem.enabled);
84
+ if (!this.modelItem) return;
85
+
81
86
  if (this.isEnabled() !== this.modelItem.enabled) {
82
87
  if (this.modelItem.enabled) {
83
88
  this.dispatchEvent(new CustomEvent('enabled', {}));