@smartbit4all/ng-client 7.3.23 → 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: [{
@@ -16629,6 +17432,10 @@ class SmarttreeService {
16629
17432
  syncTree() {
16630
17433
  throw new Error('syncTree method not implemented.');
16631
17434
  }
17435
+ /** Whether the tree was downloaded for another view than the one its page shows now. */
17436
+ isDownloadedForAnotherView() {
17437
+ return false;
17438
+ }
16632
17439
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SmarttreeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
16633
17440
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SmarttreeService, providedIn: 'root' }); }
16634
17441
  }
@@ -16704,9 +17511,15 @@ class SmartTreeComponent {
16704
17511
  // The backend knows this widget by its treeId, which is set when the service is created —
16705
17512
  // not by smartTreeModel.identifier, which only exists after the first successful
16706
17513
  // syncTree, so a tree that has not finished downloading would be unreachable. (#28361)
17514
+ const treeService = this.treeService;
16707
17515
  registerSmartWidget(injectSmartComponent(), {
16708
17516
  identifier: () => this.treeService()?.treeId,
16709
17517
  reload: () => this.treeService().initialize(),
17518
+ // A page opened again keeps this component under a new view uuid, and the server's tree
17519
+ // state of the new view knows none of the node identifiers downloaded for the old one.
17520
+ get reloadDuringInitialSync() {
17521
+ return treeService()?.isDownloadedForAnotherView() ?? false;
17522
+ },
16710
17523
  });
16711
17524
  }
16712
17525
  ngOnInit() {
@@ -18242,6 +19055,10 @@ class SmarttreeGenericService extends SmarttreeService {
18242
19055
  }
18243
19056
  async initialize() {
18244
19057
  if (this.viewContext.hasViewContextUuid()) {
19058
+ if (this.isDownloadedForAnotherView()) {
19059
+ // The selection is a node of the other view's tree, unknown to this view's tree state.
19060
+ this.selectedNode = undefined;
19061
+ }
18245
19062
  await this.downloadTree().then((rootNodes) => {
18246
19063
  if (!deepEqual(this.treeFromBackend, rootNodes)) {
18247
19064
  this.treeFromBackend = rootNodes;
@@ -18252,19 +19069,23 @@ class SmarttreeGenericService extends SmarttreeService {
18252
19069
  }
18253
19070
  async downloadTree() {
18254
19071
  let uuid = this.viewContext.getUuidOfPage(this.pageName);
18255
- const rootNodes = await this.viewContext.handlePerformActionResponse(await this.service.getRootNodes2(uuid, this.treeId).toPromise());
18256
- 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);
18257
19074
  if (uiActions) {
18258
19075
  this.constructUiActionModels(uiActions);
18259
19076
  }
18260
- if (!rootNodes) {
19077
+ if (rootNodes) {
19078
+ // Recorded only for a received tree, so that a download without one is repeated.
19079
+ this.downloadedViewUuid = uuid;
19080
+ }
19081
+ else {
18261
19082
  console.error('RootNodes are undefined!');
18262
19083
  }
18263
19084
  return rootNodes ?? [];
18264
19085
  }
18265
19086
  async downloadChildren(parentNode) {
18266
19087
  let uuid = this.viewContext.getUuidOfPage(this.pageName);
18267
- 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);
18268
19089
  if (!children) {
18269
19090
  throw new Error(`Node with identifier ${parentNode.identifier} does not have children.`);
18270
19091
  }
@@ -18273,11 +19094,11 @@ class SmarttreeGenericService extends SmarttreeService {
18273
19094
  async onTreeNodeOpen(treeNode) {
18274
19095
  let viewUuid = this.viewContext.getUuidOfPage(this.pageName);
18275
19096
  if (treeNode?.expanded) {
18276
- 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);
18277
19098
  treeNode = tempTreeNode;
18278
19099
  }
18279
19100
  else if (!treeNode?.expanded) {
18280
- 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);
18281
19102
  treeNode = tempTreeNode;
18282
19103
  }
18283
19104
  let node = this.deepSearchTreeNode(treeNode?.identifier, this.treeFromBackend);
@@ -18290,7 +19111,7 @@ class SmarttreeGenericService extends SmarttreeService {
18290
19111
  }
18291
19112
  async selectFolder(folderUri, treeNode) {
18292
19113
  let viewUuid = this.viewContext.getUuidOfPage(this.pageName);
18293
- 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);
18294
19115
  if (this.selectedNode) {
18295
19116
  this.selectedNode.selected = false;
18296
19117
  }
@@ -18306,6 +19127,10 @@ class SmarttreeGenericService extends SmarttreeService {
18306
19127
  this.syncTree();
18307
19128
  //this.viewContext.getAndSyncViewContext(this.viewContext.getViewContextUuid());
18308
19129
  }
19130
+ isDownloadedForAnotherView() {
19131
+ return (this.downloadedViewUuid !== undefined &&
19132
+ this.downloadedViewUuid !== this.viewContext.getUuidOfPage(this.pageName));
19133
+ }
18309
19134
  async updateTreeOnTheFly() {
18310
19135
  await this.initialize();
18311
19136
  }
@@ -18500,11 +19325,11 @@ class SmarttreeGenericService extends SmarttreeService {
18500
19325
  let nodeId = uiActionRequest.params['nodeUuid'];
18501
19326
  delete uiActionRequest.params['nodeUuid'];
18502
19327
  let viewUuid = this.viewContext.getUuidOfPage(this.pageName);
18503
- 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);
18504
19329
  }
18505
19330
  async performMainUiActionRequest(uiActionRequest) {
18506
19331
  let viewUuid = this.viewContext.getUuidOfPage(this.pageName);
18507
- 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);
18508
19333
  }
18509
19334
  handleSpecificDemandsAsynchronously(uiAction, uiActionRequest) {
18510
19335
  throw new Error('Method not implemented.');
@@ -18692,6 +19517,9 @@ class SmartComponentApiClient {
18692
19517
  set model(model) {
18693
19518
  this.modelState.set(model);
18694
19519
  this.actionsState.set(model?.actions);
19520
+ if (this.uuid) {
19521
+ this.valueSetStore.putImplicit(this.uuid, model?.valueSets);
19522
+ }
18695
19523
  this.initComponentByModel();
18696
19524
  }
18697
19525
  get data() {
@@ -18792,6 +19620,7 @@ class SmartComponentApiClient {
18792
19620
  }
18793
19621
  this.viewContext = inject.get(SmartViewContextService);
18794
19622
  this.bootstrap = inject.get(SmartBackendBootstrapService);
19623
+ this.valueSetStore = inject.get(SmartValueSetStore);
18795
19624
  if (this._componentName) {
18796
19625
  let pageNameForComponent = this.viewContext.getPageNameOfComponent(this._componentName);
18797
19626
  if (!this.pageName) {
@@ -19273,6 +20102,7 @@ class SmartComponentApiClient {
19273
20102
  handleValueSetChange(changes, changeKey) {
19274
20103
  let valueSetKey = changeKey.split('.')[1];
19275
20104
  this.model.valueSets[valueSetKey] = changes[changeKey];
20105
+ this.valueSetStore.putImplicit(this.uuid, { [valueSetKey]: changes[changeKey] });
19276
20106
  }
19277
20107
  handleActionChange(changes, changeKey) {
19278
20108
  this.model.actions = changes[changeKey];
@@ -21672,13 +22502,13 @@ class SmartGridComponent {
21672
22502
  this.isBlocked = true;
21673
22503
  const row = this.service.gridModel.page.rows?.find((gridRow) => gridRow.id === rowId);
21674
22504
  const viewContextChange = await this.service.select(this.smartGrid, row);
21675
- this.smartGrid.viewContextService.handlePerformActionResponse(viewContextChange);
22505
+ this.smartGrid.viewContextService.handlePerformActionResponse(viewContextChange, this.smartGrid.gridModel.viewUuid);
21676
22506
  this.refresh();
21677
22507
  }
21678
22508
  async onSelectAll(selected) {
21679
22509
  this.isBlocked = true;
21680
22510
  const viewContextChange = await this.service.selectAll(this.smartGrid, selected);
21681
- this.smartGrid.viewContextService.handlePerformActionResponse(viewContextChange);
22511
+ this.smartGrid.viewContextService.handlePerformActionResponse(viewContextChange, this.smartGrid.gridModel.viewUuid);
21682
22512
  this.refresh();
21683
22513
  }
21684
22514
  async onSelectRows(selectedIds, unselectedIds) {
@@ -21686,7 +22516,7 @@ class SmartGridComponent {
21686
22516
  let selected = selectedIds.filter((item) => item !== undefined);
21687
22517
  let unselected = unselectedIds.filter((item) => item !== undefined);
21688
22518
  const viewContextChange = await this.service.selectRows(this.smartGrid, selected, unselected);
21689
- this.smartGrid.viewContextService.handlePerformActionResponse(viewContextChange);
22519
+ this.smartGrid.viewContextService.handlePerformActionResponse(viewContextChange, this.smartGrid.gridModel.viewUuid);
21690
22520
  this.refresh();
21691
22521
  }
21692
22522
  editColumns() {
@@ -23231,7 +24061,7 @@ class SmartMapComponent {
23231
24061
  return null;
23232
24062
  }
23233
24063
  else {
23234
- return this.viewContextService.handlePerformActionResponse(change);
24064
+ return this.viewContextService.handlePerformActionResponse(change, this.uuid);
23235
24065
  }
23236
24066
  }, (err) => {
23237
24067
  console.warn('Could not submit selection change! ', err);
@@ -25784,10 +26614,8 @@ class SmartComponentLayoutComponent {
25784
26614
  this.smartForm = {
25785
26615
  direction: this.getFormLayout(),
25786
26616
  // A row gets an envelope rather than being written into: its `data` stays the row's own
25787
- // object, and the valueSets of the page come along by reference.
25788
- componentModel: gridRow
25789
- ? { ...gridRow, valueSets: gridRow.valueSets ?? this.smartComponent?.model?.valueSets }
25790
- : 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,
25791
26619
  widgets,
25792
26620
  };
25793
26621
  }
@@ -26781,6 +27609,8 @@ var ACLEntry;
26781
27609
  * Do not edit the class manually.
26782
27610
  */
26783
27611
 
27612
+ /** The key of the view's value set that lists the operations of the matrix. */
27613
+ const OPERATIONS = 'OPERATIONS';
26784
27614
  class AclEditingViewComponent extends SmartComponent {
26785
27615
  get smartForm() {
26786
27616
  return this.smartFormState();
@@ -26802,13 +27632,16 @@ class AclEditingViewComponent extends SmartComponent {
26802
27632
  */
26803
27633
  this.aclActionModels = computed(() => this.actionModels().map((model) => model.uiAction.code === 'SAVE' ? { ...model, exception: true } : model), /* @ts-ignore */
26804
27634
  ...(ngDevMode ? [{ debugName: "aclActionModels" }] : /* istanbul ignore next */ []));
26805
- this.dataChanged.pipe(takeUntil$1(this._destroy$)).subscribe(() => {
26806
- 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);
26807
27640
  this.initActions();
26808
27641
  });
26809
27642
  }
26810
- constructForm() {
26811
- let tempOpartions = this.model.valueSets['OPERATIONS'].valueSetData.values.map((value) => value.toString());
27643
+ constructForm(operations) {
27644
+ let tempOpartions = operations.values.map((value) => value.toString());
26812
27645
  let widgets = this.formService.render({
26813
27646
  layoutDefinitions: this.model.layouts['ACL_MATRIX'].widgets,
26814
27647
  });
@@ -27129,5 +27962,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
27129
27962
  * Generated bundle index. Do not edit.
27130
27963
  */
27131
27964
 
27132
- 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 };
27133
27966
  //# sourceMappingURL=smartbit4all-ng-client.mjs.map