Error handling with Angular 6 and ngx-formly

You are here:
< Back

ngx-formly provides a simple and better way of creating forms in Angular 6 but the error handling part in its official document is outdated. This post will summarize the error handling tips with ngx-formly.

In this post we assume a form field configuration like this:

fields: FormlyFieldConfig[] = [
    {
        key: 'name',
        type: 'input',
        templateOptions: {
            type: 'text',
            label: 'Name',
        },
    }
];

The package versions we are using are:

  • @angular/core@^6.0.3
  • @ngx-formly/core@^4.7.2, @ngx-formly/material@^4.7.2
  • @angular/material@^6.4.1

Front-End Validation

ngx-formly has built-in front-end validation and is quite easy to use. Its official document mentioned the solution but it seems outdated and the fields do not match the actual types.

Basically there are two steps to do front-end validation:

  1. Define validators,
  2. Define error messages for validators.

Suppose we want to validate the name should not be longer than 100 characters. First, add a validators config to the field config:

validators: {
    maxlength: ctrl => ctrl.value && ctrl.value <= 100,
},

A validators config is an object in which the key is an arbitrary name and the value is a validation function. The validation function should be:

  • Paramters: (ctrl: FormControl) , the form control being validated is passed
  • Return value: boolean, return whether validation is successful

Next, add a validation config to define the error message when validation fails:

validation: {
    messages: {
        maxlength: 'The max length is 100 characters',
    }
},

The validation.messages is an object that defines the error message for each validator. Its keys should be the validator names defined in validators, and its values are strings as the error messages.

So the final form config should look like this:

fields: FormlyFieldConfig[] = [
    {
        key: 'name',
        type: 'input',
        templateOptions: {
            type: 'text',
            label: 'Name',
        },
        validators: {
            maxlength: ctrl => ctrl.value && ctrl.value <= 100,
        },
        validation: {
            messages: {
                maxlength: 'The max length is 100 characters',
            }
        },
    }
];

One special, built-in validator is required. It should be set in templateOptions as required: true, and requires a required: 'errmsg' in validation.messages object. Thus, to make the above example required, you should write:

fields: FormlyFieldConfig[] = [
    {
        key: 'name',
        type: 'input',
        templateOptions: {
            type: 'text',
            label: 'Name',
            required: true,
        },
        validators: {
            maxlength: ctrl => ctrl.value && ctrl.value <= 100,
            required: 'This field is required.',
        },
        validation: {
            messages: {
                maxlength: 'The max length is 100 characters',
            }
        },
    }
];

Finally, to disable the submit button when form is invalid, you should use [disabled]="!form.valid" in your submit button (the form is the FormGroup object used by ngx-formly).

Back-End Validation

Not all the field criteria can be validated in front-end and back-end returns some errors in some cases.

Assume the errors returned from back-end have the following format (which is the standard format of django-rest-framework):

{
    "name": [
        "Name contains invalid characters."
    ]
}

Since back-end does not return the error type ( name is field name), we assume all the errors returned by back-end areother type. In front-end, use the following code in your error handler to set the errors to the form:

_.forEach(this.form.controls, (ctrl, name) => {
    if (errors[name]) {
        ctrl.setErrors({ other: errors[name] });
    }
}

Here _ is the lodash library. We iterate all the form controls by calling _.forEach(this.form.controls), then call ctrl.setErrors({ error_type: error_message }) for the form controls that the server returned errors for.

Next, we need to let formly know how to display the error message for other type. Just like what we have done for the maxlength type error, we add another field to validation.messages:

validation: {
    messages: {
        maxlength: 'The max length is 100 characters',
        other: (err, field) => err,
    }
},

In order to display whatever server returned, instead of using a fixed string as in maxlength, we use a function for other type. The function signature is:

  • Parameters: (error_message, error_field). error_message is the message passed by ctrl.setErrors({ error_type: error_message }), and error_field is the reference to the form control.
  • Return value: a string, the actual error message that will be displayed below the field.

Now back-end error messages can be displayed correctly.

Global Error Messages

Until now we only set error messages in each field which could be cumbersome when multiple fields have the same error message configurations. We can move the common error messages into global configurations of formly so that no need to set them field by field.

Add the following code in your app.module.ts (i.e. where the FormlyModule is imported), so that no need to set error messages for each field:

FormlyModule.forRoot({
    validationMessages: [
        { name: 'required', message: 'This field is required' },
        { name: 'other', message: (err, field) => err },
    ],
}),

That’s it!


This post came from my own experience and hopefully it can help anyone who is also using formly and struggling because of lack of document. Clap for me if you like it.


Did not find what you were looking for? Open a ticket with our Help Centre and we will get back to you resolve your issue.Click Here.
Was this article helpful?
How can we improve this article?
Need help?