I made a reusable ngx-mat-select-search component which looks like this
mat-tooltip-select-all.component.ts
export class MatTooltipSelectAllComponent implements OnInit, AfterViewInit, OnDestroy {
@Input() placeHolderName: string;
@Input() isDate: boolean = false;
/** emit an event when a new option is selected */
@Output() newOptionSelected = new EventEmitter<SelectOption[]>();
/** control for the selected option for multi-selection */
public multiCtrl: FormControl<SelectOption[]> = new FormControl<SelectOption[]>([]);
/** control for the MatSelect filter keyword multi-selection */
public multiFilterCtrl: FormControl<string> = new FormControl<string>('');
/** list of selectOptions filtered by search keyword */
public filteredMulti: ReplaySubject<SelectOption[]> = new ReplaySubject<SelectOption[]>(1);
public tooltipMessage = 'Select All / Unselect All';
@ViewChild('multiSelect', { static: true }) multiSelect: MatSelect;
/** Subject that emits when the component has been destroyed. */
protected _onDestroy = new Subject<void>();
@Input() set selectOptions(selectOptions: SelectOption[]) {
this._selectOptions = selectOptions;
// load the initial option list
this.filteredMulti.next(this.selectOptions.slice());
}
get selectOptions(): SelectOption[] {
return this._selectOptions;
}
private _selectOptions: SelectOption[];
constructor() { }
ngOnInit() {
// set initial selection to all
// this.multiCtrl.setValue(this._selectOptions.slice());
// listen for select option value changes
this.multiCtrl.valueChanges
.pipe(takeUntil(this._onDestroy))
.subscribe(() => {
this.newOptionSelected.emit(this.multiCtrl.value);
});
// listen for search field value changes
this.multiFilterCtrl.valueChanges
.pipe(takeUntil(this._onDestroy))
.subscribe(() => {
this.filterMulti();
});
}
I use it multiple times on parent component but for some reason the data seems to be loaded only on the first instance of ngx-mat-select-search. For example
//pdoNames is loaded
<app-mat-tooltip-select-all
#pdoNameFilter
class="col-md-6"
(newOptionSelected)="handleFilter($event, Filter.PDOName)"
[selectOptions]="pdoNames"
placeHolderName="PDO Name"
></app-mat-tooltip-select-all>
//beneficiaryClassifications is not loaded/filtered list is empty
<app-mat-tooltip-select-all
#beneficiaryFilter
class="col-md-6"
(newOptionSelected)="handleFilter($event, Filter.BeneficiaryClassification)"
[selectOptions]="beneficiaryClassifications"
placeHolderName="Beneficiary Classification"
></app-mat-tooltip-select-all>
Data is fetch in parent component like this
parent component ts
ngOnInit(): void {
this.reportService
.getFundingNeedsSummary()
.pipe(take(1))
.subscribe((response: any) => {
// sets pdoNames and beneficiaryClassifications here
});
}
I have to check option length like *ngIf="beneficiaryClassifications?.length > 0" on subsequent instances to make it work. But the problem with this approach is it won't display the component if option length is empty or 0.
How could I make this work without checking option length or make the component display even if option length is 0? I tried using *ngIf="beneficiaryClassifications?.length == 0 || beneficiaryClassifications?.length > 0"
but it won't load data/filter options becomes empty.
Full reproducible code: https://stackblitz.com/edit/github-qartsv?file=src%2Fapp%2Fapp.component.html
We need to update the reference of an array, when we do the push operation, since the contents are changed, but the memory location stays the same.
Angular thinks nothing has changed, hence you are not able to view the list when you only do a push operation.
When we do array destructuring, (this.clientType = [...this.clientType]
) the array has a new memory reference, so angular runs change detection on the @Input
and makes the list visible.
populateFilters() {
this.tableData.forEach((pdo) => {
const clientType = {
id: pdo?.client_type ? pdo?.client_type.id : 'null',
name: pdo?.client_type ? pdo?.client_type.name : 'null',
};
if (!this.isExisting(clientType, this.clientType)) {
this.clientType.push(clientType);
}
});
this.clientType = [...this.clientType]; // <- changed here!
}