Check out this functional stackblitz example: https://stackblitz.com/edit/angular-13-starter-x-z434ak?file=src/app/app.component.ts
The main issues can be summarized as follows:
- Ensure that you are wrapping your form within a formGroup properly.
- Double-check that the compareWith function is correctly comparing the necessary fields.
I made a change where the manufacturer field now holds the entire object. Upon closer inspection, there seems to be a discrepancy between the objects being compared. This mismatch might be caused by variations in what you're assigning as your source data and how each item is represented internally.
https://i.sstatic.net/fPRZ6.png
If you prefer to directly use the name for comparison, your compareWith function should resemble this:
compareFunction(o1: any, o2: any) {
console.log('o1 is: ', o1, o2);
return o1.name == o2;
}
Given that o1 represents your manufacturer object and o2 is just the string value of the manufacturer's name, it appears that the issue stems from differing object types with overlapping characteristics but not identical structures. It's crucial for both your data source and individual items to mirror each other.
Here's another stack blitz link demonstrating a straightforward example of using compareWith effectively: https://stackblitz.com/edit/angular-material-select-compare-with?embed=1&file=app/app.html
Edit: In case Stackblitz loads slowly or access is lost due to non-registration with Github during edits:
HTML:
<div class="details align-items-center d-flex" [formGroup]="updateEstimation">
<mat-form-field appearance="fill" class="nusx">
<mat-select formControlName="manufacturer" [compareWith]="compareFunction" #multiSelect>
<mat-option>
<ngx-mat-select-search
formControlName="manufacturerMultiFilterCtrl"
[placeholderLabel]="'search...'"
[noEntriesFoundLabel]="'not found'"
>
</ngx-mat-select-search>
</mat-option>
<mat-option
*ngFor="let item of filteredManufacturers | async"
[value]="item"
>{{ item.name }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
TS:
import { Component, ViewChild, OnInit } from '@angular/core';
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
import { MatSelect } from '@angular/material/select';
import { ReplaySubject, Subject, take, takeUntil } from 'rxjs';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
manuf: any;
carDetail: any;
updateEstimation: FormGroup;
protected _onDestroy = new Subject<void>();
public filteredManufacturers: ReplaySubject<any[]> = new ReplaySubject<any[]>(
1
);
set data(data: any[]) {
this.manuf = data;
// load the initial list
this.filteredManufacturers.next(this.data.slice());
}
constructor(private fb: FormBuilder) {}
@ViewChild('multiSelect', { static: true }) multiSelect: MatSelect;
ngOnInit() {
//chosen data
this.carDetail = {
carId: 3,
registrationNumber: 'yyyyyyyy',
manufacturer: 'manuf',
manufacturerId: 1,
carModel: 'carmod',
carModelId: 1,
year: 2021,
vincode: 'yyyyyyyy',
};
console.log('overall data', this.filteredManufacturers);
console.log('manufacturer oninit: ', this.carDetail.manufacturer);
this.updateEstimation = this.fb.group({
manufacturer: [this.carDetail],
manufacturerMultiFilterCtrl: [this.carDetail.manufacturer],
});
//all manufacturers
this.manuf = [
{
manufacturerId: 1,
name: 'manuf',
carModels: [
{
carModelId: 1,
name: 'carmod',
},
{
carModelId: 2,
name: 'Carmod1',
},
],
},
{
manufacturerId: 2,
name: 'manuf1',
carModels: [
{
carModelId: 3,
name: 'Carmod2',
},
],
},
];
this.filteredManufacturers.next(this.manuf);
}
compareFunction(o1: any, o2: any) {
console.log('o1 is: ', o1, o2);
return o1.name == o2.manufacturer && o1.manufacturerId == o2.manufacturerId;
}
}