@ascentgl/ads-ui 21.67.0 → 21.69.0

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.
@@ -2408,7 +2408,7 @@ class AbstractBaseComponent {
2408
2408
  if (this.originalValidator === null) {
2409
2409
  this.originalValidator = this.valueControl.validator;
2410
2410
  this.originalAsyncValidator = this.valueControl.asyncValidator;
2411
- this.hadRequiredValidator = this.valueControl.hasValidator(Validators.required);
2411
+ this.hadRequiredValidator = this.hasRequiredValidatorCheck(this.valueControl);
2412
2412
  }
2413
2413
  const subscription = this.valueControl.events
2414
2414
  .pipe(filter((event) => event instanceof TouchedChangeEvent))
@@ -2461,7 +2461,7 @@ class AbstractBaseComponent {
2461
2461
  // Only update hadRequiredValidator if we're not in skipValidationWhenEmpty mode
2462
2462
  // When skipValidationWhenEmpty is active, we preserve the original validator state
2463
2463
  if (!this.skipValidationWhenEmpty) {
2464
- this.hadRequiredValidator = this.valueControl.hasValidator(Validators.required);
2464
+ this.hadRequiredValidator = this.hasRequiredValidatorCheck(this.valueControl);
2465
2465
  }
2466
2466
  });
2467
2467
  this.destroyRef.onDestroy(() => {
@@ -2481,7 +2481,7 @@ class AbstractBaseComponent {
2481
2481
  if (this.skipValidationWhenEmpty) {
2482
2482
  return this.hadRequiredValidator;
2483
2483
  }
2484
- return this.valueControl.hasValidator(Validators.required);
2484
+ return this.hasRequiredValidatorCheck(this.valueControl);
2485
2485
  }
2486
2486
  constructor() {
2487
2487
  this._isStaticFooter = inject(ADS_UI_CONFIG, { optional: true })?.staticFormFieldFooter;
@@ -2553,7 +2553,7 @@ class AbstractBaseComponent {
2553
2553
  this.valueControl.setAsyncValidators(this.originalAsyncValidator);
2554
2554
  }
2555
2555
  // Update required flag based on current validators
2556
- this.hadRequiredValidator = this.valueControl.hasValidator(Validators.required);
2556
+ this.hadRequiredValidator = this.hasRequiredValidatorCheck(this.valueControl);
2557
2557
  this.valueControl.updateValueAndValidity({ emitEvent: false });
2558
2558
  this.displayControl.updateValueAndValidity({ emitEvent: false });
2559
2559
  this.syncState();
@@ -2673,6 +2673,27 @@ class AbstractBaseComponent {
2673
2673
  canShowSuccess() {
2674
2674
  return !this.valueControl.errors && this.valueControl.touched && !!this.successMessage;
2675
2675
  }
2676
+ /**
2677
+ * @ignore
2678
+ * Robustly checks whether a control has a required validator.
2679
+ * Angular's `hasValidator(Validators.required)` uses reference equality, which fails
2680
+ * when validators are provided as an array (e.g., `[Validators.required]`) because
2681
+ * Angular composes them into a wrapper function with a different reference.
2682
+ * This method also checks by running the validator against an empty value.
2683
+ */
2684
+ hasRequiredValidatorCheck(control) {
2685
+ // Fast path: direct reference check
2686
+ if (control.hasValidator(Validators.required)) {
2687
+ return true;
2688
+ }
2689
+ // Fallback: run the current validator against an empty value to detect 'required' error
2690
+ const validator = control.validator;
2691
+ if (!validator) {
2692
+ return false;
2693
+ }
2694
+ const result = validator(new FormControl(null));
2695
+ return result !== null && 'required' in result;
2696
+ }
2676
2697
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AbstractBaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
2677
2698
  static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.6", type: AbstractBaseComponent, isStandalone: true, inputs: { skipValidationWhenEmpty: "skipValidationWhenEmpty", control: "control", immediateValidation: "immediateValidation", errorMessages: "errorMessages", hint: "hint", successMessage: "successMessage", id: "id", label: "label", showFooter: "showFooter", width: "width" }, usesOnChanges: true, ngImport: i0 }); }
2678
2699
  }
@@ -4129,6 +4150,18 @@ class AdsSearchDropdownComponent extends AbstractDropdownComponent {
4129
4150
  }, 0);
4130
4151
  }
4131
4152
  }
4153
+ /** @ignore - Detect value changes when setValue is called with emitEvent: false */
4154
+ ngDoCheck() {
4155
+ // Check if valueControl value has changed (handles emitEvent: false cases)
4156
+ const currentValue = this.valueControl?.value;
4157
+ if (currentValue !== this.previousValueControlValue) {
4158
+ this.previousValueControlValue = currentValue;
4159
+ // Only sync if not set internally and component is initialized
4160
+ if (!this.isInternalValueSet && this.valueControl) {
4161
+ this.controlValueToDisplayedControlValue();
4162
+ }
4163
+ }
4164
+ }
4132
4165
  /** @ignore */
4133
4166
  ngOnDestroy() {
4134
4167
  if (this.unsubscribeFromScroll)
@@ -4572,11 +4605,27 @@ class AdsSearchDropdownComponent extends AbstractDropdownComponent {
4572
4605
  */
4573
4606
  const displayedValue = this.valueControl.value
4574
4607
  ? this.getDisplayedValue(this.valueControl.value)
4575
- : this.valueControl.value;
4608
+ : null;
4576
4609
  /**
4577
4610
  * use it to build displayedControl value
4578
4611
  */
4579
- this.displayControl.setValue(displayedValue ? { key: this.valueControl.value, value: displayedValue } : this.valueControl.value, { emitEvent: false });
4612
+ this.displayControl.setValue(displayedValue ? { key: this.valueControl.value, value: displayedValue } : null, { emitEvent: false });
4613
+ // When value is cleared, also clear the input element directly and reset state
4614
+ if (!this.valueControl.value) {
4615
+ this.hasSearched = false;
4616
+ this.previousControlValue = '';
4617
+ // Clear displayed options when value is cleared
4618
+ if (!this.staticOptions) {
4619
+ this.displayedOptions = new Map();
4620
+ this.allOptions = new Map();
4621
+ }
4622
+ // Directly clear the input element to ensure UI reflects the cleared state
4623
+ if (this.input?.nativeElement) {
4624
+ this.input.nativeElement.value = '';
4625
+ }
4626
+ }
4627
+ // Trigger change detection to update UI
4628
+ this.cdr.detectChanges();
4580
4629
  // Trigger textarea resize after DOM updates to avoid ExpressionChangedAfterItHasBeenCheckedError
4581
4630
  if (this.wrapOptionText && this.input?.nativeElement instanceof HTMLTextAreaElement) {
4582
4631
  Promise.resolve().then(() => {