I am trying to create a Material dropdown wrapper, (mat-select dropdown), which will work with formControlName. Can someone post their Stackblitz if they have on their library? Feel free to start from scratch, and create own answer, whatever meets requirements.
Requirements:
1) Needs to work with formControlName. We have parent component form with formBuilder/and its validators which is trying to refer to this child wrapper. The Parent Component formbuilder has many other form fields also, as typical scenarios.
2) Needs to show error red invalid, if data does not meet requirements of parent FormBuilder validators.
3) a) Needs to work not only with formControlName/patchValue (patchValue should work with whole class); b) optionally also if someone places data into @Input() SelectedValueId Id number. can work with the two
Attempting to get this working, but not successful yet, Does anyone have any code to fix this?
Need working stackblitz, for successful answer,
In this case, Id is sourceOfAddressId
export class SourceOfAddressDto implements ISourceOfAddressDto {
sourceOfAddressId: number | undefined; // should work with this Id
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>
- also hopefully works with default values, even if API is lagging sometimes, and default value is inserted first in @Input SelectedValueId