This is a proposal.
An Autocomplete component being capable of returning an individual property value (like id
) from a selected object, if specified. Otherwise, return the entire selected object as usual.
Return the selected object as the value for an Autocomplete component.
A common use case for autocomplete in forms is for combo boxes with key/value pairs, often stored in an object.
Currently, the Autocomplete component is not capable of returning a selected object's property as the form field value (id
).
This is cumbersome when using FormGroup.value
to instantiate a new object, because additional "id
extraction" has to be implemented outside the component. If the autocomplete component is meant to be reusable in various forms, then the post-processing has to be implemented in multiple locations, violating the DRY principle. It also requires knowing details about the selected object inside the component, which messes with clean encapsulation.
matAutocomplete::displayWith
property designates a function that computes the object property for the field to display. matAutocomplete::returnWith
property (or some other name) that designates a function to compute the return value of the form field. returnWith
isn't specified, then go about business as usual (returning the whole object).Something like this:
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn" [returnWith]="returnFn">
<mat-option *ngFor="let option of filteredOptions | async" [value]="option">
{{ option.name }}
</mat-option>
</mat-autocomplete>
export class User {
constructor(public id: number, public name: string) { }
}
...
export class AutocompleteDisplayExample {
...
displayFn(user: User): string {
return user ? user.name : user;
}
returnFn(user: User): number | undefined {
return user ? user.id : undefined;
}
}
When used, myFormGroup.value.user
would be equal to the id
of the selected user.
Related:
@HuntedCodes maybe I'm missing something, but why not assign the id to the option's value
?
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn" [returnWith]="returnFn">
<mat-option *ngFor="let option of filteredOptions | async" [value]="option.id">
{{ option.name }}
</mat-option>
</mat-autocomplete>
@HuntedCodes
I believe that it is because if you assign the id to the option's value, after the user selects an option, the displayed function would only receive the ID and not the whole object anymore:
See here:
https://stackblitz.com/edit/angular-qqcvcf?file=app%2Fautocomplete-simple-example.html
@hobojoe That's what I discovered too. The Autocomplete component understands objects internally.
An option gets pre-selected during initialization by passing an object, and when new options are
selected, objects are returned.
The goal was to return the ID of a selected object from the autocomplete FormControl
,
directly assigning it to a field on a related object.
The object hierarchy goes:
--> FormControl
--> CustomAutocomplete
--> MatAutocomplete
To keep things encapsulated, Object.defineProperty()
was used to monkey
patch the parent FormGroup.value
instance from CustomAutocomplete
. This was a proof of concept that worked:
/* Override get() for FormGroup.value. Run from FormGroup context.
* If the target FormControl value is an object that is
* set by matAutocomplete, then return the `id` property.
*/
Object.defineProperty(formGroup, 'value', {
get() {
let fgValues = {};
for (let controlName in this.controls) {
// Collect original control values.
fgValues[controlName] = this.controls[controlName].value;
// Override target control's value with `id` attribute.
if (controlName === 'targetControlName' && fgValues[controlName]) {
const targetObject = fgValues[controlName];
const hasId = targetObject.hasOwnProperty('id');
fgValues[controlName] = hasId ? fgValues[controlName].id : fgValues[controlName];
}
}
return fgValues;
}
});
While not supported, it satisfies my requirements, so I'll go ahead and close the issue. Thanks!
I have the same issue, the solution seems like a hack, the behavior is weird, auto complete should just select/show the name and return the value(key). why does it return the text inside ?
Why is this closed ???????????
This proposal is good and should be implemented, i also need this...
Please re-open
+1
We use a list of value/view object to distinct the internal value from the label show to the user.
But with reactive form and the actual behavior it's endend up with weird results :
https://stackblitz.com/edit/issue-9293
[displayWith]
)The [returnWith]
could be a good solution to me.
Or a more natural way ? If it's technically possible...
<mat-autocomplete autoActiveFirstOption #auto1="matAutocomplete">
<mat-option *ngFor="let so of speciesOptions | async" [value]="so.value">
{{ so.view }}
</mat-option>
</mat-autocomplete>
If the [value]
is not an object, no need to use [displayWith]
.
And if the {{ so.view }}
is it not an object it will be implicitly used in the input.
cc @crisbeto
@WizardPC agree that this is confusing with reactive forms. There either needs to be documentation that you can't access the array in the option loop and here's the workaround, or as mentioned [value] allows the ability to assign an id instead of an object. Otherwise, updating the formControl is cumbersome when you have it's form.value coming in from above.
Example for reactive form with redux input (resolved array via Input()... used a const as a proxy) instead of an observable) here: https://stackblitz.com/edit/angular-autocomplete-array-with-formcontrol-id?file=app%2Fautocomplete-display-example.html
All the typical use cases break as you can either get an object or an id, but not both simultaneously without reaching to get the original array in what seems to be an unnaturally complicated way.
@goelinsights - thanks for sharing that plunker. I modified it a bit to get it working with [value] assigned to the id (not the whole object) as I agree that is more natural given that later you're just trying to store that same ID to the DB, and given that storing the whole object makes no sense for relational data.
Here's the revised code..hope it helps someone!
https://stackblitz.com/edit/angular-autocomplete-array-with-formcontrol-id-1
@goelinsights thanks for the example, i modified it a bit.
I used a different approach, my solution is a bit hacky and not clean but it works.
I used a proxy element as a NgModel of the input.
<input type="text" [(ngModel)]="proxyValue" matInput [formControl]="myControl" [matAutocomplete]="auto">
and added optionSelected method on the autocomplete
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="onSelectionChanged($event)" >
<mat-option *ngFor="let option of filteredOptions" [value]="option">
{{ option.name }}
</mat-option>
</mat-autocomplete>
This element changes with selection of a option.
proxyValue: any;
onSelectionChanged(event$)聽{
this.proxyValue = event$.option.value.name;
}
https://stackblitz.com/edit/angular-autocomplete-array-with-formcontrol-id-4rffm3?file=app/autocomplete-display-example.ts
Inside the onSelectionChanged method you can change the formGroup value if you need.
+1
We would like to see this feature to be implemented
Any update on this matter?
+1 this needs to be reopened, all existing solutions are hacky.
+1 the way i do this so far is searching in the list of object for the value property =S, but in a list with +1K of object it does not work well
+1 same as Sandy
+1 and about re-open ? The example at documentation is to simple and does not reflect the actual cases.
I'm curious as to why there's still an interest in reviving this request, when there are solutions already, and furthermore the [returnWith] is a step back imho.
Considerations:
That said, if anyone has a good use case / need that isn't being addressed I'd suggest opening a new ticket with a full explanation, if you want it to get any traction.
I posted a decent solution and explanation here #4863, towards the bottom. I would prefer a cleaner api or for the default behavior to emulate the mat-select
behavior, but this works for now. If you just want to click the stackblitz
This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
_This action has been performed automatically by a bot._
Most helpful comment
Why is this closed ???????????
This proposal is good and should be implemented, i also need this...
Please re-open