angularprimengngx-formly

Autocomplete not showing suggestions


I'm trying to make primeng autocomplete component work using formly (json) but I'm stuck as suggestions are not displayed: onComplete function is clearly working as results are back from the ws call. I'm sure I'm missing something probably linked to the overlay panel?

Here is a stackblitz: first panel is straight using primeng component, second one is trying to use formly (type PARIS for example or any other french city).

https://stackblitz.com/edit/stackblitz-starters-xk42bm?file=src%2Fformly-types%2Fautocomplete-type.ts

Thanks in advance for your help.


Solution

  • You are assigning f.props['results'] = this.suggestionsCodePostalCommune before the searchCodePostalCommune method is triggered. This will not automatically update the search result to f.props['results'].

    You should update for each time the search result is returned as below:

    searchCodePostalCommune(event: AutoCompleteCompleteEvent) {
      this.adresseService
        .searchCodePostalCommune(event.query)
        .pipe(takeUntilDestroyed(this.destroyRef))
        .subscribe((s) => {
          let codepostalOuCommune = this.fields[0].fieldGroup!.find((x) =>
            x.key?.toString().includes('codepostalOuCommune')
          );
          if (codepostalOuCommune && codepostalOuCommune?.props)
            codepostalOuCommune.props['results'] = s.features;
    
          this.suggestionsCodePostalCommune = s.features;
        });
    }
    

    Besides, this solves the option drop-down list not showing. You have another issue that the displayed option is blank due to the field="label", there is no label field/property in the objects from props.results.

    If you want to achieve the same displayed options as your first panel with PrimeNG, you can pass the template:

    1. Define the featureItemTemplate for the displayed option template.
    <ng-template #featureItemTemplate let-feature>
      <div class="flex align-items-center gap-2 font-semibold">
        {{ feature.properties.postcode }} -
        {{ feature.properties.label }}
      </div>
      <div class="flex align-items-center gap-2 text-xs">
        {{ feature.properties.context }}
      </div>
    </ng-template>
    
    1. Modify AutocompleteProps to allow passing the itemTemplate value.
    interface AutocompleteProps extends FormlyFieldProps {
      results: unknown[];
      searchObject(event: { query: string }): void;
      itemTemplate: TemplateRef<any>;
    }
    
    1. Get featureItemTemplate from the view in the component with @ViewChild. You need to migrate the mapFields caller to ngAfterViewInit as the template is only after to obtain during the AfterViewInit lifecycle.
    import {
      Component,
      DestroyRef,
      inject,
      TemplateRef,
      ViewChild,
    } from '@angular/core';
    
    @ViewChild('featureItemTemplate', { static: true })
      featureItemTemplate!: TemplateRef<any>;
    
    ngAfterViewInit() {
      this.fields = this.mapFields(this.formlyJson);
    }
    
    1. Assign featureItemTemplate to f.props['itemTemplate'].
    mapFields(fields: FormlyFieldConfig[]) {
      return fields.map((f) => {
        if (f.key?.toString().includes('codepostalOuCommune')) {
          if (f.props) {
            f.props['searchObject'] = (event: AutoCompleteCompleteEvent) =>
              this.searchCodePostalCommune(event);
            f.props['results'] = this.suggestionsCodePostalCommune;
            f.props['itemTemplate'] = this.featureItemTemplate;
          }
        }
    
        if (f.fieldGroup && f.fieldGroup.length > 0) {
          this.mapFields(f.fieldGroup);
        }
    
        return f;
      });
    }
    
    1. Render passed props.itemTemplate in the pTemplate="item". Don't forget to import CommonModule or NgTemplateOutlet into the imports array.
    @Component({
      selector: 'formly-field-primeng-autocomplete',
      standalone: true,
      imports: [
        CommonModule,
        ...,
        NgTemplateOutlet, // No need to import if imported CommonModule
      ],
      ...
    })
    export class FormlyFieldAutocomplete extends FieldType<
      FieldTypeConfig<AutocompleteProps>
    > {}
    
    <p-autoComplete
      field="label"    
      [formControl]="formControl"
      [formlyAttributes]="field"
      [forceSelection]="true"
      [suggestions]="props.results"
      [style]="{ width: '100%' }"
      [inputStyle]="{ width: '100%' }"
      (completeMethod)="props.searchObject($event)">
        <ng-template pTemplate="item" let-feature>
    
          <ng-container *ngTemplateOutlet="props.itemTemplate; context: { $implicit: feature }">
          </ng-container>
        </ng-template>
    </p-autoComplete>
    

    Demo @ StackBlitz