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).
Thanks in advance for your help.
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:
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>
AutocompleteProps
to allow passing the itemTemplate
value.interface AutocompleteProps extends FormlyFieldProps {
results: unknown[];
searchObject(event: { query: string }): void;
itemTemplate: TemplateRef<any>;
}
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);
}
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;
});
}
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>