@smartbit4all/ng-client 7.3.24 → 7.3.25

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.
@@ -8,8 +8,8 @@ import { QuillEditorComponent, QuillModule } from 'ngx-quill';
8
8
  import { DateFnsAdapter } from '@angular/material-date-fns-adapter';
9
9
  import { enUS, hu } from 'date-fns/locale';
10
10
  import { isPlatformBrowser, NgClass, NgTemplateOutlet, NgStyle, Location, AsyncPipe, KeyValuePipe, DatePipe, NgIf, NgForOf } from '@angular/common';
11
- import { Subject, lastValueFrom, tap, takeUntil, interval, startWith, map, distinctUntilChanged, throwError, from, catchError as catchError$1 } from 'rxjs';
12
- import { catchError, switchMap, takeUntil as takeUntil$1 } from 'rxjs/operators';
11
+ import { Subject, lastValueFrom, defer, finalize, filter, startWith, map, distinctUntilChanged, tap, takeUntil, interval, throwError, from, catchError as catchError$1 } from 'rxjs';
12
+ import { catchError, switchMap, filter as filter$1, takeUntil as takeUntil$1 } from 'rxjs/operators';
13
13
  import { deepEqual } from 'fast-equals';
14
14
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
15
15
  import { MatDialogRef, MatDialog, MAT_DIALOG_DATA, MatDialogContent, MatDialogTitle, MatDialogClose, MatDialogActions } from '@angular/material/dialog';
@@ -23,7 +23,7 @@ import { MAT_SNACK_BAR_DATA, MatSnackBarLabel, MatSnackBar } from '@angular/mate
23
23
  import { Overlay, OverlayPositionBuilder, ScrollDispatcher } from '@angular/cdk/overlay';
24
24
  import { TemplatePortal } from '@angular/cdk/portal';
25
25
  import * as i1$1 from '@angular/forms';
26
- import { Validators, UntypedFormGroup, UntypedFormControl, NG_VALUE_ACCESSOR, FormsModule, ReactiveFormsModule } from '@angular/forms';
26
+ import { Validators, UntypedFormControl, UntypedFormGroup, NG_VALUE_ACCESSOR, FormsModule, ReactiveFormsModule } from '@angular/forms';
27
27
  import { ENTER, COMMA } from '@angular/cdk/keycodes';
28
28
  import { MatAutocompleteTrigger, MatAutocomplete } from '@angular/material/autocomplete';
29
29
  import { MatChipGrid, MatChipRow, MatChipRemove, MatChipInput, MatChip } from '@angular/material/chips';
@@ -56,6 +56,8 @@ import ZoomPlugin from 'chartjs-plugin-zoom';
56
56
  import ChartDataLabels from 'chartjs-plugin-datalabels';
57
57
  import { MatToolbar } from '@angular/material/toolbar';
58
58
 
59
+ /** The default count limit of the Global Value Set Store's explicit sets. */
60
+ const DEFAULT_VALUE_SET_STORE_SIZE = 200;
59
61
  /**
60
62
  * The resolved library configuration. Provided by {@link provideSmartNgClient}; inject it
61
63
  * to read a configured value.
@@ -68,6 +70,7 @@ function resolveSmartNgClientConfig(config) {
68
70
  treeMenuIcon: 'more_vert',
69
71
  dialogDisableClose: true,
70
72
  dateLocale: 'hu-HU',
73
+ valueSetStoreSize: DEFAULT_VALUE_SET_STORE_SIZE,
71
74
  ...config,
72
75
  // Merged field by field, so that configuring one string keeps the other two defaults.
73
76
  errorDialog: {
@@ -1609,6 +1612,60 @@ class ViewService {
1609
1612
  reportProgress: reportProgress
1610
1613
  });
1611
1614
  }
1615
+ fetchValueSets(viewUuid, valueSetFetchRequest, observe = 'body', reportProgress = false, options) {
1616
+ if (viewUuid === null || viewUuid === undefined) {
1617
+ throw new Error('Required parameter viewUuid was null or undefined when calling fetchValueSets.');
1618
+ }
1619
+ if (valueSetFetchRequest === null || valueSetFetchRequest === undefined) {
1620
+ throw new Error('Required parameter valueSetFetchRequest was null or undefined when calling fetchValueSets.');
1621
+ }
1622
+ let localVarHeaders = this.defaultHeaders;
1623
+ let localVarHttpHeaderAcceptSelected = options && options.httpHeaderAccept;
1624
+ if (localVarHttpHeaderAcceptSelected === undefined) {
1625
+ // to determine the Accept header
1626
+ const httpHeaderAccepts = [
1627
+ 'application/json'
1628
+ ];
1629
+ localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts);
1630
+ }
1631
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
1632
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
1633
+ }
1634
+ let localVarHttpContext = options && options.context;
1635
+ if (localVarHttpContext === undefined) {
1636
+ localVarHttpContext = new HttpContext();
1637
+ }
1638
+ // to determine the Content-Type header
1639
+ const consumes = [
1640
+ 'application/json'
1641
+ ];
1642
+ const httpContentTypeSelected = this.configuration.selectHeaderContentType(consumes);
1643
+ if (httpContentTypeSelected !== undefined) {
1644
+ localVarHeaders = localVarHeaders.set('Content-Type', httpContentTypeSelected);
1645
+ }
1646
+ let responseType_ = 'json';
1647
+ if (localVarHttpHeaderAcceptSelected) {
1648
+ if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
1649
+ responseType_ = 'text';
1650
+ }
1651
+ else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
1652
+ responseType_ = 'json';
1653
+ }
1654
+ else {
1655
+ responseType_ = 'blob';
1656
+ }
1657
+ }
1658
+ let localVarPath = `/component/${this.configuration.encodeParam({ name: "viewUuid", value: viewUuid, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: "uuid" })}/valueSets`;
1659
+ return this.httpClient.request('post', `${this.configuration.basePath}${localVarPath}`, {
1660
+ context: localVarHttpContext,
1661
+ body: valueSetFetchRequest,
1662
+ responseType: responseType_,
1663
+ withCredentials: this.configuration.withCredentials,
1664
+ headers: localVarHeaders,
1665
+ observe: observe,
1666
+ reportProgress: reportProgress
1667
+ });
1668
+ }
1612
1669
  getAndroidTheme(name, observe = 'body', reportProgress = false, options) {
1613
1670
  if (name === null || name === undefined) {
1614
1671
  throw new Error('Required parameter name was null or undefined when calling getAndroidTheme.');
@@ -3091,6 +3148,30 @@ var UploadWidgetType;
3091
3148
  UploadWidgetType["IMAGE"] = "IMAGE";
3092
3149
  })(UploadWidgetType || (UploadWidgetType = {}));
3093
3150
 
3151
+ /**
3152
+ * View API
3153
+ * View API
3154
+ *
3155
+ * The version of the OpenAPI document: 1.0.0
3156
+ * Contact: info@it4all.hu
3157
+ *
3158
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
3159
+ * https://openapi-generator.tech
3160
+ * Do not edit the class manually.
3161
+ */
3162
+
3163
+ /**
3164
+ * View API
3165
+ * View API
3166
+ *
3167
+ * The version of the OpenAPI document: 1.0.0
3168
+ * Contact: info@it4all.hu
3169
+ *
3170
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
3171
+ * https://openapi-generator.tech
3172
+ * Do not edit the class manually.
3173
+ */
3174
+
3094
3175
  /**
3095
3176
  * View API
3096
3177
  * View API
@@ -4228,6 +4309,602 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
4228
4309
  args: [{ selector: '[smartEmbeddedSlot]' }]
4229
4310
  }], propDecorators: { containerId: [{ type: i0.Input, args: [{ isSignal: true, alias: "smartEmbeddedSlot", required: true }] }], containerUuid: [{ type: i0.Input, args: [{ isSignal: true, alias: "containerUuid", required: false }] }] } });
4230
4311
 
4312
+ /**
4313
+ * The key and display property of a valueSet's values: the preferred name if the values carry it,
4314
+ * then the GenericValue name (`uri` / `name`), then the Value name (`objectUri` / `displayValue`).
4315
+ * With no sample value to look at the preferred name is assumed to exist.
4316
+ */
4317
+ function valueSetKeyProperty(values, preferred) {
4318
+ return firstExisting(values, [preferred ?? 'objectUri', 'uri', 'objectUri']);
4319
+ }
4320
+ function valueSetDisplayProperty(values, preferred) {
4321
+ return firstExisting(values, [preferred ?? 'displayValue', 'name', 'displayValue']);
4322
+ }
4323
+ /** The label of a code in a valueSet, or undefined when the valueSet does not know the code. */
4324
+ function valueSetLabelOf(valueSetData, code) {
4325
+ const values = valueSetData.values ?? [];
4326
+ const keyProperty = valueSetKeyProperty(values, valueSetData.keyProperty);
4327
+ const hit = values.find((value) => String(value[keyProperty]) === String(code));
4328
+ return hit?.[valueSetDisplayProperty(values)];
4329
+ }
4330
+ function firstExisting(values, candidates) {
4331
+ if (!values?.length) {
4332
+ return candidates[0];
4333
+ }
4334
+ return candidates.find((c) => c in values[0]) ?? candidates[candidates.length - 1];
4335
+ }
4336
+
4337
+ /**
4338
+ * The Global Value Set Store: the value sets of the browser tab shared by every open view,
4339
+ * explicit sets by backend module, namespace and name, implicit sets by view uuid and key. It
4340
+ * fetches the versions a view context change names and it lacks, keeps what the open views read
4341
+ * and empties at a change of the session locale.
4342
+ */
4343
+ class SmartValueSetStore {
4344
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SmartValueSetStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4345
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SmartValueSetStore, providedIn: "root", useFactory: () => new SmartValueSetMemoryStore() }); }
4346
+ }
4347
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SmartValueSetStore, decorators: [{
4348
+ type: Injectable,
4349
+ args: [{
4350
+ providedIn: "root",
4351
+ useFactory: () => new SmartValueSetMemoryStore(),
4352
+ }]
4353
+ }] });
4354
+ class SmartValueSetMemoryStore extends SmartValueSetStore {
4355
+ constructor() {
4356
+ super();
4357
+ this.viewService = inject(ViewService);
4358
+ /** The view context service injects the store, so it is looked up at use. */
4359
+ this.injector = inject(Injector);
4360
+ this.sizeLimit = inject(SMART_NG_CLIENT_CONFIG).valueSetStoreSize;
4361
+ this.changeSource = new Subject();
4362
+ this.changes = this.changeSource.asObservable();
4363
+ /** In the order of their last write, the least recently written first. */
4364
+ this.explicitEntries = new Map();
4365
+ /** The entries of each view by key. */
4366
+ this.viewEntries = new Map();
4367
+ /** The closed views the view context still lists; a late model write of one is not taken. */
4368
+ this.closedViewUuids = new Set();
4369
+ /** The selections the widgets of each open view read through, by the name of the set. */
4370
+ this.viewSelections = new Map();
4371
+ /** The keys of the explicit sets the `label` pipe of each open view read by name. */
4372
+ this.readExplicitKeys = new Map();
4373
+ /** The number of subscribers of {@link get} to each explicit set. */
4374
+ this.subscriberCounts = new Map();
4375
+ /** The version of each branch item of the version maps, by its explicit key. */
4376
+ this.branchVersions = new Map();
4377
+ /** The fetches of each entry, by its explicit or implicit key. */
4378
+ this.fetchRecords = new Map();
4379
+ /** Numbers the fetches in the order they start. */
4380
+ this.fetchSequence = 0;
4381
+ /** Stepped by {@link clear}; the answer of a fetch started before is dropped. */
4382
+ this.epoch = 0;
4383
+ /** The number of holds {@link holdFetches} gave out and not yet released. */
4384
+ this.holdCount = 0;
4385
+ /** The sets waiting for the next fetch, by backend module. */
4386
+ this.pendingFetches = new Map();
4387
+ // The sets are resolved in the session locale; the session reports it at every initialize.
4388
+ const session = inject(SmartSessionService);
4389
+ let locale = session.sessionInfoData?.locale;
4390
+ session.localeChanged.subscribe(() => {
4391
+ const previousLocale = locale;
4392
+ locale = session.sessionInfoData?.locale;
4393
+ if (previousLocale !== undefined && previousLocale !== locale) {
4394
+ this.clear();
4395
+ }
4396
+ });
4397
+ }
4398
+ get(module, namespace, name) {
4399
+ const key = createExplicitKey(module, namespace, name);
4400
+ return defer(() => {
4401
+ this.subscriberCounts.set(key, (this.subscriberCounts.get(key) ?? 0) + 1);
4402
+ return this.followChanges(name, () => this.explicitEntries.get(key)?.data);
4403
+ }).pipe(finalize(() => this.releaseSubscriber(key)));
4404
+ }
4405
+ getImplicit(viewUuid, key) {
4406
+ return this.followChanges(key, () => {
4407
+ const entry = this.viewEntries.get(viewUuid)?.get(key);
4408
+ return entry?.implicit ? entry.data : undefined;
4409
+ });
4410
+ }
4411
+ getForSelection(viewUuid, selection) {
4412
+ return defer(() => {
4413
+ this.registerSelection(viewUuid, selection);
4414
+ return this.followChanges(selection.valueSetName, () => this.findForSelection(viewUuid, selection));
4415
+ });
4416
+ }
4417
+ findByName(viewUuid, name) {
4418
+ const module = this.findViewModule(viewUuid);
4419
+ const selection = this.viewSelections.get(viewUuid)?.get(name) ?? {
4420
+ valueSetNamespace: this.findLastWrittenNamespace(module, name),
4421
+ valueSetName: name,
4422
+ };
4423
+ const data = this.findForSelection(viewUuid, selection);
4424
+ if (data && !this.viewEntries.get(viewUuid)?.get(name)?.implicit) {
4425
+ this.registerExplicitRead(viewUuid, createExplicitKey(module, selection.valueSetNamespace, name));
4426
+ }
4427
+ return data;
4428
+ }
4429
+ putImplicit(viewUuid, valueSets) {
4430
+ if (this.closedViewUuids.has(viewUuid)) {
4431
+ return;
4432
+ }
4433
+ let isFetchPending = false;
4434
+ Object.entries(valueSets ?? {}).forEach(([key, valueSet]) => {
4435
+ if (!valueSet) {
4436
+ return;
4437
+ }
4438
+ const held = this.viewEntries.get(viewUuid)?.get(key);
4439
+ const extras = {
4440
+ extraValues: valueSet.extraValues,
4441
+ excludedKeys: valueSet.excludedKeys,
4442
+ };
4443
+ // A version alone keeps the values held on screen until the fetch brings the new ones.
4444
+ const next = !valueSet.valueSetData && valueSet.version && held?.implicit
4445
+ ? { ...held, ...extras }
4446
+ : {
4447
+ implicit: !!(valueSet.valueSetData || valueSet.version),
4448
+ version: valueSet.valueSetData ? valueSet.version : undefined,
4449
+ data: valueSet.valueSetData,
4450
+ ...extras,
4451
+ };
4452
+ if (!deepEqual(held, next)) {
4453
+ this.writeViewEntry(viewUuid, key, next);
4454
+ }
4455
+ const entryKey = createImplicitKey(viewUuid, key);
4456
+ if (!valueSet.valueSetData &&
4457
+ valueSet.version &&
4458
+ valueSet.version !== next.version &&
4459
+ !this.isFetching(entryKey, valueSet.version)) {
4460
+ this.markFetching(entryKey, valueSet.version);
4461
+ this.queueImplicitItem(viewUuid, key);
4462
+ isFetchPending = true;
4463
+ }
4464
+ });
4465
+ if (isFetchPending) {
4466
+ this.scheduleFlush().catch((err) => console.error("implicit value set fetch failed", err));
4467
+ }
4468
+ }
4469
+ removeView(viewUuid) {
4470
+ // Only the views the view context still lists are kept, so the set does not grow with the tab.
4471
+ const listedViewUuids = new Set(this.injector
4472
+ .get(SmartViewContextService)
4473
+ .getCurrentViewContext()
4474
+ ?.views?.map((view) => view.uuid));
4475
+ this.closedViewUuids.forEach((uuid) => {
4476
+ if (!listedViewUuids.has(uuid)) {
4477
+ this.closedViewUuids.delete(uuid);
4478
+ }
4479
+ });
4480
+ this.closedViewUuids.add(viewUuid);
4481
+ this.viewSelections.delete(viewUuid);
4482
+ this.readExplicitKeys.delete(viewUuid);
4483
+ this.pendingFetches.forEach((pending) => {
4484
+ pending.items = pending.items.filter((item) => item.implicitViewUuid !== viewUuid);
4485
+ });
4486
+ const entries = this.viewEntries.get(viewUuid);
4487
+ if (!entries) {
4488
+ return;
4489
+ }
4490
+ this.viewEntries.delete(viewUuid);
4491
+ entries.forEach((_, key) => {
4492
+ this.fetchRecords.delete(createImplicitKey(viewUuid, key));
4493
+ this.changeSource.next(key);
4494
+ });
4495
+ }
4496
+ holdFetches() {
4497
+ this.holdCount++;
4498
+ return () => {
4499
+ this.holdCount--;
4500
+ if (!this.holdCount) {
4501
+ this.flushFetches();
4502
+ }
4503
+ };
4504
+ }
4505
+ applyVersions(viewContextChange, viewUuid) {
4506
+ const versions = viewContextChange?.valueSetVersions;
4507
+ const requestViewUuid = viewUuid ?? viewContextChange?.changes?.[0]?.uuid;
4508
+ if (!versions?.length || !requestViewUuid) {
4509
+ return Promise.resolve();
4510
+ }
4511
+ const module = this.findViewModule(requestViewUuid);
4512
+ const readKeys = this.collectReadExplicitKeys();
4513
+ let isFetchPending = false;
4514
+ /** The version noted before and the one noted now of each branch item queued for a fetch. */
4515
+ const notedBranchVersions = new Map();
4516
+ versions.forEach((version) => {
4517
+ const key = createExplicitKey(module, version.namespace, version.name, version.branchUri);
4518
+ if (version.branchUri) {
4519
+ const previous = this.branchVersions.get(key);
4520
+ this.branchVersions.set(key, version.version);
4521
+ if (previous === undefined || previous === version.version) {
4522
+ return;
4523
+ }
4524
+ const holders = this.findViewsHoldingImplicitly(module, version.name);
4525
+ if (!holders.length) {
4526
+ return;
4527
+ }
4528
+ holders.forEach((holder) => this.queueImplicitItem(holder, version.name));
4529
+ notedBranchVersions.set(key, [previous, version.version]);
4530
+ isFetchPending = true;
4531
+ return;
4532
+ }
4533
+ const held = this.explicitEntries.get(key)?.version;
4534
+ if (held === version.version ||
4535
+ this.isFetching(key, version.version) ||
4536
+ this.isHeldOnlyImplicitly(module, key, version.name, readKeys)) {
4537
+ return;
4538
+ }
4539
+ this.markFetching(key, version.version);
4540
+ this.queueItem(module, requestViewUuid, {
4541
+ namespace: version.namespace,
4542
+ name: version.name,
4543
+ branchUri: version.branchUri,
4544
+ version: held,
4545
+ });
4546
+ isFetchPending = true;
4547
+ });
4548
+ if (!isFetchPending) {
4549
+ return Promise.resolve();
4550
+ }
4551
+ const flush = this.scheduleFlush();
4552
+ // A branch version whose fetch failed is taken back, so the next map fetches again.
4553
+ flush.catch(() => notedBranchVersions.forEach(([previous, noted], key) => {
4554
+ if (this.branchVersions.get(key) === noted) {
4555
+ this.branchVersions.set(key, previous);
4556
+ }
4557
+ }));
4558
+ return flush;
4559
+ }
4560
+ fetch(module, viewUuid, items) {
4561
+ if (!items.length) {
4562
+ return Promise.resolve();
4563
+ }
4564
+ items.forEach((item) => this.queueItem(module ?? "", viewUuid, item));
4565
+ return this.scheduleFlush();
4566
+ }
4567
+ clear() {
4568
+ const readKeys = this.collectReadExplicitKeys();
4569
+ const entries = [...this.explicitEntries];
4570
+ this.explicitEntries.clear();
4571
+ this.fetchRecords.clear();
4572
+ this.epoch++;
4573
+ // A set fetched again is marked in the version it had, so a change naming it asks nothing.
4574
+ entries.forEach(([key, entry]) => {
4575
+ const { module, namespace, name, branchUri } = parseExplicitKey(key);
4576
+ if (readKeys.has(key)) {
4577
+ this.markFetching(key, entry.version);
4578
+ this.queueItem(module, entry.viewUuid, { namespace, name, branchUri });
4579
+ }
4580
+ this.changeSource.next(name);
4581
+ });
4582
+ this.viewEntries.forEach((entries, viewUuid) => entries.forEach((held, key) => {
4583
+ if (held.implicit) {
4584
+ this.markFetching(createImplicitKey(viewUuid, key), held.version);
4585
+ this.writeViewEntry(viewUuid, key, { ...held, version: undefined, data: undefined });
4586
+ this.queueImplicitItem(viewUuid, key);
4587
+ }
4588
+ }));
4589
+ if (this.pendingFetches.size) {
4590
+ this.scheduleFlush().catch((err) => console.error("value set fetch failed", err));
4591
+ }
4592
+ }
4593
+ /** Queues the item for the fetch of the module, which goes through the first view queued. */
4594
+ queueItem(module, viewUuid, item) {
4595
+ const pending = this.pendingFetches.get(module) ?? { viewUuid, items: [] };
4596
+ pending.items.push(item);
4597
+ this.pendingFetches.set(module, pending);
4598
+ }
4599
+ queueImplicitItem(viewUuid, key) {
4600
+ const module = this.findViewModule(viewUuid);
4601
+ const isQueued = this.pendingFetches
4602
+ .get(module)
4603
+ ?.items.some((item) => item.implicitViewUuid === viewUuid && item.name === key);
4604
+ if (!isQueued) {
4605
+ this.queueItem(module, viewUuid, { name: key, implicitViewUuid: viewUuid });
4606
+ }
4607
+ }
4608
+ /** The promise of the fetches waiting, sent now or when the last hold is released. */
4609
+ scheduleFlush() {
4610
+ if (!this.pendingFlush) {
4611
+ let resolve;
4612
+ let reject;
4613
+ const promise = new Promise((res, rej) => {
4614
+ resolve = res;
4615
+ reject = rej;
4616
+ });
4617
+ this.pendingFlush = { promise, resolve, reject };
4618
+ }
4619
+ const promise = this.pendingFlush.promise;
4620
+ if (!this.holdCount) {
4621
+ this.flushFetches();
4622
+ }
4623
+ return promise;
4624
+ }
4625
+ /** Sends the items waiting, one Value Set Fetch per backend module. */
4626
+ flushFetches() {
4627
+ const flush = this.pendingFlush;
4628
+ if (!flush) {
4629
+ return;
4630
+ }
4631
+ this.pendingFlush = undefined;
4632
+ const pendingFetches = [...this.pendingFetches];
4633
+ this.pendingFetches = new Map();
4634
+ Promise.all(pendingFetches.map(([module, pending]) => this.sendFetch(module, pending))).then(() => flush.resolve(), (err) => flush.reject(err));
4635
+ }
4636
+ /**
4637
+ * One Value Set Fetch of a module through its view, each answer written to the set of its item.
4638
+ * An implicit set asks with the version its view holds.
4639
+ */
4640
+ async sendFetch(module, pending) {
4641
+ const epoch = this.epoch;
4642
+ const sent = pending.items.map((item) => {
4643
+ const entryKey = item.implicitViewUuid
4644
+ ? createImplicitKey(item.implicitViewUuid, item.name)
4645
+ : createExplicitKey(module, item.namespace, item.name, item.branchUri);
4646
+ return {
4647
+ item: item.implicitViewUuid
4648
+ ? {
4649
+ ...item,
4650
+ version: this.viewEntries.get(item.implicitViewUuid)?.get(item.name)?.version,
4651
+ }
4652
+ : item,
4653
+ entryKey,
4654
+ fetchingVersion: this.fetchRecords.get(entryKey)?.fetchingVersion,
4655
+ };
4656
+ });
4657
+ try {
4658
+ if (!sent.length) {
4659
+ return;
4660
+ }
4661
+ const sequence = ++this.fetchSequence;
4662
+ const response = await lastValueFrom(this.viewService.fetchValueSets(pending.viewUuid, { items: sent.map(({ item }) => item) }));
4663
+ if (epoch !== this.epoch) {
4664
+ return;
4665
+ }
4666
+ response?.items?.forEach((result, index) => {
4667
+ const { item, entryKey } = sent[index];
4668
+ if (item.implicitViewUuid) {
4669
+ this.writeImplicitResult(item.implicitViewUuid, item.name, result, sequence);
4670
+ }
4671
+ else {
4672
+ this.writeExplicitResult(entryKey, item.name, result, pending.viewUuid, sequence);
4673
+ }
4674
+ });
4675
+ this.evictExplicitEntries();
4676
+ }
4677
+ finally {
4678
+ // A later change may start a newer fetch of an entry meanwhile; only this one's mark ends.
4679
+ sent.forEach(({ entryKey, fetchingVersion }) => {
4680
+ const record = this.fetchRecords.get(entryKey);
4681
+ const isMarkOfThisFetch = fetchingVersion && record?.fetchingVersion === fetchingVersion;
4682
+ if (epoch === this.epoch && isMarkOfThisFetch) {
4683
+ record.fetchingVersion = undefined;
4684
+ }
4685
+ });
4686
+ }
4687
+ }
4688
+ writeExplicitResult(key, name, result, viewUuid, sequence) {
4689
+ if (!this.checkAndRecordWrite(key, sequence)) {
4690
+ return;
4691
+ }
4692
+ const held = this.explicitEntries.get(key);
4693
+ if (result.valueSetData) {
4694
+ this.writeExplicitEntry(key, name, {
4695
+ version: result.version,
4696
+ data: result.valueSetData,
4697
+ viewUuid,
4698
+ });
4699
+ }
4700
+ else if (held && (held.version !== result.version || held.viewUuid !== viewUuid)) {
4701
+ this.writeExplicitEntry(key, name, { ...held, version: result.version, viewUuid });
4702
+ }
4703
+ }
4704
+ /** Values are the new version, a version alone is the one held, no version is a set gone. */
4705
+ writeImplicitResult(viewUuid, key, result, sequence) {
4706
+ // The entries of a view closed meanwhile are gone and stay so.
4707
+ const held = this.viewEntries.get(viewUuid)?.get(key);
4708
+ if (!held || !this.checkAndRecordWrite(createImplicitKey(viewUuid, key), sequence)) {
4709
+ return;
4710
+ }
4711
+ if (result.valueSetData || !result.version) {
4712
+ this.writeViewEntry(viewUuid, key, {
4713
+ ...held,
4714
+ implicit: true,
4715
+ version: result.version,
4716
+ data: result.valueSetData,
4717
+ });
4718
+ }
4719
+ }
4720
+ /**
4721
+ * Whether nothing reads the explicit set and a view of the module holds it as its implicit set
4722
+ * of the name, read by a selection in the set's namespace.
4723
+ */
4724
+ isHeldOnlyImplicitly(module, key, name, readKeys) {
4725
+ return (!readKeys.has(key) &&
4726
+ this.findViewsHoldingImplicitly(module, name).some((viewUuid) => {
4727
+ const selection = this.viewSelections.get(viewUuid)?.get(name);
4728
+ return (!!selection && createExplicitKey(module, selection.valueSetNamespace, name) === key);
4729
+ }));
4730
+ }
4731
+ /** The views of the module holding an implicit set under the name. */
4732
+ findViewsHoldingImplicitly(module, name) {
4733
+ return [...this.viewEntries]
4734
+ .filter(([viewUuid, entries]) => entries.get(name)?.implicit && this.findViewModule(viewUuid) === module)
4735
+ .map(([viewUuid]) => viewUuid);
4736
+ }
4737
+ /**
4738
+ * The keys of the explicit sets something reads: a selection of an open view whose view holds
4739
+ * no implicit set of the name, the `label` pipe of an open view, or a subscriber of {@link get}.
4740
+ */
4741
+ collectReadExplicitKeys() {
4742
+ const keys = new Set(this.subscriberCounts.keys());
4743
+ this.readExplicitKeys.forEach((viewKeys) => viewKeys.forEach((key) => keys.add(key)));
4744
+ this.viewSelections.forEach((selections, viewUuid) => {
4745
+ const module = this.findViewModule(viewUuid);
4746
+ selections.forEach((selection, name) => {
4747
+ if (!this.viewEntries.get(viewUuid)?.get(name)?.implicit) {
4748
+ keys.add(createExplicitKey(module, selection.valueSetNamespace, name));
4749
+ }
4750
+ });
4751
+ });
4752
+ return keys;
4753
+ }
4754
+ releaseSubscriber(key) {
4755
+ const count = this.subscriberCounts.get(key) - 1;
4756
+ if (count) {
4757
+ this.subscriberCounts.set(key, count);
4758
+ }
4759
+ else {
4760
+ this.subscriberCounts.delete(key);
4761
+ }
4762
+ }
4763
+ registerSelection(viewUuid, selection) {
4764
+ if (this.closedViewUuids.has(viewUuid)) {
4765
+ return;
4766
+ }
4767
+ const selections = this.viewSelections.get(viewUuid) ?? new Map();
4768
+ selections.set(selection.valueSetName, selection);
4769
+ this.viewSelections.set(viewUuid, selections);
4770
+ }
4771
+ registerExplicitRead(viewUuid, key) {
4772
+ if (this.closedViewUuids.has(viewUuid)) {
4773
+ return;
4774
+ }
4775
+ const keys = this.readExplicitKeys.get(viewUuid) ?? new Set();
4776
+ keys.add(key);
4777
+ this.readExplicitKeys.set(viewUuid, keys);
4778
+ }
4779
+ /** The namespace of the explicit set of the name in the module written last. */
4780
+ findLastWrittenNamespace(module, name) {
4781
+ let namespace;
4782
+ this.explicitEntries.forEach((_, key) => {
4783
+ const parsed = parseExplicitKey(key);
4784
+ if (parsed.module === module && parsed.name === name) {
4785
+ namespace = parsed.namespace;
4786
+ }
4787
+ });
4788
+ return namespace;
4789
+ }
4790
+ isFetching(entryKey, version) {
4791
+ return this.fetchRecords.get(entryKey)?.fetchingVersion === version;
4792
+ }
4793
+ markFetching(entryKey, version) {
4794
+ const record = this.fetchRecords.get(entryKey) ?? { writtenSequence: 0 };
4795
+ record.fetchingVersion = version;
4796
+ this.fetchRecords.set(entryKey, record);
4797
+ }
4798
+ /**
4799
+ * Records the fetch as the last writer of the entry, unless a later fetch wrote it already.
4800
+ *
4801
+ * @returns whether the answer of the fetch may write the entry.
4802
+ */
4803
+ checkAndRecordWrite(entryKey, sequence) {
4804
+ const record = this.fetchRecords.get(entryKey) ?? { writtenSequence: 0 };
4805
+ if (record.writtenSequence > sequence) {
4806
+ return false;
4807
+ }
4808
+ record.writtenSequence = sequence;
4809
+ this.fetchRecords.set(entryKey, record);
4810
+ return true;
4811
+ }
4812
+ /** Drops the least recently written sets nothing reads beyond the count limit. */
4813
+ evictExplicitEntries() {
4814
+ const readKeys = this.collectReadExplicitKeys();
4815
+ let count = this.explicitEntries.size;
4816
+ for (const key of [...this.explicitEntries.keys()]) {
4817
+ if (count <= this.sizeLimit) {
4818
+ return;
4819
+ }
4820
+ if (!readKeys.has(key)) {
4821
+ this.explicitEntries.delete(key);
4822
+ this.fetchRecords.delete(key);
4823
+ count--;
4824
+ }
4825
+ }
4826
+ }
4827
+ /** The values a widget of the view lists for its selection, see {@link getForSelection}. */
4828
+ findForSelection(viewUuid, selection) {
4829
+ const viewEntry = this.viewEntries.get(viewUuid)?.get(selection.valueSetName);
4830
+ // An implicit set of the view wins: the explicit one is not read.
4831
+ const data = viewEntry?.implicit
4832
+ ? viewEntry.data
4833
+ : this.explicitEntries.get(createExplicitKey(this.findViewModule(viewUuid), selection.valueSetNamespace, selection.valueSetName))?.data;
4834
+ return data && this.mergeViewExtras(data, viewEntry, selection);
4835
+ }
4836
+ mergeViewExtras(data, viewEntry, selection) {
4837
+ return viewEntry?.extraValues?.length || viewEntry?.excludedKeys?.length
4838
+ ? mergeExtraValues(data, viewEntry, selection)
4839
+ : data;
4840
+ }
4841
+ /** Emits the values the read gives now and again whenever the store writes a set of the name. */
4842
+ followChanges(name, read) {
4843
+ return this.changes.pipe(filter((changed) => changed === name), startWith(name), map(read), distinctUntilChanged(deepEqual));
4844
+ }
4845
+ /** The module a view is served by; the empty string stands for the session's own backend. */
4846
+ findViewModule(viewUuid) {
4847
+ return (this.injector
4848
+ .get(SmartViewContextService)
4849
+ .getCurrentViewContext()
4850
+ ?.views?.find((view) => view.uuid === viewUuid)?.module ?? "");
4851
+ }
4852
+ /** Writes the explicit entry as the most recently written one. */
4853
+ writeExplicitEntry(key, name, entry) {
4854
+ this.explicitEntries.delete(key);
4855
+ this.explicitEntries.set(key, entry);
4856
+ this.changeSource.next(name);
4857
+ }
4858
+ writeViewEntry(viewUuid, key, entry) {
4859
+ const entries = this.viewEntries.get(viewUuid) ?? new Map();
4860
+ entries.set(key, entry);
4861
+ this.viewEntries.set(viewUuid, entries);
4862
+ this.changeSource.next(key);
4863
+ }
4864
+ }
4865
+ /**
4866
+ * The set without the values whose text at the set's key property is an excluded key, then the
4867
+ * view's extras keyed by their code under the selection's key property, unless the set has the key.
4868
+ */
4869
+ function mergeExtraValues(data, extras, selection) {
4870
+ const values = data.values ?? [];
4871
+ const keyProperty = valueSetKeyProperty(values, selection.keyProperty ?? data.keyProperty);
4872
+ const displayProperty = valueSetDisplayProperty(values, selection.displayProperty);
4873
+ const excludedKeys = new Set(extras.excludedKeys ?? []);
4874
+ const keptValues = values.filter((value) => {
4875
+ const key = data.keyProperty ? value?.[data.keyProperty] : undefined;
4876
+ return key === undefined || key === null || !excludedKeys.has(String(key));
4877
+ });
4878
+ const keys = new Set(keptValues.map((value) => convertKeyToText(value?.[keyProperty])));
4879
+ const extraValues = (extras.extraValues ?? [])
4880
+ .filter((extra) => extra.code !== undefined && !keys.has(extra.code))
4881
+ .map((extra) => ({
4882
+ ...extra,
4883
+ [keyProperty]: extra.code,
4884
+ [displayProperty]: extra.displayValue,
4885
+ }));
4886
+ return { ...data, values: [...keptValues, ...extraValues] };
4887
+ }
4888
+ function convertKeyToText(key) {
4889
+ return key === undefined || key === null ? undefined : String(key);
4890
+ }
4891
+ function createExplicitKey(module, namespace, name, branchUri) {
4892
+ return JSON.stringify([module ?? "", namespace ?? "", name, branchUri ?? ""]);
4893
+ }
4894
+ /** The parts of an explicit key, an empty namespace or branch as undefined. */
4895
+ function parseExplicitKey(key) {
4896
+ const [module, namespace, name, branchUri] = JSON.parse(key);
4897
+ return {
4898
+ module,
4899
+ namespace: namespace || undefined,
4900
+ name,
4901
+ branchUri: branchUri || undefined,
4902
+ };
4903
+ }
4904
+ function createImplicitKey(viewUuid, key) {
4905
+ return JSON.stringify([viewUuid, key]);
4906
+ }
4907
+
4231
4908
  class SmartNavigationService {
4232
4909
  constructor() { }
4233
4910
  subscribeToUrlChange(callback) {
@@ -7086,31 +7763,6 @@ var ValueChangeMode;
7086
7763
  ValueChangeMode["BLUR"] = "BLUR";
7087
7764
  })(ValueChangeMode || (ValueChangeMode = {}));
7088
7765
 
7089
- /**
7090
- * The key and display property of a valueSet's values: the preferred name if the values carry it,
7091
- * then the GenericValue name (`uri` / `name`), then the Value name (`objectUri` / `displayValue`).
7092
- * With no sample value to look at the preferred name is assumed to exist.
7093
- */
7094
- function valueSetKeyProperty(values, preferred) {
7095
- return firstExisting(values, [preferred ?? 'objectUri', 'uri', 'objectUri']);
7096
- }
7097
- function valueSetDisplayProperty(values, preferred) {
7098
- return firstExisting(values, [preferred ?? 'displayValue', 'name', 'displayValue']);
7099
- }
7100
- /** The label of a code in a valueSet, or undefined when the valueSet does not know the code. */
7101
- function valueSetLabelOf(valueSet, code) {
7102
- const values = valueSet.valueSetData?.values ?? [];
7103
- const keyProperty = valueSetKeyProperty(values, valueSet.valueSetData?.keyProperty);
7104
- const hit = values.find((value) => String(value[keyProperty]) === String(code));
7105
- return hit?.[valueSetDisplayProperty(values)];
7106
- }
7107
- function firstExisting(values, candidates) {
7108
- if (!values?.length) {
7109
- return candidates[0];
7110
- }
7111
- return candidates.find((c) => c in values[0]) ?? candidates[candidates.length - 1];
7112
- }
7113
-
7114
7766
  const EMPTY_PARAMS = {};
7115
7767
  const NAMED_VALIDATOR = new InjectionToken('Named validator factory');
7116
7768
 
@@ -7223,6 +7875,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
7223
7875
  class SmartFormService {
7224
7876
  constructor() {
7225
7877
  this.validatorProvider = inject(NamedValidatorService, { optional: true });
7878
+ /** Looks up the value set store at use: a form without a view does not need it. */
7879
+ this.injector = inject(Injector);
7226
7880
  this.revisionState = signal(0, /* @ts-ignore */
7227
7881
  ...(ngDevMode ? [{ debugName: "revisionState" }] : /* istanbul ignore next */ []));
7228
7882
  /**
@@ -7247,6 +7901,8 @@ class SmartFormService {
7247
7901
  * says nothing about what the data holds.
7248
7902
  */
7249
7903
  this.exchanged = new Map();
7904
+ /** The values each selection widget's `valueList` was last filled from. */
7905
+ this.filledValueSets = new WeakMap();
7250
7906
  this.regexRegexChars = new RegExp(/[\.\-\[\]\/\{\}\(\)\+\^\$\\\|]/g);
7251
7907
  this.regexAsteriskInTheMiddle = new RegExp(/([^\*])\*([^\*])/g);
7252
7908
  this.regexAsteriskInTheEnd = new RegExp(/([^\*])\*$/g);
@@ -7405,7 +8061,7 @@ class SmartFormService {
7405
8061
  // The backend changed this field: its value reaches the control even over an edit.
7406
8062
  this.group[key]?.setValue(value ?? '', { emitEvent: false });
7407
8063
  }
7408
- this.copyFromValueSetToValuesList(widget, smartForm.componentModel.valueSets);
8064
+ this.fillValueListFromValueSet(widget);
7409
8065
  }
7410
8066
  else {
7411
8067
  // In case of CheckBox2
@@ -7413,62 +8069,113 @@ class SmartFormService {
7413
8069
  splittedKeys.splice(splittedKeys.length - 1, 1);
7414
8070
  widget = this.findSmartFormWidgetByKey(widgets, splittedKeys.join('.'));
7415
8071
  if (widget && widget.type === SmartFormWidgetType.CHECK_BOX_2) {
7416
- this.copyFromValueSetToValuesList(widget, smartForm.componentModel.valueSets);
7417
- if (widget.valueList) {
7418
- widget.valueList = widget.valueList.map((item) => {
7419
- let value = this.getValueDeeply(data, `${widget?.key}.${item.key}`);
7420
- item.value = value;
7421
- return item;
7422
- });
7423
- }
8072
+ this.fillValueListFromValueSet(widget);
8073
+ this.fillCheckBoxItemValues(widget, data);
7424
8074
  }
7425
8075
  }
7426
8076
  }
7427
8077
  }
7428
8078
  }
7429
- copyFromValueSetToValuesList(widget, valueSets) {
7430
- if (widget.type === SmartFormWidgetType.SELECT ||
7431
- widget.type === SmartFormWidgetType.SELECT_MULTIPLE ||
7432
- widget.type === SmartFormWidgetType.RADIO_BUTTON ||
7433
- widget.type === SmartFormWidgetType.CHECK_BOX_2 ||
7434
- widget.type === SmartFormWidgetType.TEXT_FIELD) {
7435
- if (widget.selection && valueSets) {
7436
- let valueSet = valueSets[widget.selection.valueSetName];
7437
- if (!valueSet) {
7438
- console.warn('valueSet not found', widget.selection.valueSetName);
7439
- }
7440
- else {
7441
- let valueSetData = valueSet.valueSetData;
7442
- let values = valueSetData.values;
7443
- let selectionDefinition = widget.selection;
7444
- let displayProperty = valueSetDisplayProperty(values, selectionDefinition?.displayProperty);
7445
- if (widget.selection.type === SelectionDefinitionTypeEnum.PROPERTY &&
7446
- !widget.selection.mapping.length) {
7447
- let keyProperty = valueSetKeyProperty(values, selectionDefinition?.keyProperty ?? valueSetData?.keyProperty);
7448
- widget.valueList = values.map((value) => {
7449
- return {
7450
- key: value[keyProperty],
7451
- label: value[displayProperty],
7452
- type: SmartFormWidgetType.ITEM,
7453
- value: value[keyProperty],
7454
- };
7455
- });
7456
- this.addEmptyItemToValueList(widget, widget.selection.emptyLabel);
7457
- }
7458
- else if (widget.selection.type === SelectionDefinitionTypeEnum.OBJECT &&
7459
- !widget.selection.mapping.length) {
7460
- widget.valueList = values.map((value, index) => {
7461
- return {
7462
- key: `${index}`,
7463
- label: value[displayProperty],
7464
- type: SmartFormWidgetType.ITEM,
7465
- value: value,
7466
- };
7467
- });
7468
- this.addEmptyItemToValueList(widget);
7469
- }
8079
+ /**
8080
+ * Lists the values of a selection widget's value set each time the model writes the field, the
8081
+ * empty item of a SELECT included, also where a mandatory constraint removed it: the set of the
8082
+ * form's own component model, else the values the store last gave the widget.
8083
+ */
8084
+ fillValueListFromValueSet(widget) {
8085
+ const valueSetData = this.findOwnValueSetData(widget) ?? this.filledValueSets.get(widget);
8086
+ if (valueSetData) {
8087
+ this.fillValueList(widget, valueSetData);
8088
+ }
8089
+ }
8090
+ /**
8091
+ * The set a selection widget's form carries in its own component model under the selection's
8092
+ * name: a form a host builds, or a delivery without the store. It wins over the store's.
8093
+ */
8094
+ findOwnValueSetData(widget) {
8095
+ if (!isListedFromValueSet(widget)) {
8096
+ return undefined;
8097
+ }
8098
+ const name = widget.selection.valueSetName;
8099
+ return this.smartForm?.componentModel?.valueSets?.[name]?.valueSetData;
8100
+ }
8101
+ /**
8102
+ * The store's values for the selection widget in its view, emitted at once and at every new
8103
+ * version; nothing for a widget that lists no value set.
8104
+ */
8105
+ observeValueSetData(widget, viewUuid) {
8106
+ if (!isListedFromValueSet(widget)) {
8107
+ return undefined;
8108
+ }
8109
+ return this.injector
8110
+ .get(SmartValueSetStore)
8111
+ .getForSelection(viewUuid, widget.selection);
8112
+ }
8113
+ /**
8114
+ * Lists the values the store gave the widget when they are not the ones listed, unless its form
8115
+ * carries its own; a checkbox group gets the values of its items from the data and a control for
8116
+ * each new item.
8117
+ *
8118
+ * @returns whether the widget lists new values.
8119
+ */
8120
+ relistValueSet(widget, storeValueSetData, form) {
8121
+ const valueSetData = this.findOwnValueSetData(widget) ?? storeValueSetData;
8122
+ if (!valueSetData || deepEqual(this.filledValueSets.get(widget), valueSetData)) {
8123
+ return false;
8124
+ }
8125
+ this.fillValueList(widget, valueSetData);
8126
+ if (widget.type === SmartFormWidgetType.CHECK_BOX_2) {
8127
+ this.fillCheckBoxItemValues(widget, this.smartForm?.componentModel?.data);
8128
+ // The widget's form, not this service's controls: a host may give the widget its own form.
8129
+ widget.valueList.forEach((item) => {
8130
+ const key = `${widget.key}.${item.key}`;
8131
+ if (form && !form.controls[key]) {
8132
+ form.addControl(key, new UntypedFormControl({ value: item.value ?? '', disabled: !!widget.isDisabled }), { emitEvent: false });
7470
8133
  }
7471
- }
8134
+ });
8135
+ }
8136
+ return true;
8137
+ }
8138
+ /** Gives each item of a checkbox group its value from the data. */
8139
+ fillCheckBoxItemValues(widget, data) {
8140
+ widget.valueList = widget.valueList?.map((item) => {
8141
+ item.value = data ? this.getValueDeeply(data, `${widget.key}.${item.key}`) : undefined;
8142
+ return item;
8143
+ });
8144
+ }
8145
+ /**
8146
+ * Fills the `valueList` of a selection widget from the values of its value set, a SELECT with
8147
+ * the empty item; a constraint that makes the widget mandatory removes it.
8148
+ */
8149
+ fillValueList(widget, valueSetData) {
8150
+ this.filledValueSets.set(widget, valueSetData);
8151
+ let values = valueSetData.values;
8152
+ let select = widget;
8153
+ let selection = select.selection;
8154
+ let displayProperty = valueSetDisplayProperty(values, selection.displayProperty);
8155
+ if (selection.type === SelectionDefinitionTypeEnum.PROPERTY &&
8156
+ !selection.mapping?.length) {
8157
+ let keyProperty = valueSetKeyProperty(values, selection.keyProperty ?? valueSetData?.keyProperty);
8158
+ select.valueList = values.map((value) => {
8159
+ return {
8160
+ key: value[keyProperty],
8161
+ label: value[displayProperty],
8162
+ type: SmartFormWidgetType.ITEM,
8163
+ value: value[keyProperty],
8164
+ };
8165
+ });
8166
+ this.addEmptyItemToValueList(select, selection.emptyLabel);
8167
+ }
8168
+ else if (selection.type === SelectionDefinitionTypeEnum.OBJECT &&
8169
+ !selection.mapping?.length) {
8170
+ select.valueList = values.map((value, index) => {
8171
+ return {
8172
+ key: `${index}`,
8173
+ label: value[displayProperty],
8174
+ type: SmartFormWidgetType.ITEM,
8175
+ value: value,
8176
+ };
8177
+ });
8178
+ this.addEmptyItemToValueList(select);
7472
8179
  }
7473
8180
  }
7474
8181
  toFormGroup() {
@@ -7806,6 +8513,15 @@ class SmartFormService {
7806
8513
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SmartFormService, decorators: [{
7807
8514
  type: Injectable
7808
8515
  }] });
8516
+ /** Whether the widget lists the values of the value set its selection names. */
8517
+ function isListedFromValueSet(widget) {
8518
+ return ((widget.type === SmartFormWidgetType.SELECT ||
8519
+ widget.type === SmartFormWidgetType.SELECT_MULTIPLE ||
8520
+ widget.type === SmartFormWidgetType.RADIO_BUTTON ||
8521
+ widget.type === SmartFormWidgetType.CHECK_BOX_2 ||
8522
+ widget.type === SmartFormWidgetType.TEXT_FIELD) &&
8523
+ !!widget.selection);
8524
+ }
7809
8525
  /**
7810
8526
  * Splits a date-time widget's backend value into the `HH:mm` text of its companion
7811
8527
  * `<key>-time` control, and normalizes the widget's own value to a `Date` for the picker.
@@ -8866,11 +9582,11 @@ const PIPES = {
8866
9582
  time: (v, arg, ctx) => formatDate(v, arg ?? 'H:mm', ctx),
8867
9583
  number: (v, arg, ctx) => formatNumber(v, arg ?? '1.0-3', ctx),
8868
9584
  label: (v, arg, ctx) => {
8869
- const valueSet = ctx.valueSets?.[arg];
8870
- if (isEmpty(v) || !valueSet) {
9585
+ const valueSetData = isEmpty(v) ? undefined : ctx.findValueSetData?.(arg);
9586
+ if (!valueSetData) {
8871
9587
  return v;
8872
9588
  }
8873
- return valueSetLabelOf(valueSet, v) ?? v;
9589
+ return valueSetLabelOf(valueSetData, v) ?? v;
8874
9590
  },
8875
9591
  default: (v, arg) => (isEmpty(v) ? (arg ?? '') : v),
8876
9592
  upper: (v, _arg, ctx) => (isEmpty(v) ? v : String(v).toLocaleUpperCase(locales(ctx))),
@@ -9387,6 +10103,32 @@ function ownAttribute(element, name, value, wanted, owned) {
9387
10103
  return owned;
9388
10104
  }
9389
10105
 
10106
+ /**
10107
+ * The lookup the `label` pipe reads a value set through by its name: the set a component model
10108
+ * carries under the name (a form a host builds, or a delivery without the store), else the set
10109
+ * the value set store serves the view under the name.
10110
+ */
10111
+ function createValueSetLookup(valueSets, store, viewUuid) {
10112
+ return (name) => valueSets?.[name]?.valueSetData ?? (viewUuid ? store.findByName(viewUuid, name) : undefined);
10113
+ }
10114
+ function injectValueSetReads() {
10115
+ const changed = signal(0, /* @ts-ignore */
10116
+ ...(ngDevMode ? [{ debugName: "changed" }] : /* istanbul ignore next */ []));
10117
+ let names = new Set();
10118
+ inject(SmartValueSetStore)
10119
+ .changes.pipe(filter((name) => names.has(name)), takeUntilDestroyed())
10120
+ .subscribe(() => changed.update((count) => count + 1));
10121
+ return {
10122
+ changed: changed.asReadonly(),
10123
+ track: (lookup) => {
10124
+ names = new Set();
10125
+ return (name) => {
10126
+ names.add(name);
10127
+ return lookup(name);
10128
+ };
10129
+ },
10130
+ };
10131
+ }
9390
10132
  /**
9391
10133
  * The class the container carries while the html on screen is about to be replaced. A style sheet
9392
10134
  * that gives it an animation or transition plays that before the new html is written; without one
@@ -9498,7 +10240,7 @@ function injectSmartHtmlRenderer(container) {
9498
10240
  render: (newTemplate, context) => untracked(() => {
9499
10241
  template = newTemplate;
9500
10242
  const result = resolveTemplate(template, context.data, {
9501
- valueSets: context.valueSets,
10243
+ findValueSetData: context.findValueSetData,
9502
10244
  locale: locale(),
9503
10245
  warn,
9504
10246
  });
@@ -9548,24 +10290,28 @@ function injectSmartHtmlRenderer(container) {
9548
10290
  /**
9549
10291
  * The HTML widget: server-sent HTML whose client placeholders are resolved against the model of
9550
10292
  * the form it sits on, the widget's `root` applied. It has no control, no label and no value of
9551
- * its own; it follows the form's revision and re-resolves, which rewrites the html only when the
9552
- * result differs. See HTML-WIDGET.md.
10293
+ * its own; it follows the form's revision and the value sets it reads and re-resolves, which
10294
+ * rewrites the html only when the result differs. See HTML-WIDGET.md.
9553
10295
  */
9554
10296
  class SmartHtmlWidgetComponent {
9555
10297
  constructor() {
9556
10298
  this.service = inject(SmartFormService);
10299
+ this.valueSetStore = inject(SmartValueSetStore);
10300
+ this.valueSetReads = injectValueSetReads();
10301
+ this.smartComponent = injectSmartComponent();
9557
10302
  this.renderer = injectSmartHtmlRenderer(inject(ElementRef).nativeElement);
9558
10303
  this.widgetInstance = input.required(/* @ts-ignore */
9559
10304
  ...(ngDevMode ? [{ debugName: "widgetInstance" }] : /* istanbul ignore next */ []));
9560
10305
  effect(() => {
9561
10306
  this.service.revision();
10307
+ this.valueSetReads.changed();
9562
10308
  const properties = this.widgetInstance().htmlProperties;
9563
10309
  const model = this.service.smartForm?.componentModel;
9564
10310
  this.warnUnloaded(properties);
9565
10311
  this.renderer.useCss(properties?.css);
9566
10312
  this.renderer.render(properties?.html ?? '', {
9567
10313
  data: this.scope(model?.data, properties?.root, properties?.html),
9568
- valueSets: model?.valueSets,
10314
+ findValueSetData: this.valueSetReads.track(createValueSetLookup(model?.valueSets, this.valueSetStore, this.smartComponent?.uuid)),
9569
10315
  icons: properties?.icons,
9570
10316
  });
9571
10317
  });
@@ -10175,6 +10921,7 @@ class SmartformwidgetComponent {
10175
10921
  }
10176
10922
  this.filteredOptions = this.onValueChange.pipe(startWith(""), map((value) => this._filter(value || "")), takeUntil(this._destroy$));
10177
10923
  }
10924
+ this.followValueSet();
10178
10925
  if (this.widgetInstance.type === SmartFormWidgetType.YOUTUBE_PLAYER) {
10179
10926
  let videoId = this.parseYoutubeUrl(this.widgetInstance.link);
10180
10927
  if (videoId)
@@ -10201,6 +10948,24 @@ class SmartformwidgetComponent {
10201
10948
  });
10202
10949
  });
10203
10950
  }
10951
+ /**
10952
+ * Lists the value set of a selection widget from the value set store and lists it again at
10953
+ * every new version, with no change of the data. A form without a view follows its own model.
10954
+ */
10955
+ followValueSet() {
10956
+ const viewUuid = this.smartComponent?.uuid;
10957
+ if (!viewUuid) {
10958
+ return;
10959
+ }
10960
+ this.service
10961
+ .observeValueSetData(this.widgetInstance, viewUuid)
10962
+ ?.pipe(takeUntil(this._destroy$))
10963
+ .subscribe((valueSetData) => {
10964
+ if (this.service.relistValueSet(this.widgetInstance, valueSetData, this.form())) {
10965
+ this.changeDetector.markForCheck();
10966
+ }
10967
+ });
10968
+ }
10204
10969
  getSophisticatedEventKind() {
10205
10970
  return this.widgetInstance.type === SmartFormWidgetType.SELECT ||
10206
10971
  this.widgetInstance.type === SmartFormWidgetType.SELECT_MULTIPLE ||
@@ -11946,6 +12711,7 @@ class SmartViewContextService {
11946
12711
  this.dialog = inject(SmartViewContextDialogService);
11947
12712
  this.apiQueueService = inject(ApiQueueService);
11948
12713
  this.themeService = inject(ThemeService);
12714
+ this.valueSetStore = inject(SmartValueSetStore);
11949
12715
  this.inject = inject(Injector);
11950
12716
  this.smartConfig = inject(SMART_NG_CLIENT_CONFIG);
11951
12717
  this.viewContextUuidName = "viewContextUuid";
@@ -12936,7 +13702,10 @@ class SmartViewContextService {
12936
13702
  await this.sendViewContextUpdates(updates);
12937
13703
  this.viewContextHasBeenSynced.next();
12938
13704
  }
12939
- /** Opens and closes the views as the view context says, returns the states to report. */
13705
+ /**
13706
+ * Opens and closes the views as the view context says, returns the states to report. A closed
13707
+ * view's implicit value sets are dropped from the value set store.
13708
+ */
12940
13709
  syncViewStates() {
12941
13710
  const updates = [];
12942
13711
  const ops = buildViewOps(this.viewContext?.views ?? [], this.reuseEnabled());
@@ -12968,6 +13737,9 @@ class SmartViewContextService {
12968
13737
  updates.push(...this.closeView(viewData));
12969
13738
  });
12970
13739
  }
13740
+ updates
13741
+ .filter((update) => update?.state === ViewState.CLOSED && update.uuid)
13742
+ .forEach((update) => this.valueSetStore.removeView(update.uuid));
12971
13743
  return updates;
12972
13744
  }
12973
13745
  async startDownloadsFiles(downloads) {
@@ -13050,7 +13822,7 @@ class SmartViewContextService {
13050
13822
  async message(messageData, messageResult) {
13051
13823
  return await this.handlePerformActionResponse(await this.view
13052
13824
  .message(messageData.viewUuid ?? "", messageData.uuid, messageResult)
13053
- .toPromise());
13825
+ .toPromise(), messageData.viewUuid);
13054
13826
  // in future use this.apiQueueService.addToQueue
13055
13827
  }
13056
13828
  getSmartViewContextApiErrorByCode(code) {
@@ -13183,27 +13955,27 @@ class SmartViewContextService {
13183
13955
  async _performAction(config) {
13184
13956
  return await this.handlePerformActionResponse(await this.withModelSync(config.uiActionRequest, () => this.view
13185
13957
  .performAction(config.uuid, config.uiActionRequest)
13186
- .toPromise()));
13958
+ .toPromise()), config.uuid);
13187
13959
  }
13188
13960
  async _performWidgetAction(config) {
13189
13961
  return await this.handlePerformActionResponse(await this.withModelSync(config.uiActionRequest, () => this.view
13190
13962
  .performWidgetAction(config.uuid, config.widgetId, config.nodeId, config.uiActionRequest)
13191
- .toPromise()));
13963
+ .toPromise()), config.uuid);
13192
13964
  }
13193
13965
  async _performWidgetMainAction(config) {
13194
13966
  return await this.handlePerformActionResponse(await this.withModelSync(config.uiActionRequest, () => this.view
13195
13967
  .performWidgetMainAction(config.uuid, config.widgetId, config.uiActionRequest)
13196
- .toPromise()));
13968
+ .toPromise()), config.uuid);
13197
13969
  }
13198
13970
  async _performUploadAction(config) {
13199
13971
  return await this.handlePerformActionResponse(await this.withModelSync(config.uiActionRequest, () => this.view
13200
13972
  .uploadAction(config.uuid, JSON.stringify(config.uiActionRequest), config.param, config.content)
13201
- .toPromise()));
13973
+ .toPromise()), config.uuid);
13202
13974
  }
13203
13975
  async _performUploadMultipleAction(config) {
13204
13976
  return await this.handlePerformActionResponse(await this.withModelSync(config.uiActionRequest, () => this.view
13205
13977
  .uploadMultipleAction(config.uuid, JSON.stringify(config.uiActionRequest), config.param, config.content)
13206
- .toPromise()));
13978
+ .toPromise()), config.uuid);
13207
13979
  }
13208
13980
  /**
13209
13981
  * Calls run concurrently, so the response of an earlier call can land after that of a later
@@ -13220,8 +13992,22 @@ class SmartViewContextService {
13220
13992
  * reports it closed; the applied view context lists such a view OPENED only because an OPENED
13221
13993
  * report of the client reached the server after the close. So a view to be closed in the view
13222
13994
  * context not applied is closed all the same.
13995
+ *
13996
+ * The Value Set Version Map of the change goes to the value set store, whose fetches are held
13997
+ * until the change is applied, so one request carries the implicit sets the changed models name.
13998
+ * The fetch is not awaited: the widgets follow the store.
13223
13999
  */
13224
- async handlePerformActionResponse(viewContextChange) {
14000
+ async handlePerformActionResponse(viewContextChange, viewUuid) {
14001
+ // The implicit sets the changed models name go in the Value Set Fetch of the version map.
14002
+ const releaseValueSetFetches = this.valueSetStore.holdFetches();
14003
+ try {
14004
+ return await this.applyPerformActionResponse(viewContextChange, viewUuid);
14005
+ }
14006
+ finally {
14007
+ releaseValueSetFetches();
14008
+ }
14009
+ }
14010
+ async applyPerformActionResponse(viewContextChange, viewUuid) {
13225
14011
  await Promise.allSettled(this.runningViewContextUpdates);
13226
14012
  const sequence = viewContextChange?.viewContext?.sequence;
13227
14013
  const applied = this.viewContext?.sequence;
@@ -13239,6 +14025,9 @@ class SmartViewContextService {
13239
14025
  if (viewContextChange?.changes) {
13240
14026
  this.handleChanges(viewContextChange.changes);
13241
14027
  }
14028
+ this.valueSetStore
14029
+ .applyVersions(viewContextChange, viewUuid)
14030
+ .catch((err) => console.error("value set fetch failed", err));
13242
14031
  return viewContextChange?.result;
13243
14032
  }
13244
14033
  handleChanges(changes) {
@@ -13256,7 +14045,7 @@ class SmartViewContextService {
13256
14045
  let change = await this.view.getComponentModel2(uuid).toPromise();
13257
14046
  // Not awaited: the sync ends with a viewcontext PUT, which would serialise the mount.
13258
14047
  // The failure still has to surface, so log it instead of dropping the promise.
13259
- this.handlePerformActionResponse(change).catch((err) => console.error("syncView after component load failed", err));
14048
+ this.handlePerformActionResponse(change, uuid).catch((err) => console.error("syncView after component load failed", err));
13260
14049
  return change;
13261
14050
  }
13262
14051
  getCurrentViewContext() {
@@ -15064,7 +15853,8 @@ function rowActionModels(row, gridIdentifier, serviceToUse) {
15064
15853
  /**
15065
15854
  * A table cell rendered from a column template: server-sent HTML whose client placeholders are
15066
15855
  * resolved against the row's data, the template's `root` applied. The toolbar slots and action
15067
- * triggers in it offer the row's actions, the way a card's row layout does.
15856
+ * triggers in it offer the row's actions, the way a card's row layout does. A new version of a
15857
+ * value set it reads renders it again, which rewrites the html only when the result differs.
15068
15858
  *
15069
15859
  * The cell provides the action host itself rather than the row: the table creates its cells
15070
15860
  * from templates declared in the table, so they inject from there, never from the row element.
@@ -15074,10 +15864,23 @@ class SmartHtmlCellComponent {
15074
15864
  constructor() {
15075
15865
  this.actionHost = inject(SmartActionHost);
15076
15866
  this.smartComponent = injectSmartComponent();
15867
+ this.valueSetStore = inject(SmartValueSetStore);
15868
+ this.valueSetReads = injectValueSetReads();
15077
15869
  this.renderer = injectSmartHtmlRenderer(inject(ElementRef).nativeElement);
15870
+ effect(() => {
15871
+ this.valueSetReads.changed();
15872
+ untracked(() => {
15873
+ if (this.row && this.template) {
15874
+ this.renderTemplate();
15875
+ }
15876
+ });
15877
+ });
15078
15878
  }
15079
15879
  ngOnChanges() {
15080
15880
  this.actionHost.actionModels.set(rowActionModels(this.row, this.gridIdentifier));
15881
+ this.renderTemplate();
15882
+ }
15883
+ renderTemplate() {
15081
15884
  const { html, root, css, icons } = this.template;
15082
15885
  const scope = rootScope(this.row.data, root);
15083
15886
  if (scope === undefined && root && this.warnedRoot !== root) {
@@ -15088,7 +15891,7 @@ class SmartHtmlCellComponent {
15088
15891
  this.renderer.useCss(css);
15089
15892
  this.renderer.render(html ?? '', {
15090
15893
  data: scope,
15091
- valueSets: this.smartComponent?.model?.valueSets,
15894
+ findValueSetData: this.valueSetReads.track(createValueSetLookup(this.smartComponent?.model?.valueSets, this.valueSetStore, this.smartComponent?.uuid)),
15092
15895
  icons,
15093
15896
  });
15094
15897
  }
@@ -15098,7 +15901,7 @@ class SmartHtmlCellComponent {
15098
15901
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SmartHtmlCellComponent, decorators: [{
15099
15902
  type: Component,
15100
15903
  args: [{ selector: 'smart-html-cell', template: '', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [SmartActionHost], styles: ["smart-html-cell{display:block}smart-html-cell [data-sb4-action]{cursor:pointer}smart-html-cell [data-sb4-action].sb4-action-inert{cursor:inherit}smart-html-cell [data-sb4-action].sb4-action-disabled{cursor:default;opacity:.5}\n"] }]
15101
- }], propDecorators: { row: [{
15904
+ }], ctorParameters: () => [], propDecorators: { row: [{
15102
15905
  type: Input,
15103
15906
  args: [{ required: true }]
15104
15907
  }], template: [{
@@ -18266,8 +19069,8 @@ class SmarttreeGenericService extends SmarttreeService {
18266
19069
  }
18267
19070
  async downloadTree() {
18268
19071
  let uuid = this.viewContext.getUuidOfPage(this.pageName);
18269
- const rootNodes = await this.viewContext.handlePerformActionResponse(await this.service.getRootNodes2(uuid, this.treeId).toPromise());
18270
- const uiActions = await this.viewContext.handlePerformActionResponse(await this.service.getMainActions2(uuid, this.treeId).toPromise());
19072
+ const rootNodes = await this.viewContext.handlePerformActionResponse(await this.service.getRootNodes2(uuid, this.treeId).toPromise(), uuid);
19073
+ const uiActions = await this.viewContext.handlePerformActionResponse(await this.service.getMainActions2(uuid, this.treeId).toPromise(), uuid);
18271
19074
  if (uiActions) {
18272
19075
  this.constructUiActionModels(uiActions);
18273
19076
  }
@@ -18282,7 +19085,7 @@ class SmarttreeGenericService extends SmarttreeService {
18282
19085
  }
18283
19086
  async downloadChildren(parentNode) {
18284
19087
  let uuid = this.viewContext.getUuidOfPage(this.pageName);
18285
- const children = await this.viewContext.handlePerformActionResponse(await this.service.getChildrenNodes2(uuid, this.treeId, parentNode.identifier).toPromise());
19088
+ const children = await this.viewContext.handlePerformActionResponse(await this.service.getChildrenNodes2(uuid, this.treeId, parentNode.identifier).toPromise(), uuid);
18286
19089
  if (!children) {
18287
19090
  throw new Error(`Node with identifier ${parentNode.identifier} does not have children.`);
18288
19091
  }
@@ -18291,11 +19094,11 @@ class SmarttreeGenericService extends SmarttreeService {
18291
19094
  async onTreeNodeOpen(treeNode) {
18292
19095
  let viewUuid = this.viewContext.getUuidOfPage(this.pageName);
18293
19096
  if (treeNode?.expanded) {
18294
- let tempTreeNode = await this.viewContext.handlePerformActionResponse(await this.service.collapseNode2(viewUuid, this.treeId, treeNode.identifier).toPromise());
19097
+ let tempTreeNode = await this.viewContext.handlePerformActionResponse(await this.service.collapseNode2(viewUuid, this.treeId, treeNode.identifier).toPromise(), viewUuid);
18295
19098
  treeNode = tempTreeNode;
18296
19099
  }
18297
19100
  else if (!treeNode?.expanded) {
18298
- let tempTreeNode = await this.viewContext.handlePerformActionResponse(await this.service.expandNode2(viewUuid, this.treeId, treeNode?.identifier).toPromise());
19101
+ let tempTreeNode = await this.viewContext.handlePerformActionResponse(await this.service.expandNode2(viewUuid, this.treeId, treeNode?.identifier).toPromise(), viewUuid);
18299
19102
  treeNode = tempTreeNode;
18300
19103
  }
18301
19104
  let node = this.deepSearchTreeNode(treeNode?.identifier, this.treeFromBackend);
@@ -18308,7 +19111,7 @@ class SmarttreeGenericService extends SmarttreeService {
18308
19111
  }
18309
19112
  async selectFolder(folderUri, treeNode) {
18310
19113
  let viewUuid = this.viewContext.getUuidOfPage(this.pageName);
18311
- let updatedTreeNode = await this.viewContext.handlePerformActionResponse(await this.viewContext.withModelSync({}, (request) => this.service.selectNode2(viewUuid, this.treeId, treeNode.identifier, request).toPromise()));
19114
+ let updatedTreeNode = await this.viewContext.handlePerformActionResponse(await this.viewContext.withModelSync({}, (request) => this.service.selectNode2(viewUuid, this.treeId, treeNode.identifier, request).toPromise()), viewUuid);
18312
19115
  if (this.selectedNode) {
18313
19116
  this.selectedNode.selected = false;
18314
19117
  }
@@ -18522,11 +19325,11 @@ class SmarttreeGenericService extends SmarttreeService {
18522
19325
  let nodeId = uiActionRequest.params['nodeUuid'];
18523
19326
  delete uiActionRequest.params['nodeUuid'];
18524
19327
  let viewUuid = this.viewContext.getUuidOfPage(this.pageName);
18525
- this.treeFromBackend = await this.viewContext.handlePerformActionResponse(await this.viewContext.withModelSync(uiActionRequest, (request) => this.service.performAction2(viewUuid, this.treeId, nodeId, request).toPromise()));
19328
+ this.treeFromBackend = await this.viewContext.handlePerformActionResponse(await this.viewContext.withModelSync(uiActionRequest, (request) => this.service.performAction2(viewUuid, this.treeId, nodeId, request).toPromise()), viewUuid);
18526
19329
  }
18527
19330
  async performMainUiActionRequest(uiActionRequest) {
18528
19331
  let viewUuid = this.viewContext.getUuidOfPage(this.pageName);
18529
- this.treeFromBackend = await this.viewContext.handlePerformActionResponse(await this.viewContext.withModelSync(uiActionRequest, (request) => this.service.performMainAction2(viewUuid, this.treeId, request).toPromise()));
19332
+ this.treeFromBackend = await this.viewContext.handlePerformActionResponse(await this.viewContext.withModelSync(uiActionRequest, (request) => this.service.performMainAction2(viewUuid, this.treeId, request).toPromise()), viewUuid);
18530
19333
  }
18531
19334
  handleSpecificDemandsAsynchronously(uiAction, uiActionRequest) {
18532
19335
  throw new Error('Method not implemented.');
@@ -18714,6 +19517,9 @@ class SmartComponentApiClient {
18714
19517
  set model(model) {
18715
19518
  this.modelState.set(model);
18716
19519
  this.actionsState.set(model?.actions);
19520
+ if (this.uuid) {
19521
+ this.valueSetStore.putImplicit(this.uuid, model?.valueSets);
19522
+ }
18717
19523
  this.initComponentByModel();
18718
19524
  }
18719
19525
  get data() {
@@ -18814,6 +19620,7 @@ class SmartComponentApiClient {
18814
19620
  }
18815
19621
  this.viewContext = inject.get(SmartViewContextService);
18816
19622
  this.bootstrap = inject.get(SmartBackendBootstrapService);
19623
+ this.valueSetStore = inject.get(SmartValueSetStore);
18817
19624
  if (this._componentName) {
18818
19625
  let pageNameForComponent = this.viewContext.getPageNameOfComponent(this._componentName);
18819
19626
  if (!this.pageName) {
@@ -19295,6 +20102,7 @@ class SmartComponentApiClient {
19295
20102
  handleValueSetChange(changes, changeKey) {
19296
20103
  let valueSetKey = changeKey.split('.')[1];
19297
20104
  this.model.valueSets[valueSetKey] = changes[changeKey];
20105
+ this.valueSetStore.putImplicit(this.uuid, { [valueSetKey]: changes[changeKey] });
19298
20106
  }
19299
20107
  handleActionChange(changes, changeKey) {
19300
20108
  this.model.actions = changes[changeKey];
@@ -21694,13 +22502,13 @@ class SmartGridComponent {
21694
22502
  this.isBlocked = true;
21695
22503
  const row = this.service.gridModel.page.rows?.find((gridRow) => gridRow.id === rowId);
21696
22504
  const viewContextChange = await this.service.select(this.smartGrid, row);
21697
- this.smartGrid.viewContextService.handlePerformActionResponse(viewContextChange);
22505
+ this.smartGrid.viewContextService.handlePerformActionResponse(viewContextChange, this.smartGrid.gridModel.viewUuid);
21698
22506
  this.refresh();
21699
22507
  }
21700
22508
  async onSelectAll(selected) {
21701
22509
  this.isBlocked = true;
21702
22510
  const viewContextChange = await this.service.selectAll(this.smartGrid, selected);
21703
- this.smartGrid.viewContextService.handlePerformActionResponse(viewContextChange);
22511
+ this.smartGrid.viewContextService.handlePerformActionResponse(viewContextChange, this.smartGrid.gridModel.viewUuid);
21704
22512
  this.refresh();
21705
22513
  }
21706
22514
  async onSelectRows(selectedIds, unselectedIds) {
@@ -21708,7 +22516,7 @@ class SmartGridComponent {
21708
22516
  let selected = selectedIds.filter((item) => item !== undefined);
21709
22517
  let unselected = unselectedIds.filter((item) => item !== undefined);
21710
22518
  const viewContextChange = await this.service.selectRows(this.smartGrid, selected, unselected);
21711
- this.smartGrid.viewContextService.handlePerformActionResponse(viewContextChange);
22519
+ this.smartGrid.viewContextService.handlePerformActionResponse(viewContextChange, this.smartGrid.gridModel.viewUuid);
21712
22520
  this.refresh();
21713
22521
  }
21714
22522
  editColumns() {
@@ -23253,7 +24061,7 @@ class SmartMapComponent {
23253
24061
  return null;
23254
24062
  }
23255
24063
  else {
23256
- return this.viewContextService.handlePerformActionResponse(change);
24064
+ return this.viewContextService.handlePerformActionResponse(change, this.uuid);
23257
24065
  }
23258
24066
  }, (err) => {
23259
24067
  console.warn('Could not submit selection change! ', err);
@@ -25806,10 +26614,8 @@ class SmartComponentLayoutComponent {
25806
26614
  this.smartForm = {
25807
26615
  direction: this.getFormLayout(),
25808
26616
  // A row gets an envelope rather than being written into: its `data` stays the row's own
25809
- // object, and the valueSets of the page come along by reference.
25810
- componentModel: gridRow
25811
- ? { ...gridRow, valueSets: gridRow.valueSets ?? this.smartComponent?.model?.valueSets }
25812
- : this.smartComponent?.model,
26617
+ // object. Its widgets read the value sets of the page's view from the value set store.
26618
+ componentModel: gridRow ? { ...gridRow } : this.smartComponent?.model,
25813
26619
  widgets,
25814
26620
  };
25815
26621
  }
@@ -26803,6 +27609,8 @@ var ACLEntry;
26803
27609
  * Do not edit the class manually.
26804
27610
  */
26805
27611
 
27612
+ /** The key of the view's value set that lists the operations of the matrix. */
27613
+ const OPERATIONS = 'OPERATIONS';
26806
27614
  class AclEditingViewComponent extends SmartComponent {
26807
27615
  get smartForm() {
26808
27616
  return this.smartFormState();
@@ -26824,13 +27632,16 @@ class AclEditingViewComponent extends SmartComponent {
26824
27632
  */
26825
27633
  this.aclActionModels = computed(() => this.actionModels().map((model) => model.uiAction.code === 'SAVE' ? { ...model, exception: true } : model), /* @ts-ignore */
26826
27634
  ...(ngDevMode ? [{ debugName: "aclActionModels" }] : /* istanbul ignore next */ []));
26827
- this.dataChanged.pipe(takeUntil$1(this._destroy$)).subscribe(() => {
26828
- this.constructForm();
27635
+ // The operations come from the value set store, which may get them after the data.
27636
+ this.dataChanged
27637
+ .pipe(switchMap(() => this.valueSetStore.getImplicit(this.uuid, OPERATIONS)), filter$1((operations) => !!operations), takeUntil$1(this._destroy$))
27638
+ .subscribe((operations) => {
27639
+ this.constructForm(operations);
26829
27640
  this.initActions();
26830
27641
  });
26831
27642
  }
26832
- constructForm() {
26833
- let tempOpartions = this.model.valueSets['OPERATIONS'].valueSetData.values.map((value) => value.toString());
27643
+ constructForm(operations) {
27644
+ let tempOpartions = operations.values.map((value) => value.toString());
26834
27645
  let widgets = this.formService.render({
26835
27646
  layoutDefinitions: this.model.layouts['ACL_MATRIX'].widgets,
26836
27647
  });
@@ -27151,5 +27962,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
27151
27962
  * Generated bundle index. Do not edit.
27152
27963
  */
27153
27964
 
27154
- export { ACLEntry, APIS$5 as APIS, AclEditingViewComponent, ApiModule$4 as ApiModule, ApiQueueService, BASE_PATH$7 as BASE_PATH, COLLECTION_FORMATS$7 as COLLECTION_FORMATS, CUSTOM_DIAGRAM_OPTIONS, CloseResult, ComponentFactoryService, ComponentType, ComponentWidgetType, Configuration$7 as Configuration, DEFAULT_ROW_LAYOUT, DIALOG_DISABLE_CLOSE, DataChangeKind, DefaultChartOptionsProvider, DefaultUiActionCode_CLOSE, DrawTime, EMPTY_PARAMS, ExpandableSectionButtonIconPosition, ExpandableSectionButtonType, ExpandableSectionComponent, FilterExpressionBoolOperator$1 as FilterExpressionBoolOperator, FilterExpressionBuilderGroupBuilderGroupKindEnum, FilterExpressionBuilderUiModelTypeEnum, FilterExpressionDataType, FilterExpressionFieldWidgetType, FilterExpressionOperation, FilterExpressionOrderByOrderEnum, GenericPageComponent, GeoMapDataLoadingMode, GeoMapDataSourceType, GeoMapItemKind, GeoMapOperationMode, GeoMapSelectionMode, GeoMapTextType, GridColumnContentType, GridDataAccessConfigKindEnum, GridDefaultRowActionTrigger, GridRowStateDisplay, GridSelectionMode, GridSelectionType, GridUiActionType, GridViewDescriptorKindEnum, HighlightPipe, IS_ASYNC_PARAM_NAME, IconPosition, ImageResourceKindEnum, InvalidSmartlinkComponent, LayoutDirection, LinkTargetEnum, MAP_ENGINE, MapEngine, MenuOpenDirection, MenuTriggerType, MessageOptionType, MessageTextType, MessageType, NAMED_VALIDATOR, NamedValidatorOperationEnum, NamedValidatorService, NoPermissionPageComponent, PageExportFieldScope, PageExportSelectionKind, PageExportValueType, PageExportValuesSource, REUSE_ROUTER_SLOT_COMPONENT, ROOT_CONTAINER_UUID, ROUTER_SLOT_ID, RecordingUploaderPropertiesOpenDirectionEnum, SMART_DATE_FORMATS, SMART_DEFAULT_VIEW_COMPONENTS, SMART_NG_CLIENT_CONFIG, SMART_ROW_LAYOUT_COMPONENT, SearchPageComponent, SelectionDefinitionTypeEnum, SelectionDefinitionValueSetValueTypeEnum, ServerRequestType, SessionAPIS, SessionErrorBehaviour, SessionService, SmartActionHost, SmartActionType, SmartBackendBootstrapService, SmartComponent, SmartComponentApiClient, SmartComponentDetachedDirective, SmartComponentHost, SmartComponentLayoutComponent, SmartDateFnsAdapter, SmartDatePipe, SmartDateTimePipe, SmartDefaultErrorUiService, SmartDiagramComponent, SmartDialog, SmartEmbeddedSlotDirective, SmartExpandableSectionService, SmartFileEditorComponent, SmartFilterComponent, SmartFilterEditorContentComponent, SmartFilterEditorService, SmartFilterExpressionFieldComponent, SmartFilterPosition, SmartFilterType, SmartFilterWidgetComponent, SmartFormInputMode, SmartFormTextFieldButtonIconPosition, SmartFormWidgetDirection, SmartFormWidgetType, SmartFormWidgetWidth, SmartGridButtonType, SmartGridComponent, SmartGridDataLayout, SmartGridService, SmartGridType, SmartIconComponent, SmartIconService, SmartKeybindingService, SmartLayoutDef, SmartLinkChannelVariableInPath, SmartLinkMigrationStatusStatusEnum, SmartLinkUuidVariableInPath, SmartMapComponent, SmartMultiFileEditorComponent, SmartNavbarComponent, SmartNavbarService, SmartNavigationService, SmartNgClientFeatureKind, SmartService, SmartSessionService, SmartSessionTimerComponent, SmartTable, SmartTableButtonType, SmartTableHeaderPropertyType, SmartTableInterfaceTypeEnum, SmartTableOptionButtonDirection, SmartTableOrder, SmartTableType, SmartTimePipe, SmartTooltipDirective, SmartTreeComponent, SmartTreeNodeButtonType, SmartUserSettinsIconPosition, SmartValidatorName, SmartViewContextService, SmartViewRedirect, SmartVoiceRecorderComponent, SmartWidgetHintPosition, SmartWidgetHintPositionEnum, SmartdialogService, SmartfileuploaderComponent, SmartformComponent, SmartformLayoutDefinitionService, SmarttreeGenericService, SmarttreeService, SubjectSelectorComponent, ThemeService, ToggleLabelPosition, ToolbarAlignment, ToolbarAppearance, ToolbarDirection, TreeArrowPosition, TreeGridControl, USE_ADVANCED_FILTER, UiActionButtonComponent, UiActionButtonType, UiActionConfirmDialogService, UiActionDescriptorService, UiActionDialogButtonComponent, UiActionDialogType, UiActionFeedbackType, UiActionInputDialogService, UiActionInputType, UiActionService, UiActionToolbarComponent, UiActionTooltipTooltipPositionEnum, UiBadgeComponent, UiBadgeDirective, UploadWidgetType, ValidationResultPageComponent, ValueChangeMode, ViewContextRebuiltError, ViewEventHandlerViewEventTypeEnum, ViewService, ViewState, ViewType, barChart, bubbleChart, createBasicOptions, doughnutChart, formatChord, hasGroupEditActions, horizontalBarChart, horizontalStackedBarChart, injectSmartComponent, isMacPlatform, matches, multiAxisLineChart, parseChord, pieChart, polarAreaChart, provideSmartNgClient, radarChart, registerSmartWidget, resolveDateLocale, resolveSlot, resolveSmartNgClientConfig, resolveViewHandler, scatterChart, stackedBarChart, transformDataToBarLikeDataSet, transformDataToBasicDataSets, transformDataToBubble, transformDataToScatter, withSmartDiagram, withSmartMap };
27965
+ export { ACLEntry, APIS$5 as APIS, AclEditingViewComponent, ApiModule$4 as ApiModule, ApiQueueService, BASE_PATH$7 as BASE_PATH, COLLECTION_FORMATS$7 as COLLECTION_FORMATS, CUSTOM_DIAGRAM_OPTIONS, CloseResult, ComponentFactoryService, ComponentType, ComponentWidgetType, Configuration$7 as Configuration, DEFAULT_ROW_LAYOUT, DIALOG_DISABLE_CLOSE, DataChangeKind, DefaultChartOptionsProvider, DefaultUiActionCode_CLOSE, DrawTime, EMPTY_PARAMS, ExpandableSectionButtonIconPosition, ExpandableSectionButtonType, ExpandableSectionComponent, FilterExpressionBoolOperator$1 as FilterExpressionBoolOperator, FilterExpressionBuilderGroupBuilderGroupKindEnum, FilterExpressionBuilderUiModelTypeEnum, FilterExpressionDataType, FilterExpressionFieldWidgetType, FilterExpressionOperation, FilterExpressionOrderByOrderEnum, GenericPageComponent, GeoMapDataLoadingMode, GeoMapDataSourceType, GeoMapItemKind, GeoMapOperationMode, GeoMapSelectionMode, GeoMapTextType, GridColumnContentType, GridDataAccessConfigKindEnum, GridDefaultRowActionTrigger, GridRowStateDisplay, GridSelectionMode, GridSelectionType, GridUiActionType, GridViewDescriptorKindEnum, HighlightPipe, IS_ASYNC_PARAM_NAME, IconPosition, ImageResourceKindEnum, InvalidSmartlinkComponent, LayoutDirection, LinkTargetEnum, MAP_ENGINE, MapEngine, MenuOpenDirection, MenuTriggerType, MessageOptionType, MessageTextType, MessageType, NAMED_VALIDATOR, NamedValidatorOperationEnum, NamedValidatorService, NoPermissionPageComponent, PageExportFieldScope, PageExportSelectionKind, PageExportValueType, PageExportValuesSource, REUSE_ROUTER_SLOT_COMPONENT, ROOT_CONTAINER_UUID, ROUTER_SLOT_ID, RecordingUploaderPropertiesOpenDirectionEnum, SMART_DATE_FORMATS, SMART_DEFAULT_VIEW_COMPONENTS, SMART_NG_CLIENT_CONFIG, SMART_ROW_LAYOUT_COMPONENT, SearchPageComponent, SelectionDefinitionTypeEnum, SelectionDefinitionValueSetValueTypeEnum, ServerRequestType, SessionAPIS, SessionErrorBehaviour, SessionService, SmartActionHost, SmartActionType, SmartBackendBootstrapService, SmartComponent, SmartComponentApiClient, SmartComponentDetachedDirective, SmartComponentHost, SmartComponentLayoutComponent, SmartDateFnsAdapter, SmartDatePipe, SmartDateTimePipe, SmartDefaultErrorUiService, SmartDiagramComponent, SmartDialog, SmartEmbeddedSlotDirective, SmartExpandableSectionService, SmartFileEditorComponent, SmartFilterComponent, SmartFilterEditorContentComponent, SmartFilterEditorService, SmartFilterExpressionFieldComponent, SmartFilterPosition, SmartFilterType, SmartFilterWidgetComponent, SmartFormInputMode, SmartFormTextFieldButtonIconPosition, SmartFormWidgetDirection, SmartFormWidgetType, SmartFormWidgetWidth, SmartGridButtonType, SmartGridComponent, SmartGridDataLayout, SmartGridService, SmartGridType, SmartIconComponent, SmartIconService, SmartKeybindingService, SmartLayoutDef, SmartLinkChannelVariableInPath, SmartLinkMigrationStatusStatusEnum, SmartLinkUuidVariableInPath, SmartMapComponent, SmartMultiFileEditorComponent, SmartNavbarComponent, SmartNavbarService, SmartNavigationService, SmartNgClientFeatureKind, SmartService, SmartSessionService, SmartSessionTimerComponent, SmartTable, SmartTableButtonType, SmartTableHeaderPropertyType, SmartTableInterfaceTypeEnum, SmartTableOptionButtonDirection, SmartTableOrder, SmartTableType, SmartTimePipe, SmartTooltipDirective, SmartTreeComponent, SmartTreeNodeButtonType, SmartUserSettinsIconPosition, SmartValidatorName, SmartValueSetStore, SmartViewContextService, SmartViewRedirect, SmartVoiceRecorderComponent, SmartWidgetHintPosition, SmartWidgetHintPositionEnum, SmartdialogService, SmartfileuploaderComponent, SmartformComponent, SmartformLayoutDefinitionService, SmarttreeGenericService, SmarttreeService, SubjectSelectorComponent, ThemeService, ToggleLabelPosition, ToolbarAlignment, ToolbarAppearance, ToolbarDirection, TreeArrowPosition, TreeGridControl, USE_ADVANCED_FILTER, UiActionButtonComponent, UiActionButtonType, UiActionConfirmDialogService, UiActionDescriptorService, UiActionDialogButtonComponent, UiActionDialogType, UiActionFeedbackType, UiActionInputDialogService, UiActionInputType, UiActionService, UiActionToolbarComponent, UiActionTooltipTooltipPositionEnum, UiBadgeComponent, UiBadgeDirective, UploadWidgetType, ValidationResultPageComponent, ValueChangeMode, ViewContextRebuiltError, ViewEventHandlerViewEventTypeEnum, ViewService, ViewState, ViewType, barChart, bubbleChart, createBasicOptions, doughnutChart, formatChord, hasGroupEditActions, horizontalBarChart, horizontalStackedBarChart, injectSmartComponent, isMacPlatform, matches, multiAxisLineChart, parseChord, pieChart, polarAreaChart, provideSmartNgClient, radarChart, registerSmartWidget, resolveDateLocale, resolveSlot, resolveSmartNgClientConfig, resolveViewHandler, scatterChart, stackedBarChart, transformDataToBarLikeDataSet, transformDataToBasicDataSets, transformDataToBubble, transformDataToScatter, withSmartDiagram, withSmartMap };
27155
27966
  //# sourceMappingURL=smartbit4all-ng-client.mjs.map