@jinntec/fore 1.0.0-1 → 1.0.0-4

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 (43) hide show
  1. package/README.md +20 -1
  2. package/dist/fore-all.js +10 -10
  3. package/dist/fore-debug.js +140 -0
  4. package/index.js +8 -0
  5. package/package.json +7 -6
  6. package/resources/{vars.css → fore-styles.css} +292 -0
  7. package/src/DependencyNotifyingDomFacade.js +9 -1
  8. package/src/ForeElementMixin.js +25 -17
  9. package/src/actions/abstract-action.js +8 -7
  10. package/src/actions/fx-action.js +6 -1
  11. package/src/actions/fx-dispatch.js +10 -2
  12. package/src/actions/fx-hide.js +23 -0
  13. package/src/actions/fx-insert.js +9 -9
  14. package/src/actions/fx-send.js +1 -1
  15. package/src/actions/fx-setvalue.js +2 -4
  16. package/src/actions/fx-show.js +23 -0
  17. package/src/dep_graph.js +9 -0
  18. package/src/drawdown.js +172 -0
  19. package/src/fore.js +56 -3
  20. package/src/fx-bind.js +20 -17
  21. package/src/fx-fore.js +297 -33
  22. package/src/fx-instance.js +27 -6
  23. package/src/fx-model.js +182 -37
  24. package/src/fx-submission.js +29 -7
  25. package/src/fx-var.js +43 -0
  26. package/src/getInScopeContext.js +28 -7
  27. package/src/modelitem.js +1 -0
  28. package/src/ui/abstract-control.js +17 -4
  29. package/src/ui/fx-alert.js +16 -20
  30. package/src/ui/fx-container.js +9 -4
  31. package/src/ui/fx-control.js +76 -39
  32. package/src/ui/fx-dialog.js +68 -0
  33. package/src/ui/fx-inspector.js +44 -0
  34. package/src/ui/fx-items.js +120 -0
  35. package/src/ui/fx-output.js +50 -12
  36. package/src/ui/fx-repeat.js +83 -16
  37. package/src/ui/fx-repeatitem.js +11 -4
  38. package/src/ui/fx-trigger.js +3 -0
  39. package/src/xpath-evaluation.js +372 -135
  40. package/src/xpath-util.js +52 -12
  41. package/dist/fore.js +0 -2
  42. package/resources/fore.css +0 -86
  43. package/resources/toastify.css +0 -87
package/src/fx-model.js CHANGED
@@ -16,11 +16,13 @@ export class FxModel extends HTMLElement {
16
16
  this.instances = [];
17
17
  this.modelItems = [];
18
18
  this.defaultContext = {};
19
+ this.changed = [];
19
20
 
20
21
  // this.mainGraph = new DepGraph(false);
21
22
  this.inited = false;
22
23
  this.modelConstructed = false;
23
24
  this.attachShadow({ mode: 'open' });
25
+ this.computes = 0;
24
26
  }
25
27
 
26
28
  get formElement() {
@@ -39,7 +41,7 @@ export class FxModel extends HTMLElement {
39
41
  console.log('model-construct-done fired ', e.detail.model.instances);
40
42
  });
41
43
 
42
- // logging
44
+ this.skipUpdate = false;
43
45
  }
44
46
 
45
47
  static lazyCreateModelItem(model, ref, node) {
@@ -83,10 +85,20 @@ export class FxModel extends HTMLElement {
83
85
  return mi;
84
86
  }
85
87
 
88
+ /**
89
+ * modelConstruct starts actual processing of the model by
90
+ *
91
+ * 1. loading instances if present or constructing one
92
+ * 2. calling updateModel to run the model update cycle of rebuild, recalculate and revalidate
93
+ *
94
+ * @event model-construct-done is fired once all instances have be loaded or after generating instance
95
+ *
96
+ */
86
97
  modelConstruct() {
87
98
  console.log('### <<<<< dispatching model-construct >>>>>');
88
99
  this.dispatchEvent(new CustomEvent('model-construct', { detail: this }));
89
100
 
101
+ console.time('instance-loading');
90
102
  const instances = this.querySelectorAll('fx-instance');
91
103
  if (instances.length > 0) {
92
104
  console.group('init instances');
@@ -121,6 +133,7 @@ export class FxModel extends HTMLElement {
121
133
  }),
122
134
  );
123
135
  }
136
+ console.timeEnd('instance-loading');
124
137
  this.inited = true;
125
138
  }
126
139
 
@@ -133,33 +146,46 @@ export class FxModel extends HTMLElement {
133
146
  * update action triggering the update cycle
134
147
  */
135
148
  updateModel() {
149
+ console.time('updateModel');
136
150
  this.rebuild();
151
+ if(this.skipUpdate) return;
137
152
  this.recalculate();
138
153
  this.revalidate();
154
+ console.timeEnd('updateModel');
139
155
  }
140
156
 
141
157
  rebuild() {
142
158
  console.group('### rebuild');
143
-
144
- this.mainGraph = new DepGraph(false);
159
+ console.time('rebuild');
160
+ this.mainGraph = new DepGraph(false); //do: should be moved down below binds.length check but causes errors in tests.
145
161
  this.modelItems = [];
146
162
 
147
163
  // trigger recursive initialization of the fx-bind elements
148
164
  const binds = this.querySelectorAll('fx-model > fx-bind');
165
+ if(binds.length === 0 ) {
166
+ console.log('skipped model update');
167
+ this.skipUpdate = true;
168
+ return ;
169
+ }
170
+
149
171
  binds.forEach(bind => {
150
172
  bind.init(this);
151
173
  });
174
+ console.timeEnd('rebuild');
152
175
 
153
176
  // console.log(`dependencies of a `, this.mainGraph.dependenciesOf("/Q{}data[1]/Q{}a[1]:required"));
154
177
  // console.log(`dependencies of b `, this.mainGraph.dependenciesOf("/Q{}data[1]/Q{}b[1]:required"));
155
178
  console.log(`rebuild mainGraph`, this.mainGraph);
156
179
  console.log(`rebuild mainGraph calc order`, this.mainGraph.overallOrder());
180
+
181
+ this.dispatchEvent(new CustomEvent('rebuild-done', { detail: { maingraph: this.mainGraph } }));
182
+
157
183
  /*
158
- console.log(
159
- `rebuild finished with modelItems ${this.modelItems.length} item(s)`,
160
- this.modelItems,
161
- );
162
- */
184
+ console.log(
185
+ `rebuild finished with modelItems ${this.modelItems.length} item(s)`,
186
+ this.modelItems,
187
+ );
188
+ */
163
189
  console.groupEnd();
164
190
  }
165
191
 
@@ -170,34 +196,70 @@ export class FxModel extends HTMLElement {
170
196
  */
171
197
  recalculate() {
172
198
  console.group('### recalculate');
173
- console.log('recalculate instances ', this.instances);
174
-
175
- const v = this.mainGraph.overallOrder();
176
- v.forEach(path => {
177
- const node = this.mainGraph.getNodeData(path);
178
- const modelItem = this.getModelItem(node);
179
-
180
- if (modelItem && path.includes(':')) {
181
- const property = path.split(':')[1];
182
- if (property) {
183
- if (property === 'calculate') {
184
- const expr = modelItem.bind[property];
185
- const compute = evaluateXPath(expr, modelItem.node, this);
186
- modelItem.value = compute;
187
- } else if (property !== 'constraint' && property !== 'type') {
188
- const expr = modelItem.bind[property];
189
- if (expr) {
190
- const compute = evaluateXPathToBoolean(expr, modelItem.node, this);
191
- modelItem[property] = compute;
192
- console.log(
193
- `recalculating path ${path} - Expr:'${expr}' computed`,
194
- modelItem[property],
195
- );
196
- }
199
+ console.log('changed nodes ', this.changed);
200
+
201
+ console.time('recalculate');
202
+ this.computes = 0;
203
+
204
+ this.subgraph = new DepGraph(false);
205
+ if (this.changed.length !== 0) {
206
+ // ### build the subgraph
207
+ this.changed.forEach(modelItem => {
208
+ this.subgraph.addNode(modelItem.path, modelItem.node);
209
+ // const dependents = this.mainGraph.dependantsOf(modelItem.path, false);
210
+ // this._addSubgraphDependencies(modelItem.path);
211
+ if (this.mainGraph.hasNode(modelItem.path)) {
212
+ // const dependents = this.mainGraph.directDependantsOf(modelItem.path)
213
+
214
+ const all = this.mainGraph.dependantsOf(modelItem.path, false);
215
+ const dependents = all.reverse();
216
+ if (dependents.length !== 0) {
217
+ dependents.forEach(dep => {
218
+ // const subdep = this.mainGraph.dependentsOf(dep,false);
219
+ // subgraph.addDependency(dep, modelItem.path);
220
+ const val = this.mainGraph.getNodeData(dep);
221
+ this.subgraph.addNode(dep, val);
222
+ if (dep.includes(':')) {
223
+ const path = dep.substring(0, dep.indexOf(':'));
224
+ this.subgraph.addNode(path, val);
225
+
226
+ const deps = this.mainGraph.dependentsOf(modelItem.path, false);
227
+ // if we find the dep to be first in list of dependents we are dependent on ourselves not adding edge to modelItem.path
228
+ if (deps.indexOf(dep) !== 0) {
229
+ this.subgraph.addDependency(dep, modelItem.path);
230
+ }
231
+ }
232
+ // subgraph.addDependency(dep,modelItem.path);
233
+ });
197
234
  }
198
235
  }
199
- }
200
- });
236
+ });
237
+
238
+ // ### compute the subgraph
239
+ const ordered = this.subgraph.overallOrder(false);
240
+ ordered.forEach(path => {
241
+ if (this.mainGraph.hasNode(path)) {
242
+ const node = this.mainGraph.getNodeData(path);
243
+ this.compute(node, path);
244
+ }
245
+ });
246
+ const toRefresh = [...this.changed];
247
+ this.formElement.toRefresh = toRefresh;
248
+ this.changed = [];
249
+ console.log('subgraph', this.subgraph);
250
+ this.dispatchEvent(
251
+ new CustomEvent('recalculate-done', { detail: { subgraph: this.subgraph } }),
252
+ );
253
+ } else {
254
+ const v = this.mainGraph.overallOrder(false);
255
+ v.forEach(path => {
256
+ const node = this.mainGraph.getNodeData(path);
257
+ this.compute(node, path);
258
+ });
259
+ }
260
+ console.log(`recalculated ${this.computes} modelItems`);
261
+
262
+ console.timeEnd('recalculate');
201
263
  console.log(
202
264
  `recalculate finished with modelItems ${this.modelItems.length} item(s)`,
203
265
  this.modelItems,
@@ -205,6 +267,86 @@ export class FxModel extends HTMLElement {
205
267
  console.groupEnd();
206
268
  }
207
269
 
270
+ /*
271
+ _addSubgraphDependencies(path){
272
+ const dependents = this.mainGraph.directDependantsOf(path)
273
+
274
+ const alreadyInGraph = this.subgraph.incomingEdges[path];
275
+ // const alreadyInGraph = path in this.subgraph;
276
+ if(dependents.length !== 0 && alreadyInGraph.length === 0){
277
+
278
+ dependents.forEach(dep => {
279
+ // const val= this.mainGraph.getNodeData(dep);
280
+ // this.subgraph.addNode(dep,val);
281
+ if(dep.includes(':')){
282
+ const subpath = dep.substring(0, dep.indexOf(':'));
283
+ // this.subgraph.addNode(subpath,val);
284
+ this.subgraph.addDependency(subpath,dep);
285
+ this.subgraph.addDependency(dep,path);
286
+ /!*
287
+ const subdeps = this.mainGraph.directDependantsOf(path);
288
+ console.log('subdeps',path, subdeps);
289
+ subdeps.forEach(sdep => {
290
+ const sval= this.mainGraph.getNodeData(sdep);
291
+ this.subgraph.addNode(sdep,sval);
292
+ console.log('subdep',sdep);
293
+ });
294
+ *!/
295
+ if(this.subgraph.incomingEdges[dep] === 0){
296
+ this._addSubgraphDependencies(subpath)
297
+ }
298
+
299
+ }
300
+ });
301
+
302
+ }
303
+
304
+ }
305
+ */
306
+
307
+ /**
308
+ * (re-) computes a modelItem.
309
+ * @param node - the node the modelItem is attached to
310
+ * @param path - the canonical XPath of the node
311
+ */
312
+ compute(node, path) {
313
+ const modelItem = this.getModelItem(node);
314
+ if (modelItem && path.includes(':')) {
315
+ const property = path.split(':')[1];
316
+ if (property) {
317
+ /*
318
+ if (property === 'readonly') {
319
+ // make sure that calculated items are always readonly
320
+ if(modelItem.bind['calculate']){
321
+ modelItem.readonly = true;
322
+ }else {
323
+ const expr = modelItem.bind[property];
324
+ const compute = evaluateXPathToBoolean(expr, modelItem.node, this);
325
+ modelItem.readonly = compute;
326
+ }
327
+ }
328
+ */
329
+ if (property === 'calculate') {
330
+ const expr = modelItem.bind[property];
331
+ const compute = evaluateXPath(expr, modelItem.node, this);
332
+ modelItem.value = compute;
333
+ modelItem.readonly = true; // calculated nodes are always readonly
334
+ } else if (property !== 'constraint' && property !== 'type') {
335
+ const expr = modelItem.bind[property];
336
+ if (expr) {
337
+ const compute = evaluateXPathToBoolean(expr, modelItem.node, this);
338
+ modelItem[property] = compute;
339
+ console.log(
340
+ `recalculating path ${path} - Expr:'${expr}' computed`,
341
+ modelItem[property],
342
+ );
343
+ }
344
+ }
345
+ }
346
+ this.computes += 1;
347
+ }
348
+ }
349
+
208
350
  /**
209
351
  * Iterates all modelItems to calculate the validation status.
210
352
  *
@@ -224,6 +366,7 @@ export class FxModel extends HTMLElement {
224
366
  revalidate() {
225
367
  console.group('### revalidate');
226
368
 
369
+ console.time('revalidate');
227
370
  let valid = true;
228
371
  this.modelItems.forEach(modelItem => {
229
372
  // console.log('validating node ', modelItem.node);
@@ -231,15 +374,16 @@ export class FxModel extends HTMLElement {
231
374
  const { bind } = modelItem;
232
375
  if (bind) {
233
376
  /*
234
- todo: investigate why bind is an element when created in fx-bind.init() and an fx-bind object when
235
- created lazily.
236
- */
377
+ todo: investigate why bind is an element when created in fx-bind.init() and an fx-bind object when
378
+ created lazily.
379
+ */
237
380
  if (typeof bind.hasAttribute === 'function' && bind.hasAttribute('constraint')) {
238
381
  const constraint = bind.getAttribute('constraint');
239
382
  if (constraint) {
240
383
  const compute = evaluateXPathToBoolean(constraint, modelItem.node, this);
241
384
  console.log('modelItem validity computed: ', compute);
242
385
  modelItem.constraint = compute;
386
+ this.formElement.addToRefresh(modelItem); // let fore know that modelItem needs refresh
243
387
  if (!compute) valid = false;
244
388
  // ### alerts are added only once during model-construct. Otherwise they would add up in each run of revalidate()
245
389
  if (!this.modelConstructed) {
@@ -253,6 +397,7 @@ export class FxModel extends HTMLElement {
253
397
  }
254
398
  }
255
399
  });
400
+ console.timeEnd('revalidate');
256
401
  console.log('modelItems after revalidate: ', this.modelItems);
257
402
  console.groupEnd();
258
403
  return valid;
@@ -26,6 +26,9 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
26
26
  /** if present should be a existing instance id */
27
27
  this.instance = this.hasAttribute('instance') ? this.getAttribute('instance') : null;
28
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
+
29
32
  /** http method */
30
33
  this.method = this.hasAttribute('method') ? this.getAttribute('method') : 'get';
31
34
 
@@ -78,6 +81,9 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
78
81
  const valid = model.revalidate();
79
82
  if (!valid) {
80
83
  console.log('validation failed. Bubmission stopped');
84
+ // ### allow alerts to pop up
85
+ this.dispatch('submit-error', {});
86
+ this.getModel().parentNode.refresh();
81
87
  return;
82
88
  }
83
89
  }
@@ -173,9 +179,17 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
173
179
  body: serialized,
174
180
  });
175
181
 
176
- const contentType = response.headers.get('content-type').toLowerCase();
182
+ if (!response.ok || response.status > 400) {
183
+ this.dispatch('submit-error', { message: `Error while submitting ${this.id}` });
184
+ return;
185
+ }
177
186
 
178
- if (contentType.startsWith('text/plain') || contentType.startsWith('text/html')) {
187
+ const contentType = response.headers.get('content-type').toLowerCase();
188
+ if (
189
+ contentType.startsWith('text/plain') ||
190
+ contentType.startsWith('text/html') ||
191
+ contentType.startsWith('text/markdown')
192
+ ) {
179
193
  const text = await response.text();
180
194
  this._handleResponse(text);
181
195
  } else if (contentType.startsWith('application/json')) {
@@ -190,10 +204,6 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
190
204
  this._handleResponse(blob);
191
205
  }
192
206
 
193
- if (!response.ok || response.status > 400) {
194
- this.dispatch('submit-error', { message: `Error while submitting ${this.id}` });
195
- return;
196
- }
197
207
  this.dispatch('submit-done', {});
198
208
  }
199
209
 
@@ -265,12 +275,24 @@ export class FxSubmission extends foreElementMixin(HTMLElement) {
265
275
  const targetInstance = this._getTargetInstance();
266
276
  if (targetInstance) {
267
277
  if (this.targetref) {
268
- const theTarget = evaluateXPath(this.targetref, targetInstance, this.getOwnerForm());
278
+ const theTarget = evaluateXPath(
279
+ this.targetref,
280
+ targetInstance.instanceData.firstElementChild,
281
+ this,
282
+ );
269
283
  console.log('theTarget', theTarget);
270
284
  const clone = data.firstElementChild;
271
285
  const parent = theTarget.parentNode;
272
286
  parent.replaceChild(clone, theTarget);
273
287
  console.log('finally ', parent);
288
+ } else if (this.into) {
289
+ const theTarget = evaluateXPath(
290
+ this.into,
291
+ targetInstance.instanceData.firstElementChild,
292
+ this,
293
+ );
294
+ console.log('theTarget', theTarget);
295
+ theTarget.innerHTML = data;
274
296
  } else {
275
297
  const instanceData = data;
276
298
  targetInstance.instanceData = instanceData;
package/src/fx-var.js ADDED
@@ -0,0 +1,43 @@
1
+ import './fx-instance.js';
2
+ import { evaluateXPath } from './xpath-evaluation.js';
3
+ import { foreElementMixin } from './ForeElementMixin.js';
4
+ import getInScopeContext from './getInScopeContext.js';
5
+
6
+ /**
7
+ * @ts-check
8
+ */
9
+ export class FxVariable extends foreElementMixin(HTMLElement) {
10
+ constructor() {
11
+ super();
12
+
13
+ this.attachShadow({ mode: 'open' });
14
+ this.name = '';
15
+ this.valueQuery = '';
16
+ this.value = null;
17
+ }
18
+
19
+ connectedCallback() {
20
+ this.name = this.getAttribute('name');
21
+ this.valueQuery = this.getAttribute('value');
22
+ }
23
+
24
+ refresh() {
25
+ const inscope = getInScopeContext(this, this.valueQuery);
26
+
27
+ this.value = evaluateXPath(this.valueQuery, inscope, this, this.precedingVariables);
28
+ }
29
+
30
+ setInScopeVariables(inScopeVariables) {
31
+ if (inScopeVariables.has(this.name)) {
32
+ console.error(`The variable ${this.name} is declared more than once`);
33
+ this.dispatch('xforms-binding-error');
34
+ return;
35
+ }
36
+ inScopeVariables.set(this.name, this);
37
+ // Clone the preceding variables to make sure we are not going to get access to variables we
38
+ // should not get access to
39
+ this.inScopeVariables = new Map(inScopeVariables);
40
+ }
41
+ }
42
+
43
+ customElements.define('fx-var', FxVariable);
@@ -2,10 +2,18 @@ import { evaluateXPathToFirstNode } from './xpath-evaluation.js';
2
2
 
3
3
  import { XPathUtil } from './xpath-util.js';
4
4
 
5
- function _getParentElement(node) {
5
+ function _getElement(node) {
6
6
  if (node.nodeType === Node.ATTRIBUTE_NODE) {
7
+ // The context of an attribute is the ref of the element it's defined on
7
8
  return node.ownerElement;
8
9
  }
10
+
11
+ if (node.nodeType === Node.ELEMENT_NODE) {
12
+ // The context of a query should be the element having a ref
13
+ return node;
14
+ }
15
+
16
+ // For text nodes, just start looking from the parent element
9
17
  return node.parentNode;
10
18
  }
11
19
 
@@ -31,14 +39,14 @@ function _getInitialContext(node, ref) {
31
39
  const instanceId = XPathUtil.getInstanceId(ref);
32
40
  return model.getInstance(instanceId).getDefaultContext();
33
41
  }
34
- if (model.getDefaultInstance() !== null) {
42
+ if (model.getDefaultInstance() !== null && model.inited) {
35
43
  return model.getDefaultInstance().getDefaultContext();
36
44
  }
37
45
  return [];
38
46
  }
39
47
 
40
48
  export default function getInScopeContext(node, ref) {
41
- const parentElement = _getParentElement(node);
49
+ const parentElement = _getElement(node);
42
50
  /*
43
51
  if(parentElement.nodeName.toUpperCase() === 'FX-REPEATITEM'){
44
52
  return parentElement.nodeset;
@@ -50,10 +58,23 @@ export default function getInScopeContext(node, ref) {
50
58
  return repeatItem.nodeset;
51
59
  }
52
60
 
53
- if (node.nodeType === Node.ELEMENT_NODE && node.hasAttribute('context')) {
54
- const initialContext = _getInitialContext(node.parentNode, ref);
55
- const contextAttr = node.getAttribute('context');
56
- return evaluateXPathToFirstNode(contextAttr, initialContext, _getForeContext(parentElement));
61
+ if (node.nodeType === Node.ATTRIBUTE_NODE && node.nodeName === 'ref') {
62
+ // Note: do not consider the ref of the owner element since it should not be used to define the
63
+ // context
64
+ if (node.ownerElement.hasAttribute('context')) {
65
+ const initialContext = _getInitialContext(node.ownerElement.parentNode, ref);
66
+ const contextAttr = node.ownerElement.getAttribute('context');
67
+ return evaluateXPathToFirstNode(contextAttr, initialContext, _getForeContext(parentElement));
68
+ }
69
+
70
+ // Never resolve the context from a ref itself!
71
+ return _getInitialContext(parentElement.parentNode, ref);
57
72
  }
73
+
74
+ // if (node.nodeType === Node.ELEMENT_NODE && node.hasAttribute('context')) {
75
+ // const initialContext = _getInitialContext(node.parentNode, ref);
76
+ // const contextAttr = node.getAttribute('context');
77
+ // return evaluateXPathToFirstNode(contextAttr, initialContext, _getForeContext(parentElement));
78
+ // }
58
79
  return _getInitialContext(parentElement, ref);
59
80
  }
package/src/modelitem.js CHANGED
@@ -30,6 +30,7 @@ export class ModelItem {
30
30
  this.bind = bind;
31
31
  this.changed = false;
32
32
  this.alerts = [];
33
+ this.boundControls = [];
33
34
  // this.value = this._getValue();
34
35
  }
35
36
 
@@ -26,12 +26,12 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
26
26
  /**
27
27
  * (re)apply all modelItem state properties to this control. model -> UI
28
28
  */
29
- async refresh() {
30
- console.log('### AbstractControl.refresh on : ', this);
29
+ async refresh(force) {
30
+ // console.log('### AbstractControl.refresh on : ', this);
31
31
 
32
32
  const currentVal = this.value;
33
33
 
34
- // if(this.repeated) return ;
34
+ // if(this.repeated) return
35
35
  if (this.isNotBound()) return;
36
36
 
37
37
  // await this.updateComplete;
@@ -52,6 +52,14 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
52
52
  // console.log('### XfAbstractControl.refresh modelItem : ', this.modelItem);
53
53
 
54
54
  this.value = this.modelItem.value;
55
+
56
+ /*
57
+ this is another case that highlights the fact that an init() function might make sense in general.
58
+ */
59
+ if(!this.modelItem.boundControls.includes(this)){
60
+ this.modelItem.boundControls.push(this);
61
+ }
62
+
55
63
  // console.log('>>>>>>>> abstract refresh ', this.control);
56
64
  // this.control[this.valueProp] = this.value;
57
65
  await this.updateWidgetValue();
@@ -67,16 +75,21 @@ export default class AbstractControl extends foreElementMixin(HTMLElement) {
67
75
  // this.requestUpdate();
68
76
  }
69
77
  }
78
+ // Fore.refreshChildren(this,force);
70
79
  // await this.updateComplete;
71
80
  }
72
81
 
82
+ /**
83
+ *
84
+ * @returns {Promise<void>}
85
+ */
73
86
  // eslint-disable-next-line class-methods-use-this
74
87
  async updateWidgetValue() {
75
88
  throw new Error('You have to implement the method updateWidgetValue!');
76
89
  }
77
90
 
78
91
  handleModelItemProperties() {
79
- console.log('form ready', this.getOwnerForm().ready);
92
+ // console.log('form ready', this.getOwnerForm().ready);
80
93
  this.handleRequired();
81
94
  this.handleReadonly();
82
95
  if (this.getOwnerForm().ready) {
@@ -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,17 @@ export class FxAlert extends XfAbstractControl {
14
16
  display: none;
15
17
  }
16
18
  `;
17
- }
18
-
19
- constructor() {
20
- super();
21
- this.style.display = 'none';
22
- }
23
-
24
- static get properties() {
25
- return {
26
- ...super.properties,
27
- };
28
- }
29
19
 
30
- render() {
31
- return html`
20
+ const html = `
32
21
  <slot></slot>
33
22
  `;
23
+
24
+ this.shadowRoot.innerHTML = `
25
+ <style>
26
+ ${style}
27
+ </style>
28
+ ${html}
29
+ `;
34
30
  }
35
31
 
36
32
  async updateWidgetValue() {
@@ -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', {}));