@dsivd/prestations-ng 19.2.0-beta.5 → 19.2.0-beta.7

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.
package/CHANGELOG.md CHANGED
@@ -98,6 +98,9 @@ Please check the [UPGRADING_V19.md](UPGRADING_V19.md) guide for more information
98
98
 
99
99
  ### Fixed
100
100
 
101
+ - `providePrestationsNgCore()` registers again the HTTP interceptors lost with `FoehnPageModule` in 19.0.0: the
102
+ `foehn-transmit-waiting-modal` is displayed again during a long transmission, the warning before the expiration of
103
+ the session receives its date again, and a new version of the application deployed meanwhile is detected again.
101
104
  - `foehn-select`, `foehn-radio`, `foehn-checkbox` and the autocompletes:
102
105
  - the model is no longer set back to its previous value when the parent changes `elements` and `model`
103
106
  at the same time
@@ -119,6 +122,10 @@ Please check the [UPGRADING_V19.md](UPGRADING_V19.md) guide for more information
119
122
  - dynamic list: a model changed by the project (another value, `null`) is displayed again; once a value had been
120
123
  displayed, the display no longer followed the model. The elements loaded for the search of the user still do
121
124
  not replace what they type.
125
+ - `foehn-input-address` without zone.js: an address given by the parent right after the initialization of the
126
+ component (e.g. prefilled with `SessionInfoWithApplicationService.prefillForm()` for a connected user) is no
127
+ longer overwritten by the model with the default country only; the default country and the clean-up of the model
128
+ are applied after the next rendering, from the latest model.
122
129
  - `foehn-input-address`: the deferred clean-up of the model (hidden address lines and post office box) applies
123
130
  to the latest model, it could restore a previous value (e.g. remove the default country); nothing is
124
131
  updated anymore once the component is destroyed.
@@ -2,7 +2,7 @@ import * as i0 from '@angular/core';
2
2
  import { Injectable, inject, InjectionToken, model, input, viewChildren, viewChild, forwardRef, output, computed, linkedSignal, DestroyRef, signal, effect, untracked, PendingTasks, Directive, ChangeDetectorRef, Pipe, Component, contentChildren, EnvironmentInjector, runInInjectionContext, afterNextRender, ElementRef, Injector, makeEnvironmentProviders, ErrorHandler, provideBrowserGlobalErrorListeners, provideAppInitializer, importProvidersFrom, NgZone, contentChild, TemplateRef } from '@angular/core';
3
3
  import { takeUntilDestroyed, toSignal, toObservable, outputToObservable } from '@angular/core/rxjs-interop';
4
4
  import { Router, ActivatedRoute, RouterLink, NavigationStart, NavigationEnd } from '@angular/router';
5
- import { Subject, BehaviorSubject, combineLatest, of, filter as filter$1, tap as tap$1, throwError, switchMap as switchMap$1, forkJoin, from, concatMap, toArray, EMPTY, startWith, merge, noop, interval, withLatestFrom, Observable, map as map$1, concat } from 'rxjs';
5
+ import { Subject, BehaviorSubject, combineLatest, of, filter as filter$1, tap as tap$1, throwError, switchMap as switchMap$1, forkJoin, from, concatMap, toArray, EMPTY, startWith, merge, noop, interval, withLatestFrom, map as map$1, concat } from 'rxjs';
6
6
  import { filter, shareReplay, tap, map, debounceTime, catchError, switchMap, first, finalize, throttleTime, mergeMap, share, take, distinctUntilChanged, startWith as startWith$1, takeWhile, takeUntil } from 'rxjs/operators';
7
7
  import { HttpClient, HttpEventType, HttpResponse, HTTP_INTERCEPTORS, HttpStatusCode, HttpResponseBase, HttpErrorResponse, HttpParams, provideHttpClient, withInterceptorsFromDi, withInterceptors } from '@angular/common/http';
8
8
  import * as i1 from '@angular/forms';
@@ -5461,6 +5461,203 @@ class I18nForm {
5461
5461
  referenceInterne = undefined;
5462
5462
  }
5463
5463
 
5464
+ const APP_VERSION_HEADER = 'app-version';
5465
+ class AppInfoHeadersInterceptor {
5466
+ foehnPageService = inject(FoehnPageService);
5467
+ appVersion = undefined;
5468
+ errorListenerRegistered = false;
5469
+ constructor() {
5470
+ // Enregistrer une seule fois
5471
+ if (!this.errorListenerRegistered) {
5472
+ window.addEventListener('error', (event) => {
5473
+ if (event.message?.includes('ChunkLoadError') ||
5474
+ event.message?.includes('Loading chunk')) {
5475
+ this.foehnPageService.setAppVersionMismatch();
5476
+ }
5477
+ });
5478
+ this.errorListenerRegistered = true;
5479
+ }
5480
+ }
5481
+ intercept(request, next) {
5482
+ return next.handle(request).pipe(tap$1((httpEvent) => {
5483
+ if (httpEvent instanceof HttpResponse) {
5484
+ const appVersionInHeader = httpEvent.headers.get(APP_VERSION_HEADER);
5485
+ if (!this.appVersion?.length) {
5486
+ this.appVersion = appVersionInHeader ?? '';
5487
+ }
5488
+ else if (appVersionInHeader &&
5489
+ this.appVersion !== appVersionInHeader) {
5490
+ this.foehnPageService.setAppVersionMismatch();
5491
+ }
5492
+ }
5493
+ }));
5494
+ }
5495
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.23", ngImport: i0, type: AppInfoHeadersInterceptor, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
5496
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.23", ngImport: i0, type: AppInfoHeadersInterceptor, providedIn: 'root' });
5497
+ }
5498
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.23", ngImport: i0, type: AppInfoHeadersInterceptor, decorators: [{
5499
+ type: Injectable,
5500
+ args: [{
5501
+ providedIn: 'root',
5502
+ }]
5503
+ }], ctorParameters: () => [] });
5504
+ const APP_INFO_HEADERS_INTERCEPTOR_PROVIDER = [
5505
+ {
5506
+ provide: HTTP_INTERCEPTORS,
5507
+ useExisting: AppInfoHeadersInterceptor,
5508
+ multi: true,
5509
+ },
5510
+ ];
5511
+
5512
+ class CurrentSessionExpiration {
5513
+ expiration = undefined;
5514
+ type = undefined;
5515
+ }
5516
+
5517
+ class IntervalHelper {
5518
+ static accurateInterval(
5519
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
5520
+ callBack, intervalInMilliseconds,
5521
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
5522
+ errorCallBack) {
5523
+ let timeout = null;
5524
+ let nextAt = Date.now() + intervalInMilliseconds;
5525
+ const clear = () => {
5526
+ if (timeout !== null) {
5527
+ clearTimeout(timeout);
5528
+ }
5529
+ };
5530
+ const timerHandler = () => {
5531
+ nextAt += intervalInMilliseconds;
5532
+ // Avoid time drift by doing "nextAt - Date.now()"
5533
+ const calculatedTimeout = nextAt - Date.now();
5534
+ // Verify browser tab inactivity
5535
+ if (calculatedTimeout < 0) {
5536
+ if (errorCallBack) {
5537
+ clear();
5538
+ return errorCallBack();
5539
+ }
5540
+ }
5541
+ timeout = setTimeout(timerHandler, Math.max(0, nextAt - Date.now()));
5542
+ callBack();
5543
+ };
5544
+ // Avoid time drift by doing "nextAt - Date.now()"
5545
+ timeout = setTimeout(timerHandler, nextAt - Date.now());
5546
+ return { clear };
5547
+ }
5548
+ }
5549
+
5550
+ class DemandeExpirationService {
5551
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5552
+ interval;
5553
+ PULLING_IN_MILLISECONDS = 1000;
5554
+ _demandeExpirationDateTimeSubject = new BehaviorSubject(null);
5555
+ intervalSubject = new BehaviorSubject(undefined);
5556
+ shouldDisplayExpirationTimerMessage() {
5557
+ return combineLatest([
5558
+ this.getDemandeExpirationDateTime(),
5559
+ this.intervalSubject.asObservable(),
5560
+ ]).pipe(map(([value]) => !dayjs().isBefore(value?.expiration)), tap((visible) => {
5561
+ if (visible) {
5562
+ this.clearPulling();
5563
+ }
5564
+ }),
5565
+ // Checked every second: only a change must refresh the views.
5566
+ distinctUntilChanged(), shareReplay(1));
5567
+ }
5568
+ setDemandeExpirationDateTime(expiration) {
5569
+ this._demandeExpirationDateTimeSubject.next(expiration);
5570
+ }
5571
+ getDemandeExpirationDateTime() {
5572
+ return this._demandeExpirationDateTimeSubject.asObservable().pipe(filter((value) => !!value?.expiration?.isValid()), tap((value) => {
5573
+ if (dayjs().isBefore(value.expiration)) {
5574
+ this.startPulling();
5575
+ }
5576
+ }), shareReplay(1));
5577
+ }
5578
+ startPulling() {
5579
+ this.clearPulling();
5580
+ this.interval = IntervalHelper.accurateInterval(() => {
5581
+ this.intervalSubject.next();
5582
+ }, this.PULLING_IN_MILLISECONDS, () => {
5583
+ this.startPulling();
5584
+ });
5585
+ }
5586
+ clearPulling() {
5587
+ this.interval?.clear();
5588
+ }
5589
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.23", ngImport: i0, type: DemandeExpirationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
5590
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.23", ngImport: i0, type: DemandeExpirationService, providedIn: 'root' });
5591
+ }
5592
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.23", ngImport: i0, type: DemandeExpirationService, decorators: [{
5593
+ type: Injectable,
5594
+ args: [{
5595
+ providedIn: 'root',
5596
+ }]
5597
+ }] });
5598
+
5599
+ const EXPIRATION_HEADER = 'Sessions-Expirations';
5600
+ class DemandeExpirationInterceptor {
5601
+ demandeExpirationService = inject(DemandeExpirationService);
5602
+ intercept(request, next) {
5603
+ return next.handle(request).pipe(tap$1((httpEvent) => {
5604
+ if (httpEvent.type === HttpEventType.Sent) {
5605
+ return;
5606
+ }
5607
+ if (httpEvent instanceof HttpResponse) {
5608
+ if (httpEvent.headers.has(EXPIRATION_HEADER)) {
5609
+ const expirationHeader = httpEvent.headers.get(EXPIRATION_HEADER);
5610
+ if (expirationHeader) {
5611
+ this.manageDemandeExpirationHeader(expirationHeader, httpEvent.body);
5612
+ }
5613
+ }
5614
+ }
5615
+ }));
5616
+ }
5617
+ manageDemandeExpirationHeader(expirationByDemande, responseBody) {
5618
+ const expirationByDemandeMap = this.getExpirationByDemandeMap(expirationByDemande);
5619
+ const expiration = new CurrentSessionExpiration();
5620
+ const reference = responseBody?.meta?.reference || '';
5621
+ const dateTimeForReference = expirationByDemandeMap.get(reference);
5622
+ const dateTimeForIam = expirationByDemandeMap.get('IAM');
5623
+ if (dateTimeForReference) {
5624
+ expiration.type = 'SESSION-TOKEN';
5625
+ expiration.expiration = dayjs(dateTimeForReference);
5626
+ }
5627
+ else if (dateTimeForIam) {
5628
+ expiration.type = 'IAM';
5629
+ expiration.expiration = dayjs(dateTimeForIam);
5630
+ }
5631
+ if (expiration.expiration?.isValid()) {
5632
+ this.demandeExpirationService.setDemandeExpirationDateTime(expiration);
5633
+ }
5634
+ }
5635
+ getExpirationByDemandeMap(expirationByDemande) {
5636
+ const myMap = new Map();
5637
+ const expirationByDemandesAsArray = expirationByDemande.split(';');
5638
+ expirationByDemandesAsArray.forEach((value) => {
5639
+ const valueAsArray = value.split(',');
5640
+ myMap.set(valueAsArray[0], new Date(valueAsArray[1]));
5641
+ });
5642
+ return myMap;
5643
+ }
5644
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.23", ngImport: i0, type: DemandeExpirationInterceptor, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
5645
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.23", ngImport: i0, type: DemandeExpirationInterceptor, providedIn: 'root' });
5646
+ }
5647
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.23", ngImport: i0, type: DemandeExpirationInterceptor, decorators: [{
5648
+ type: Injectable,
5649
+ args: [{
5650
+ providedIn: 'root',
5651
+ }]
5652
+ }] });
5653
+ const DEMANDE_EXPIRATION_INTERCEPTOR_PROVIDER = [
5654
+ {
5655
+ provide: HTTP_INTERCEPTORS,
5656
+ useExisting: DemandeExpirationInterceptor,
5657
+ multi: true,
5658
+ },
5659
+ ];
5660
+
5464
5661
  const OIDC_ENABLED = new InjectionToken('enable.oidc.interceptor', {
5465
5662
  factory: () => false,
5466
5663
  });
@@ -5531,6 +5728,11 @@ function providePrestationsNgCore() {
5531
5728
  useExisting: IamExpiredInterceptorService,
5532
5729
  multi: true,
5533
5730
  },
5731
+ // Formerly provided by FoehnPageModule: the version of the application changed during a deployment,
5732
+ // the expiration of the session and the modal displayed during the transmission
5733
+ APP_INFO_HEADERS_INTERCEPTOR_PROVIDER,
5734
+ DEMANDE_EXPIRATION_INTERCEPTOR_PROVIDER,
5735
+ DEMANDE_TRANSMIT_INTERCEPTOR_PROVIDER,
5534
5736
  {
5535
5737
  provide: ErrorHandler,
5536
5738
  useClass: ChunkLoadErrorHandler,
@@ -7456,88 +7658,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.23", ngImpo
7456
7658
  args: [{ selector: 'foehn-app-version-mis-match-modal', imports: [FoehnModalComponent, AsyncPipe, SdkDictionaryPipe], template: "<foehn-modal\n [id]=\"'foehn-app-version-mismatch-modal'\"\n [modalHeaderText]=\"\n 'foehn-app-version-mismatch-modal.title' | fromDictionary\n \"\n modalSize=\"modal-md\"\n [isModalVisible]=\"(isModalVisible | async) ?? false\"\n [closeable]=\"false\"\n>\n <div\n id=\"foehn-app-version-mismatch-modal-body\"\n [innerHTML]=\"\n 'foehn-app-version-mismatch-modal.content' | fromDictionary\n \"\n ></div>\n\n <div modal-footer class=\"w-100\">\n <div class=\"d-flex\">\n <button\n id=\"saveAndReloadBtn\"\n type=\"button\"\n class=\"btn btn-primary ms-auto\"\n [innerHTML]=\"\n (hasDemandeInProgress\n ? 'foehn-app-version-mismatch-modal.btn.save-and-reload'\n : 'foehn-app-version-mismatch-modal.btn.reload'\n ) | fromDictionary\n \"\n (click)=\"saveAndReload()\"\n ></button>\n </div>\n </div>\n</foehn-modal>\n", styles: [":host ::ng-deep .modal{z-index:10000}\n"] }]
7457
7659
  }], ctorParameters: () => [] });
7458
7660
 
7459
- class IntervalHelper {
7460
- static accurateInterval(
7461
- // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
7462
- callBack, intervalInMilliseconds,
7463
- // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
7464
- errorCallBack) {
7465
- let timeout = null;
7466
- let nextAt = Date.now() + intervalInMilliseconds;
7467
- const clear = () => {
7468
- if (timeout !== null) {
7469
- clearTimeout(timeout);
7470
- }
7471
- };
7472
- const timerHandler = () => {
7473
- nextAt += intervalInMilliseconds;
7474
- // Avoid time drift by doing "nextAt - Date.now()"
7475
- const calculatedTimeout = nextAt - Date.now();
7476
- // Verify browser tab inactivity
7477
- if (calculatedTimeout < 0) {
7478
- if (errorCallBack) {
7479
- clear();
7480
- return errorCallBack();
7481
- }
7482
- }
7483
- timeout = setTimeout(timerHandler, Math.max(0, nextAt - Date.now()));
7484
- callBack();
7485
- };
7486
- // Avoid time drift by doing "nextAt - Date.now()"
7487
- timeout = setTimeout(timerHandler, nextAt - Date.now());
7488
- return { clear };
7489
- }
7490
- }
7491
-
7492
- class DemandeExpirationService {
7493
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
7494
- interval;
7495
- PULLING_IN_MILLISECONDS = 1000;
7496
- _demandeExpirationDateTimeSubject = new BehaviorSubject(null);
7497
- intervalSubject = new BehaviorSubject(undefined);
7498
- shouldDisplayExpirationTimerMessage() {
7499
- return combineLatest([
7500
- this.getDemandeExpirationDateTime(),
7501
- this.intervalSubject.asObservable(),
7502
- ]).pipe(map(([value]) => !dayjs().isBefore(value?.expiration)), tap((visible) => {
7503
- if (visible) {
7504
- this.clearPulling();
7505
- }
7506
- }),
7507
- // Checked every second: only a change must refresh the views.
7508
- distinctUntilChanged(), shareReplay(1));
7509
- }
7510
- setDemandeExpirationDateTime(expiration) {
7511
- this._demandeExpirationDateTimeSubject.next(expiration);
7512
- }
7513
- getDemandeExpirationDateTime() {
7514
- return this._demandeExpirationDateTimeSubject.asObservable().pipe(filter((value) => !!value?.expiration?.isValid()), tap((value) => {
7515
- if (dayjs().isBefore(value.expiration)) {
7516
- this.startPulling();
7517
- }
7518
- }), shareReplay(1));
7519
- }
7520
- startPulling() {
7521
- this.clearPulling();
7522
- this.interval = IntervalHelper.accurateInterval(() => {
7523
- this.intervalSubject.next();
7524
- }, this.PULLING_IN_MILLISECONDS, () => {
7525
- this.startPulling();
7526
- });
7527
- }
7528
- clearPulling() {
7529
- this.interval?.clear();
7530
- }
7531
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.23", ngImport: i0, type: DemandeExpirationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
7532
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.23", ngImport: i0, type: DemandeExpirationService, providedIn: 'root' });
7533
- }
7534
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.23", ngImport: i0, type: DemandeExpirationService, decorators: [{
7535
- type: Injectable,
7536
- args: [{
7537
- providedIn: 'root',
7538
- }]
7539
- }] });
7540
-
7541
7661
  class FoehnPageExpirationTimerComponent {
7542
7662
  isVisible;
7543
7663
  countDownTimeToDisplay;
@@ -8924,34 +9044,6 @@ class StreetNumber {
8924
9044
  }
8925
9045
  const isDefined$1 = (value) => value === true || value === false;
8926
9046
  const hasInputChanged$1 = (change) => change && !ObjectHelper.isEqual(change.previousValue, change.currentValue);
8927
- /**
8928
- * Same as `debounceTime(0)`, but the application is not stable (PendingTasks, e.g. `fixture.whenStable()`)
8929
- * while a value is waiting to be emitted and processed synchronously by the next operators.
8930
- */
8931
- const debounceTimeZeroAsPendingTask = (pendingTasks) => (source) => new Observable((subscriber) => {
8932
- let removeTask = null;
8933
- const subscription = source
8934
- .pipe(tap(() => (removeTask ??= pendingTasks.add())), debounceTime(0))
8935
- .subscribe({
8936
- next: (value) => {
8937
- const removeSettledTask = removeTask;
8938
- removeTask = null;
8939
- try {
8940
- subscriber.next(value);
8941
- }
8942
- finally {
8943
- removeSettledTask?.();
8944
- }
8945
- },
8946
- error: (error) => subscriber.error(error),
8947
- complete: () => subscriber.complete(),
8948
- });
8949
- return () => {
8950
- subscription.unsubscribe();
8951
- removeTask?.();
8952
- removeTask = null;
8953
- };
8954
- });
8955
9047
  class FoehnInputAddressComponent extends FoehnInputComponent {
8956
9048
  isOnlySwiss = input(true, ...(ngDevMode ? [{ debugName: "isOnlySwiss" }] : /* istanbul ignore next */ []));
8957
9049
  showPostOfficeBoxText = input(false, ...(ngDevMode ? [{ debugName: "showPostOfficeBoxText" }] : /* istanbul ignore next */ []));
@@ -8998,8 +9090,9 @@ class FoehnInputAddressComponent extends FoehnInputComponent {
8998
9090
  streetCustomErrors = signal({}, ...(ngDevMode ? [{ debugName: "streetCustomErrors" }] : /* istanbul ignore next */ []));
8999
9091
  httpClient = inject(HttpClient);
9000
9092
  dictionaryService = inject(SdkDictionaryService);
9001
- addressPendingTasks = inject(PendingTasks);
9093
+ addressInjector = inject(Injector);
9002
9094
  isSanitizationScheduled = false;
9095
+ isDefaultCountryScheduled = false;
9003
9096
  iso2IdSubject = new BehaviorSubject(null);
9004
9097
  zipCodeIdSubject = new BehaviorSubject(null);
9005
9098
  estrIdSubject = new BehaviorSubject(null);
@@ -9235,32 +9328,21 @@ class FoehnInputAddressComponent extends FoehnInputComponent {
9235
9328
  return ObjectHelper.isDifferent(value, sanitized) ? sanitized : null;
9236
9329
  }
9237
9330
  /**
9238
- * The sanitized model is set in a macrotask (not while the view is checked) and computed from the latest model
9239
- * (e.g. the default country may have been set in between). The application is not stable until it is done.
9331
+ * The sanitized model is set after the next rendering (not while the view is checked), from the latest model: the
9332
+ * model given by the parent in between (e.g. prefilled asynchronously) has been received, it is not overwritten.
9240
9333
  */
9241
9334
  scheduleModelSanitization() {
9242
9335
  if (this.isSanitizationScheduled) {
9243
9336
  return;
9244
9337
  }
9245
9338
  this.isSanitizationScheduled = true;
9246
- const removeTask = this.addressPendingTasks.add();
9247
- const timeoutId = setTimeout(() => {
9248
- unregisterOnDestroy();
9339
+ afterNextRender(() => {
9249
9340
  this.isSanitizationScheduled = false;
9250
- try {
9251
- const sanitized = this.getSanitizedModel(this.model());
9252
- if (sanitized) {
9253
- this.updateNgModel(sanitized);
9254
- }
9255
- }
9256
- finally {
9257
- removeTask();
9341
+ const sanitized = this.getSanitizedModel(this.model());
9342
+ if (sanitized) {
9343
+ this.updateNgModel(sanitized);
9258
9344
  }
9259
- });
9260
- const unregisterOnDestroy = this._destroyRef.onDestroy(() => {
9261
- clearTimeout(timeoutId);
9262
- removeTask();
9263
- });
9345
+ }, { injector: this.addressInjector });
9264
9346
  }
9265
9347
  replaceAtLeastOneNotNull(notNullErrorLabel) {
9266
9348
  if (!this.showPostOfficeBoxText()) {
@@ -9402,29 +9484,45 @@ class FoehnInputAddressComponent extends FoehnInputComponent {
9402
9484
  this.isEmpty(this.addressLine2()));
9403
9485
  }
9404
9486
  manageDefaultCountry() {
9405
- // The application is not stable until the debounced changes are processed (the model can be updated).
9406
9487
  combineLatest([
9407
- this.countryModelChangeSubject.asObservable().pipe(debounceTimeZeroAsPendingTask(this.addressPendingTasks), distinctUntilChanged((previous, current) => previous?.iso2Id === current?.iso2Id)),
9488
+ this.countryModelChangeSubject
9489
+ .asObservable()
9490
+ .pipe(distinctUntilChanged((previous, current) => previous?.iso2Id === current?.iso2Id)),
9408
9491
  this.isOnlySwissChangeSubject
9409
9492
  .asObservable()
9410
- .pipe(debounceTimeZeroAsPendingTask(this.addressPendingTasks), distinctUntilChanged()),
9493
+ .pipe(distinctUntilChanged()),
9411
9494
  ])
9412
- .pipe(tap(([country, isOnlySwiss]) => {
9413
- if (!isOnlySwiss &&
9414
- !country &&
9415
- !this.isCountryInputDirty()) {
9416
- this.setDefaultCountry();
9417
- return;
9418
- }
9419
- if (isOnlySwiss) {
9420
- // When there is a country, we need to check if it has to be cleaned and set to default
9421
- if (country && !ObjectHelper.isEmpty(country)) {
9422
- this.clearAutocompletesWhenCountryIsUnappropriatedToComponentConfiguration();
9423
- }
9424
- this.setDefaultCountry();
9495
+ .pipe(takeUntilDestroyed(this._destroyRef))
9496
+ .subscribe(() => this.scheduleDefaultCountry());
9497
+ }
9498
+ /**
9499
+ * The default country is applied after the next rendering (once for the changes of the same rendering), from the
9500
+ * latest state: without zone.js, a model given by the parent in between (e.g. prefilled asynchronously) is only
9501
+ * received with the next change detection, it must not be overwritten by the model with the default country only.
9502
+ */
9503
+ scheduleDefaultCountry() {
9504
+ if (this.isDefaultCountryScheduled) {
9505
+ return;
9506
+ }
9507
+ this.isDefaultCountryScheduled = true;
9508
+ afterNextRender(() => {
9509
+ this.isDefaultCountryScheduled = false;
9510
+ this.applyDefaultCountry();
9511
+ }, { injector: this.addressInjector });
9512
+ }
9513
+ applyDefaultCountry() {
9514
+ const country = this.country();
9515
+ if (!this.isOnlySwiss() && !country && !this.isCountryInputDirty()) {
9516
+ this.setDefaultCountry();
9517
+ return;
9518
+ }
9519
+ if (this.isOnlySwiss()) {
9520
+ // When there is a country, we need to check if it has to be cleaned and set to default
9521
+ if (country && !ObjectHelper.isEmpty(country)) {
9522
+ this.clearAutocompletesWhenCountryIsUnappropriatedToComponentConfiguration();
9425
9523
  }
9426
- }), takeUntilDestroyed(this._destroyRef))
9427
- .subscribe();
9524
+ this.setDefaultCountry();
9525
+ }
9428
9526
  }
9429
9527
  isCountryInputDirty() {
9430
9528
  const countryAutocomplete = this.countryAutocomplete();