Server-side data validation is necessarily still a thing with Single Page Applications. While rendering JSON validation errors in the backend is ridiculously simple with Rails, I’ll show you how you can handle these responses on the client-side and integrate them in your Angular 2 forms.

Rendering server-side validation errors
In my previous article about Rails 5 and its new API mode, I’ve briefly mentioned rendering of server-side validation errors in a JSON format. All it takes is the rendering of the model’s error details, preferably with a HTTP status code 422 (Unprocessable Entity):
def create category = Category.new(category_params) if category.save render json: category else render json: category.errors.details, status: :unprocessable_entity end end
Rails then returns a JSON response with the invalid attributes as keys and an array of errors and additional parameters as values:
{"name":[{"error":"taken","value":"fruits"}]}
For validation errors not concerning a specific attribute, Rails uses the key base.
You can obviously render validation errors with any other backend technology in a similar fashion.
An example
Let’s assume we have a web application with a form to create categories. A category has an attribute „name“ with the following constraints:
- required
- maximum length of 10 characters
- unique
While the required and maximum length constraints can (and should) already be validated on the client (in addition to the server), the unique validation can only be performed on the server. In case of a unique error, it is ideally presented to the users in the same way the other client-side errors are. With our example we will eventually achieve this behavior:
The complete source code of the example is available on Github. Checkout the README file for the instructions on how to setup the frontend and backend.
Building a reactive form with Angular 2
First, you need to have a working Angular 2 form that allows to do client-side validations. In this example we are using the reactive forms approach.
Form template
Here is how the template may look like:
Notice these things:
- the
tag binds to the
formGroup
directive - the
formControlName
on the form fields correlates them with the Angular form controls
Futhermore a
component is used to render generic errors (i.e. with key base) and
components are used to render field-specific errors. Typically these two kinds of errors are styled differently.
Form class
In the form component class, the FormGroup
instance is created using the FormBuilder
and the client-side validations are defined:
form: FormGroup; constructor(fb: FormBuilder) { this.form = fb.group({ name: ['', Validators.compose([ Validators.required, Validators.maxLength(10)]) ], description: [''] }); }
In the onSubmit()
function, the form’s values are only submitted if it is valid:
onSubmit() { this.submitted = true; if (this.form.valid) { this.restService.create(this.form.value) .subscribe( entry => this.handleSubmitSuccess(entry), error => this.handleSubmitError(error) ); } }
There are also two functions used in the template to fetch the generic or field-specific errors:
formErrors(): FormError[] { if (this.submitted && this.form.errors) { return this.getErrors(this.form); } } fieldErrors(name: string): FormError[] { let control = this.findFieldControl(name); if (control && (control.touched || this.submitted) && control.errors) { return this.getErrors(control); } else { return undefined; } }
In case of client-side validation errors, the findFieldControl()
function basically does a this.form.get(field)
to get the form control in question.
Integrating server-side validation errors
To integrate server-side validation errors, .setErrors() is called on the corresponding form control to use the same infrastructure for the handling and rendering of the form errors.
Since the validation error responses all have status code 422, they can be handled in the this.handleSubmitError(error)
function that is used as the Observable’s onError
callback in onSubmit()
.
protected handleSubmitError(error: any) { if (error.status === 422) { const data = error.json(); const fields = Object.keys(data || {}); fields.forEach((field) => { const control = this.findFieldControl(field); const errors = this.fetchFieldErrors(data, field); control.setErrors(errors); }); } }
With the fetchFieldErrors()
helper, the validation error from the server response in the format [{ error:
is converted to the format for the form control: {
The other helper, findFieldControl()
, returns the form control with the given name, but also falls back to the form group instance if it is a base error or returns the from controls of nested form groups.
You may study the implementation of these functions for more details.
Error presentation and i18n
Earlier, I’ve mentioned the
and
components that are used to render the errors. The
component translates and outputs all errors of a certain form control:
{{ 'errors.messages.' + e.error | translate:e.params }}
In case of Rails, you may get the possible error keys and their translations from the rails-i18n project’s locale files and add them to the ng2-translate locale file. Translations for the Angular validations also have to be added (be aware that the same keys are used for both kinds of errors, so appropriate translations must be chosen).
Conclusion
From a user’s perspective the presentation of server-side validation errors in the same way as client-side ones is a real benefit. Angular 2 gives us good instruments to realize this, although there is still quite some work required.
The whole validation logic can easily be put in a generic form component from which concrete model form components may inherit using component inheritance.
Image credit: „Trespass Guard“ by Mathis Hofer, 2008, CC BY-SA 3.0