In Angular we have different kind of validators. Client-side and client-side async validators which call a function on the server. Often however, after satisfying all client-side validators, you may want to send the form to the server for validation. In case of an error, the server needs to be able to communicate that back to the client where we want to display it directly on the single form fields again.
In this lesson I'll show you a possible approach of implementing such scenario, returning a 422
status code, processing the response format we specify in the lesson and display it on the Angular form field again
Instructor: [00:00] Here, we see a simple Angular reactive form, which accepts username. You can see it also here on our app component defined as a form group, and here a form control which accepts that username property.
[00:11] Similarly, as an app component, we have here a material form. We have the normal form tag bind in the form group, and then here for our form input field that binds it as username form control.
[00:23] As you can see here, I have also added a validator. In this case is the required validator and whenever here I don't submit any kind of username, I get here an error message and I get also the message here just below the field that indicates to the user what happened.
[00:39] An error message happens here in the submit event. Whenever the form which I get is not valid, I'm getting this error here.
[00:48] This is a very simple client-side validator. What I would like to do is to allow to send a form to the server, the server does its evaluations, and then sends back one or more validation errors which could potentially have happened.
[01:01] What we want to do is not only show that to the user as an error message or a generic error message, but I would like to match that error to the user fields on the form.
[01:12] In order to do so, first of all, we will need to have some space where the server-side validations with be matched onto our fields. What we have to do here is to create a similar validation method, as we did for the required field, but now let's call it, for instance, server error. This is reserved for errors coming back from the server.
[01:32] We don't have any kind of static message here, but rather, the server error itself contains already the message. That's something the server needs to send us back in the response body. Let's save this. The server error validator now is active. What we need to do is to handle the response from the server.
[01:50] You can see, whenever the form is valid, all client-side validators have been satisfied, then I'm here invoking a person service, and when that gets back, I'm logging out in console here. Let's have a look.
[02:02] If I'm just writing it in here as a value, you see here I simulate a submission, and then I get a safe person back, and I visualize it here on the console by printing out this message.
[02:11] The person service here actually just simulates the call. I don't have any kind of HTTP service or backend running here. I'm just simulating here the delay here with a timer, and then I'm sending back here as a map in RxJS Observable what the server would potentially return.
[02:29] In order to simulate an error or a validation error in our case in the server, let's do something like this. Whenever the person.username is different from [inaudible] , which is my handle, then let's say that call is good and everything is fine. Note this logic would be the validation logic happening on the server side. We're just simulating in here.
[02:49] In the error case, we need to simulate an error coming back. This is simulate server error. An important thing right away is what kind of status code do we want to return.
[03:00] It's an error, so it shouldn't be a 200 HTTP status code. It shouldn't either be a 500, because a 500 is something that happens when a server has a problem. There's nothing the user can actually do. It's either a configuration problem or even a coding problem, or the server is just down.
[03:16] What I found quite suits the need is a 422, because that means the data is syntactically correct, but there was a problem that the server couldn't accept it for whatever reason.
[03:27] To simulate that, again, let's use a timer to simulate some delay. Let's use here the pipe. Again, here, we need to use the map operator. Let's also call here a syntactic error just to make sure it runs fine afterwards.
[03:43] Here, I'm throwing an error to activate RxJS failure callback, which would also happen if the server sends back an error. This is actually the object the server should return us.
[03:54] If the server is able to identify a property which was wrong, then what I did usually is create a structure, an object, and make an object response body that has an errors property in there, and then inside here is the property that actually failed to validate.
[04:11] In this case, there could even be the localized error message like the username is already taken. In this case, the server signals back to the client that an error happened. From that point of view, we're done. Again, to remember, this is something the server would do. In a real-world scenario, we would just call an HTTP call to the server. We will get back a 422 call or a success call.
[04:35] Let's go back to our app component. In order to handle this error here, we need to implement here the error callback. Here, again, if it is a real call from the server, what we would do here is if there is an instance of an HTTP error response, then we would check the status. If it is our famous 422, then here, we can handle the validation error.
[05:00] Since I'm not having here an HTTP call, let's just comment this for a minute and implement it straight away. What I will do now is extract the validation errors based on the format that we have just specified before in our person service. Let's actually say here that validation errors = .error.errosobject that contains all of the properties that have a problem.
[05:22] Now, we can iterate over .errorsobject using the object.keys on the validation errors. For each of them, we get here the prop. For each of them, we want to try to map it on our form. I'm using here the [inaudible] form control, and then this.form, which is our form group get and try to match it on that property name.
[05:45] You need to make sure that you got this. It could happen that form control is not present or is not visible, therefore we won't see it, we won't get it here.
[05:55] If we get it, however, we can directly invalidate it that form control by using this setErrors method. In this setErrors method, we can specify the errors which would happen or which should be invalidated, like a require validator or whatever validator.
[06:09] This is exactly the object which we have seen here. If we set now the server error to a value that will be defined and therefor our validator would show up. That's exactly what we're doing here.
[06:22] We can also use the validation errors which contains also the localized text. We set the errors to the text. That text will then be displayed as we have defined before within our validation methods here. Now, we can save it. Let's try this out.
[06:38] If I type in something that is correct, I will get it here printed out. If I don't type in anything, I get username is required. If I type in our handle, which we'll soon be allowed to use, then you see here we get the username has already been taken. This will be the message that comes back from the server response.
[06:57] Note the important point here is also to basically [inaudible] . Whenever the form control has not been found, you should add that message to some messages array and show it via an alert or a notification to the user that you make sure you don't lose any kind of errors just because the form control doesn't have any matching property.