Search code examples
angulartypescriptabp-frameworkangular15

Setting Default Value for Dropdown in ABP Framework with Angular UI Dynamic Forms


I'm working on ABP framework with Angular UI, and I'm currently working dynamic forms, so I have a dropdown as:

const timeZoneProp = new FormProp<IdentityUserDto>({
    type: ePropType.Enum,
    name: 'TimeZoneId',
    displayName: '::TimeZone',
    isExtra: true,
    id: 'TimeZoneId',
    autocomplete: 'off',
    validators: () => [Validators.required],
    options: (data: PropData<IdentityUserDto>): Observable<Option<any>[]> => {
      const service = data.getInjected(TimeZoneService);
      return service
        .getList()
        .pipe(
          map(
            response =>
              response.items?.map(
                item =>
                  ({ key: item.description, value: item.id } as Option<any>)
              ) || []
          )
        );
    }
  });

  propList.addByIndex(timeZoneProp, 6);

This is working as expected; I have a dropdown with data displayed, and now I want to auto-select a value on the dropdown load, so I noticed there is a defaultValue option in the documentation:

  • defaultValue is the initial value the field will have. (default: null)

That default accept multiple type of values, it is declared as:

readonly defaultValue: boolean | number | string | Date;

So I tried to set it via index like defaultValue: 0 or via the key value like defaultValue: '(UTC+00:00) Coordinated Universal Time ETC/GMT', but it does not work, it still showing the options but is not selecting an option on load. So I try something different, I try to set it inside de options definition as:

const timeZoneProp = new FormProp<IdentityUserDto>({
  type: ePropType.Enum,
  name: 'TimeZoneId',
  displayName: '::TimeZone',
  isExtra: true,
  id: 'TimeZoneId',
  autocomplete: 'off',
  validators: () => [Validators.required],
  options: data => {
    const service = data.getInjected(TimeZoneService);
    return service.getList().pipe(
      map(response => {
        const options = response.items?.map(item => ({
          key: item.description,
          value: item.id
        })) || [];

        // Find the item where the key equals "(UTC+00:00) Coordinated Universal Time ETC/GMT"
        const defaultValueItem = options.find(
          item => item.key === '(UTC+00:00) Coordinated Universal Time ETC/GMT'
        );

        // If the defaultValueItem is found, assign its value to defaultValue
        if (defaultValueItem) {
          timeZoneProp.defaultValue = defaultValueItem.value;
        }

        return options;
      })
    );
  }
});

As you can see I'm trying to set in this line:

timeZoneProp.defaultValue = defaultValueItem.value;

But the property is read-only, so it can not be overridden in the options definition.

How can I achieve this?

UPDATE

I think the default value is not displayed because the options are being loaded asynchronously, and the default value is set before the options are available, how can I do a workaround to achieve this commitment?

I try to set defaultValue as a promise like:

const timeZoneProp = new FormProp<IdentityUserDto>({
    type: ePropType.Enum,
    name: 'TimeZoneId',
    displayName: '::TimeZone',
    isExtra: true,
    id: 'TimeZoneId',
    autocomplete: 'off',
    defaultValue: new Promise(resolve => {
      const data = {}; 
      const optionsPromise = this.options(data).toPromise();

      optionsPromise.then(options => {
        if (options.length > 0) {
          resolve(options[0].value);
        } else {
          resolve(null); // Set default value to null if options array is empty
        }
      });
    }).then(
      resolvedValue =>
        resolvedValue as string | number | boolean | Date | undefined
    ), // Cast the resolved value to the appropriate type
    validators: () => [Validators.required],
    options: (data: PropData<IdentityUserDto>): Observable<Option<any>[]> => {
      const service = data.getInjected(TimeZoneService);
      return service
        .getList()
        .pipe(
          map(
            response =>
              response.items?.map(
                item =>
                  ({ key: item.description, value: item.id } as Option<any>)
              ) || []
          )
        );
    }
  });

But this throw error in defaultValue prop

Type 'Promise<string | number | boolean | Date | undefined>' is not assignable to type 'string | number | boolean | Date | undefined'.ts(2322) form-props.d.ts(36, 14): The expected type comes from property 'defaultValue' which is declared here on type '{ validators?: PropCallback<IdentityUserDto, ValidatorFn[]> | undefined; asyncValidators?: PropCallback<IdentityUserDto, AsyncValidatorFn[]> | undefined; ... 15 more ...; name: string; }' (property) defaultValue?: string | number | boolean | Date | undefined

 const optionsPromise = this.options(data).toPromise();

it throws:

this' implicitly has type 'any' because it does not have a type annotation.


Solution

  • The default value must be a value. It is a promise. The default value is not supported the dynamic value (for a now)

    Did you try to (UTC+00:00) Coordinated Universal Time ETC/GMT as defualt value.

    defaultValue:'(UTC+00:00) Coordinated Universal Time ETC/GMT' and set key and value with same value ? maybe convert key to value in backend ?