Does anyone have a working solution for creating a Material dropdown wrapper (mat-select dropdown) that can be used with formControlName? If so, could you please share a Stackblitz demo of your implementation?
Here are the requirements:
- Should work seamlessly with formControlName within a parent component form that uses formBuilder and its validators. The parent form may contain multiple form fields.
- Must display a red error message if the data entered does not meet the validation criteria set by the parent formBuilder.
- a) Should work with formControlName/patchValue (which should work with the entire class). b) Optionally, should also support inputting data through @Input() SelectedValueId Id number.
I've been attempting to achieve this but haven't had much success yet. Any help in fixing this issue would be greatly appreciated!
In this case, the ID refers to sourceOfAddressId.
export class SourceOfAddressDto implements ISourceOfAddressDto {
sourceOfAddressId: number | undefined; // This ID should be supported
sourceOfAddressCode: string | undefined;
sourceOfAddressDescription: string | undefined;
Typescript:
@Component({
selector: 'app-address-source-dropdown',
templateUrl: './address-source-dropdown.component.html',
styleUrls: ['./address-source-dropdown.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AddressSourceDropdownComponent),
multi: true
}
]
})
export class AddressSourceDropdownComponent implements OnInit, OnChanges {
dataList: any[] = [];
@Input() Label = 'Address Source';
@Input() sourceOfAddressDefaultItem: SourceOfAddressDto = SourceOfAddressDefault;
@Input() selectedSourceOfAddress: any;
@Input() TxtValue = 'sourceOfAddressId';
@Input() TxtField = 'sourceOfAddressDescription';
@Input() Disabled: boolean;
@Input() valuesToExclude: number[] = [];
@Input() Hint = '';
@Input() styles: string;
@Input() defaultSourceOfAddressCode: any;
@Output() addressSourceChange = new EventEmitter<any>();
private _selectedValueId: number;
@Input() set selectedValueId(value: number) {
this._selectedValueId = value;
let outputData: any;
if (this.selectedValueId == this.sourceOfAddressDefaultItem[this.TxtValue]) {
outputData = null;
} else {
outputData = this.dataList.find(x => x[this.TxtValue] == this.selectedValueId);
}
this.onChange(outputData);
}
get selectedValueId(): any {
return this._selectedValueId;
}
@Input() errors: any = null;
disabled: boolean;
control: FormControl;
writeValue(value: any) {
this.selectedValueId = value ? value : '';
}
onChange = (_: any) => { };
onTouched: any = () => { };
registerOnChange(fn: any) { this.onChange = fn; }
registerOnTouched(fn: any) { this.onTouched = fn; }
setDisabledState(isDisabled) { this.disabled = isDisabled; }
constructor(
public injector: Injector,
private AddressService: AddressServiceProxy,
) { }
ngOnInit() {
this.loadDataList();
}
ngOnChanges() { }
loadDataList() {
this.AddressService.getSourceOfAddressAll().subscribe(res => {
this.dataList = res.body.filter(q => q.sourceOfAddressId !== -1);
});
}
}
HTML:
<div class="dropdown-cont">
<mat-form-field appearance="outline">
<mat-label>{{Label}}</mat-label>
<mat-select
disableOptionCentering
[disabled]="Disabled"
[ngStyle]="styles"
(ngModelChange)="selectedValueId=$event"
required>
<mat-option [value]="sourceOfAddressDefaultItem[TxtValue]">{{sourceOfAddressDefaultItem[TxtField]}}</mat-option>
<mat-option *ngFor="let item of dataList" [value]="item[TxtValue]">
{{item[TxtField]}}
</mat-option>
</mat-select>
<mat-hint>{{Hint}}</mat-hint>
</mat-form-field>
</div>
- The implementation should ideally handle default values even when there are delays in API responses, ensuring the default value is inserted first into the @Input SelectedValueId field.