I’m struggling to find solution for several days, without success.
I’m using Angular Formly, Angular Material and Angular 17 for making mat-select as custom type. When I’m using the mat-select control as custom type it throws error:
Error: mat-form-field must contain a MatFormFieldControl.
at getMatFormFieldMissingControlError (form-field.mjs:422:12)
at MatFormField._assertFormFieldControl (form-field.mjs:673:19)
at MatFormField.ngAfterContentChecked (form-field.mjs:581:14)
at callHookInternal (core.mjs:5136:14)
at callHook (core.mjs:5167:9)
at callHooks (core.mjs:5118:17)
at executeInitAndCheckHooks (core.mjs:5068:9)
at refreshView (core.mjs:12787:21)
at detectChangesInView$1 (core.mjs:12970:9)
at detectChangesInViewIfAttached (core.mjs:12933:5)
If I don’t use as custom type I don’t have problems. As I saw, the above error is thrown when angular formly custom type is defined.
I’m having the following structure in my code.
export let AppInjector: Injector;
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
HttpClientModule,
BlockUIModule.forRoot({
delayStart: 100,
delayStop: 0
}),
ToastrModule.forRoot({
timeOut: 10000,
positionClass: 'toast-bottom-right',
closeButton: true,
enableHtml: true
}),
SharedModule,
AppRoutingModule
],
providers: [
provideAnimationsAsync()
],
bootstrap: [AppComponent]
})
export class AppModule {
constructor(private injector: Injector) {
AppInjector = this.injector;
}
}
@NgModule({
declarations: [
],
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
ProvidersModule,
AngularFormlyModule
],
exports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
ProvidersModule,
AngularFormlyModule
]
})
export class SharedModule { }
@NgModule({
declarations: [],
imports: [
MatSlideToggleModule,
MatButtonModule,
MatFormFieldModule,
MatSelectModule,
MatInputModule,
MatTabsModule,
MatMenuModule,
MatToolbarModule,
MatIconModule,
MatPaginatorModule,
MatSortModule,
MatTableModule,
MatDialogModule,
MatDatepickerModule,
MatNativeDateModule,
MatProgressBarModule,
NgApexchartsModule
],
exports: [
MatSlideToggleModule,
MatButtonModule,
MatFormFieldModule,
MatSelectModule,
MatInputModule,
MatTabsModule,
MatMenuModule,
MatToolbarModule,
MatIconModule,
MatPaginatorModule,
MatSortModule,
MatTableModule,
MatDialogModule,
MatDatepickerModule,
MatNativeDateModule,
MatProgressBarModule,
NgApexchartsModule
]
})
export class ProvidersModule { }
@NgModule({
providers: [
],
declarations: [
AngularFormlySelectComponent
],
exports: [
FormlyModule,
FormlyMaterialModule,
FormlyMatDatepickerModule,
FormlyMatToggleModule
],
imports: [
ReactiveFormsModule,
FormlyMaterialModule,
FormlyMatDatepickerModule,
FormlyMatToggleModule,
ProvidersModule,
FormlyModule.forRoot({
types: [
{
name: 'select',
component: AngularFormlySelectComponent,
wrappers: ['form-field']
}
]
})
]
})
export class AngularFormlyModule { }
@Component({
selector: 'app-angular-formly-select',
template: `
<mat-form-field appearance="fill">
<mat-label>{{ props.placeholder }}</mat-label>
<mat-select [formControl]="formControl" [formlyAttributes]="field" (selectionChange)="onSelectionChange($event)">
<mat-option>(...)</mat-option>
@for (item of filter; track item) {
<mat-option [value]="item.value">{{ item.label }}</mat-option>
}
</mat-select>
</mat-form-field>
`
})
export class AngularFormlySelectComponent extends FieldType<FieldTypeConfig> implements OnInit {
filter: any=[];
ngOnInit(): void {
this.filter = this.props?.options ? this.props.options : EMPTY;
}
onSelectionChange(event: any) {
if (this.props.onChange) {
this.props.onChange(this.field, event);
}
}
}
response of the form:
{
"Id": 3,
"Fields": [
{
"key": "SearchText",
"id": "SearchText",
"className": "col-md-3",
"type": "input",
"props": {
"label": "Search",
"placeholder": "Search",
"type": null,
"options": []
},
"validators": {
"validation": [
"novalidation"
]
},
"method": null
},
{
"key": "DateFrom",
"id": "DateFrom",
"className": "col-md-3",
"type": "datepicker",
"props": {
"label": "From Date",
"placeholder": "Insert from Date",
"type": null,
"options": []
},
"validators": {
"validation": [
"novalidation"
]
},
"method": null
},
{
"key": "DateTo",
"id": "DateTo",
"className": "col-md-3",
"type": "datepicker",
"props": {
"label": "To Date",
"placeholder": "Insert to Date",
"type": null,
"options": []
},
"validators": {
"validation": [
"novalidation"
]
},
"method": null
},
{
"key": "ClientId",
"id": "ClientId",
"className": "col-md-3",
"type": "select",
"props": {
"label": "Client",
"placeholder": "Choose client",
"type": null,
"options": [
{
"value": "3",
"label": "Microsoft",
"defaultValue": null
},
{
"value": "1",
"label": "Coca Cola",
"defaultValue": null
},
{
"value": "2",
"label": "Apple",
"defaultValue": null
}
]
},
"validators": {
"validation": [
"novalidation"
]
},
"method": "GetClients"
},
{
"key": "BudgetTypeId",
"id": "BudgetTypeId",
"className": "col-md-3",
"type": "select",
"props": {
"label": "Budget Type",
"placeholder": "Choose Budget Type",
"type": null,
"options": [
{
"value": "2",
"label": "Money (Denars)",
"defaultValue": null
},
{
"value": "3",
"label": "Money (Dollars)",
"defaultValue": null
},
{
"value": "4",
"label": "Money (EURs)",
"defaultValue": null
},
{
"value": "1",
"label": "Hours",
"defaultValue": null
}
]
},
"validators": {
"validation": [
"novalidation"
]
},
"method": "GetBudgetTypes"
},
{
"key": "StatusId",
"id": "StatusId",
"className": "col-md-3",
"type": "select",
"props": {
"label": "Status",
"placeholder": "Choose Status",
"type": null,
"options": [
{
"value": "1",
"label": "Not configured",
"defaultValue": null
},
{
"value": "3",
"label": "Closed",
"defaultValue": null
},
{
"value": "4",
"label": "Overdue",
"defaultValue": null
},
{
"value": "2",
"label": "Active",
"defaultValue": null
}
]
},
"validators": {
"validation": [
"novalidation"
]
},
"method": "GetStatuses"
},
{
"key": "ExceededBudget",
"id": "ExceededBudget",
"className": "col-md-3",
"type": "checkbox",
"props": {
"label": "Projects with exceeded budget",
"placeholder": "Search projects with exceeded budget",
"type": "NULL",
"options": []
},
"validators": {
"validation": [
"novalidation"
]
},
"method": null
},
{
"key": "ExceededBudgetPrediction",
"id": "ExceededBudgetPrediction",
"className": "col-md-3",
"type": "checkbox",
"props": {
"label": "Projects with exceeded budget prediction",
"placeholder": "Search projects with exceeded budget prediction",
"type": "NULL",
"options": []
},
"validators": {
"validation": [
"novalidation"
]
},
"method": null
}
],
"ClientModel": "ProjectSearchModel"
}