简体   繁体   中英

Angular: Correct approach for rendering server side validation messages

Let's say I have this form in Angular:

<form [formGroup]="form">
    <md-input-container>
        <input mdInput formControlName="name" [(ngModel)]="dummy.name" name="name">
        <md-error *ngIf="form.controls.name.hasError('required')">This is required</md-error>
    </md-input-container>
    <md-input-container>
        <input mdInput formControlName="email" [(ngModel)]="dummy.email" name="email">
        <md-error *ngIf="form.controls.email.hasError('invalid_format')">The email is not valid</md-error>
    </md-input-container>
</form>

Which is submitted to a web service for saving, updating, etc. The web service of course has to check that the inputs are correct and return some validation messages in case there is any problem. Let's say I POST the form and get these validation errors in a JSON dictionary:

{
    "name": [
        "Middle names are not allowed."
    ],
    "email": [
        "The email already exists."
    ]
}

It's unclear to me how angular should process this situation. Most of the information around suggest to write a custom validator that would do a asynchronous validation against the server, for example:

uniqueEmailValidator(control: Control): {[key: string]: any} {
    return new Promise (resolve => {
        let headers = new Headers();
        headers.append('Content-Type', 'application/json');
        this.http.get('http://webservice.com/email/unique/', {headers:headers})
            .map(res => res.json())
            .subscribe(data => {
                if(data != null) {
                    resolve({"duplicate": true})
                }
                else resolve(null);      
            })
        });
    });
}

Now, this means that I should have a specific web service for every single piece of data that might exist in my form. While this might make sense in some specific cases (like unique email/username validation), I don't like the idea of filling my web API with lots of services that will only do validation tasks, plus then having to write the validators in my angular project.

A possible solution I figured out, is to save the server error object in my component as an attribute, then have an Angular validator to check wether a specific field has an error message:

public checkServerErrorMessage(control: FormControl):{[key: string]: boolean}{
    if("name" in this.serverErrors){
        return { serverValidationError: true };
    }
    return {};
}

Then force the validators to refresh using updateValueAndValidity or some sort of thing. But I don't feel this right. Is there an "angularish" approach to resolve this situation?

Since I have been getting some upvotes, and just in case anyone is struggling through this, here is the solution I made up.

Instead of using validators, I ended up using the setError method to trigger validation errors manually on controls (which in my understanding, is basically what a validator would do).

So, in my component, I will iterate over the server's response to determine if there is an error on each field. I guess you can determine this in many ways and it depends on how your web service was designed. In my case, there is an error if the name of the control is present in the dictionary of errors returned by the server. If so, i flag the matching form control as a 'serverValidationError' (or however you want to call it):

for(var key in serverResponse["errors"]) { 

    this.serverErrors[key] = serverResponse["errors"][key]
    this.form.controls[key].setErrors({serverValidationError: true});

}

"serverErrors" will store the messages from the server to display them on the template.

Then in the template, i check for that error:

<form [formGroup]="form">
    <md-input-container>
        <input mdInput formControlName="name" [(ngModel)]="dummy.name" name="name">
        <md-error *ngIf="form.controls.name.hasError('serverValidationError')">{{ serverErrors["name"] }}</md-error>
    </md-input-container>
    <md-input-container>
        <input mdInput formControlName="email" [(ngModel)]="dummy.email" name="email">
        <md-error *ngIf="form.controls.email.hasError('serverValidationError')">{{ serverErrors["email"] }}</md-error>
    </md-input-container>
</form>

Don't know if this is the best solution, but at least is the best I could think of.

PS: I'm writing all this from memory, so the code is probably not 100% correct.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM