Angular Knowledge (4) : Forms

  • Angular’s two forms apis
    • Summary of 1.1.
  • 2. Reactive forms
    • 2.1. How to build a responsive form data model
    • 2.1.1. FormControl
    • 2.1.2. FormGroup
    • 2.1.3. FormArray
    • 2.2. Instructions for reactive forms
    • 2.3. Characteristics of reactive forms
    • 2.4. Examples of reactive forms
    • Use against 2.4.1.FormGroupWrite the form and get the submission
    • 2.4.2. FormArrayBinding dynamic forms
    • 2.5. Can be used aloneformControlinstruction
    • 2.6. FormBuilder
  • 3. Template forms
    • Summary of 3.1.
    • 3.2. Template form pairsHandling of elements
    • 3.3. A template form invokes an instance of a form object
    • 3.4. Submission of template forms
    • 3.5. Template form bindingngModel
  • 4. Reactive form verification
    • 4.1. Angular validator
    • 4.2. Use of validators
    • 4.3. Acquisition of validator information
    • 4.4. Implement a customized validator
    • 4.5. Display verification information in the template
    • 4.6. Write verification failure information in the validator
    • 4.7. Asynchronous validator
    • 4.8. Status field of the validator
    • 4.8.1. touched/untouched
    • 4.8.2. pristine/dirty
    • 4.8.3. pending
    • 4.9. Class attribute with state
  • 5. Template form verification
    • 5.1. Angular built-in validation directives
    • 5.2. Custom instructions
    • 5.2.1. Instruction validator
      • 5.2.1.1. Use of instruction validators
    • 5.3. Obtain the verification result
    • 5.4. Error messages
    • 5.5. Status field
  • 6. Reference materials

Angular’s two forms apis

Summary of 1.1.

Angular has two forms apis: template and reactive forms. Template-based forms manipulate forms in templates via Angular directives. Limited by HTML syntax, template-based forms are only suitable for simple forms scenarios. Reactive forms are controlled by a custom data model in the component. Therefore, it is more suitable for complex forms functions.

Angular forms require a data model to receive the data stored in the form. This data model is not a custom type, but consists of Angular specific types FormControl, FormGroup, FormArray, and so on.

The data model in the template form was generated by Angular in HTML using Angular directives. Reactive forms, on the other hand, require customization in the component, so they are more flexible.

Template forms exist inFormModuleIn the module, reactive forms exist inReactiveFormModuleModule, be sure to import the corresponding module when usingapp.module.tsRoot module.

2. Reactive forms

2.1. How to build a responsive form data model

To create a reactive table, you first create the data model needed by the form, and then “map” the data model to the template using instructions.

To use reactive forms, import the ReactiveFormsModule module into the app.module.ts module:


import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
  ...
  imports: [
    ...
    ReactiveFormsModule
  ],
  ...
})
export class AppModule { }Copy the code

Defining a form’s data model uses the three Angular types FormControl, FormGroup, and FormArray.

2.1.1. FormControl

The FormControl is the smallest unit of the form model, which is equivalent to a property in the data model object. Specifically, the form can hold the value of an input element and other metadata.

Title: FormControl = new FormControl(' default title ');Copy the code
  • FormControlThe constructor can pass in an argument that represents a default value, such as bound toinputElement, is the default input for that element.

2.1.2. FormGroup

A FormGroup semantically represents the whole form, but you can also store a part of the form, and it can have multiple formControls in it, it can have multiple formGroups.


regist: FormGroup = new FormGroup({
    account: new FormControl(),
    password: new FormGroup({
      pwd: new FormControl(),
      repwd: new FormControl()
    })
});Copy the code
  • FormGroupThe constructor needs to pass in an objectFormControlandFormGroup.
  • In the above code, we define a demo registration information form that contains the account number and password as two main pieces of data, but the password is split into twoFormControl, respectively the password and the duplicate password. These two are the same data from the business, so they are included in oneFormGroupIn the now.

2.1.3. FormArray

So FormArray is essentially the same thing as FormGroup, except that there’s a fixed number of formControls in a FormGroup, and you can initialize as many as you want; The number of formControls in FormArray is variable and can be dynamically increased or decreased.

Address: FormArray = new FormArray([new FormControl(' Beijing '), new FormControl(' Shanghai '), new FormControl()]));Copy the code
  • FormArrayThe constructor needs to pass in an array type containing one or more arraysFormControlObject.

Another difference between FormGroup and FormArray is the way in which FormControl is accessed internally:

  • FormGroupConcrete objects can be accessed by the object’s property nameFormControl.
  • FormArrayIt can only be accessed by subscript (index)FormControl.

2.2. Instructions for reactive forms

class Instructions (1) Instructions (2)
FormGroup formGroup formGroupName
FormControl formControl formControlName
FormArray formArrayName
  • The instructions in the instruction (1) column need to be used by attribute binding syntax, and the instructions in the instruction (2) column need not be directly used by attribute binding syntax.

2.3. Characteristics of reactive forms

  • Reactive form instructions are based onformThe beginning, the template form is usedngAt the beginning of.
  • You cannot use template local variables in reactive forms.

2.4. Examples of reactive forms

Use against 2.4.1.FormGroupWrite the form and get the submission

// testComponent ... export class TestComponent implements OnInit { regist: FormGroup = new FormGroup({ account: new FormControl(), password: new FormGroup({ pwd: new FormControl(), repwd: new FormControl() }) }); onSubmit() { console.log(this.regist); }}Copy the code
<form [formGroup]="regist" (submit)="onSubmit()"> <input formControlName="account"> <div formGroupName="password"> Password: < INPUT formControlName=" PWD "> <input formControlName="repwd"> </div> <input type="submit" value=" confirm ">Copy the code
  • [FormGroup]Is defined in the componentFormGroupThe data model is bound to the form.
  • formControlNamewithfromGroupNameRespectively within the data modelFormControlandFormGroupObject bound to the corresponding element.
  • Using the property binding method ([xxx]=ooo), there is no use, this is because of the use of property bindingregistIs a property in the component, but inside the formaccount,password,pwd,repwdIt is a string (property name), so it is assigned directly.

Page output:

  • You can seeFormGroupThis object contains a number of properties and methods that you can use directly if you only care about its form valuevalueProperties.

2.4.2. FormArrayBinding dynamic forms

// testComponent ... export class TestComponent implements OnInit { regist: FormGroup = new FormGroup({ account: new FormControl(), password: new FormGroup({ pwd: new FormControl(), repwd: new FormControl() }), address: new FormArray([ new FormControl(), new FormControl() ]) }); onSubmit() { console.log(this.regist); } addAddress() { const address = this.regist.get('address') as FormArray; address.push(new FormControl()); }}Copy the code
  • We are inregistA new one is added to the data modeladdresstheFormArrayBy default, there are twoFormControlObject, which by default has two empty input fields.
  • addAddress()The function responds to a button on the page to add to the dynamic formFormControl, which is displayed on the page by adding an input box at a time.
  • You can see in theaddAddress()Get from functionaddressThe process is a little strange becauseregistIs aFormGroupObject, and the models we define are passed in from the constructor, so inFormGroupObject that must be exposed with itget()The function passes in the attribute name to get. And you need to use type conversion to use.
// template <form [formGroup]="regist" (submit)="onSubmit()"> <input formControlName="account"> <div formGroupName="password"> Password: < INPUT formControlName=" PWD "> <input formControlName="repwd"> </div> <br> <div> <ul formArrayName="address"> <li *ngFor="let a of this.regist.get('address').controls; let i=index;" > Shipping address: <input type="text" [formControlName]=" I "> </li> </ul> <button type="button" (click)="addAddress()"> < form> </form>Copy the code
  • Bind on the input element of the shipping addressformControlNameYou can see the way property binding is used, and there may be some questions here: it doesn’t say containNameDo directives at the end require no property binding? It depends on how it’s used, and this isFormArrayIn particular, it cannot access the member by attribute name, only by subscript, so here we pass toformControlNameThe value of theiIt is not a string (property name), but an actual variable, so it needs to be passed by property binding.

Input page effect:

  • We added two input fields of shipping address, and then entered the content in three of them, and filled in the other content, click Submit, you can see the console output result:addressThe value of is an array, and the last unfilled value is NULL.

2.5. Can be used aloneformControlinstruction

The fromControl directive is special. It cannot appear inside a formGroup, or else it will report an error:

// Component formModel: FormGroup = new FormGroup({youName: new FormControl(' your name ')}); UserNamr: FormControl = new FormControl(' name '); // template <form [formGroup]="formModel"> <input formControlName="youName"> <input [formControl]="userName"> </form>Copy the code

You can see if[formControl]Appear in the[formGroup]An error is raised inside:

Put [formControl] outside of [formGroup] and there will be no errors. As mentioned earlier in the data Binding article, interpolation bindings are one-way binding, so it is obvious that [formControl] can create a data and component association that requires two-way binding.

2.6. FormBuilder

Angualr also provides a FormBuilder object to simplify the creation of the data model. You can modify the previous code to FormBuilder:

export class TestComponent implements OnInit { constructor(private fb: FormBuilder) {} regist = this.fb.group({account: [' account '], password: this.fb.group({PWD: [' password '], repwd: [' repeat password ']}), address: this. Fb. Array ([' address 1 ', '2' address, '])}); . }Copy the code

The page outputs the same thing:

  • FormBuilderObject has only three methods:
  • These three methods correspond to the objects of the three form models.
  • useFormBuilderThis saves some code and makes it easier to use form validation later.

3. Template forms

Summary of 3.1.

Template forms don’t define the data model in the component. Angular implicitly creates the underlying data model, which corresponds to FormControl and FormGroup:

Template form directive Form model object
NgForm FormGroup
NgModel FormControl
NgModelGroup FormGroup

Template forms depend on the functionality of the FormsModule module, so you need to import this module into the main app.module.ts module first:


import { FormsModule } from '@angular/forms';
@NgModule({
  ...
  imports: [
    FormsModule,
  ],
  ...
})
export class AppModule { }Copy the code

3.2. Template-based form handling of elements

If a

tag appears in an Angular template, it is automatically managed by Angular, and all of its native attributes and events are deactivated.

Another way to display an annotated form is to add an ngForm attribute to any tag to make it an Angular form:


<div ngForm>
    ...
</div>Copy the code

If you don’t want a form element to be taken over by Angular, annotate ngNoForm on the element to indicate that the expression doesn’t need Angular management:


<form ngNoForm>
    ...
</form>Copy the code

3.3. A template form invokes an instance of a form object

There is no explicit FormGroup definition in a template form, so template local variables are needed to get the form’s value or call methods on the form object.

<form #myForm="ngForm" action="/regist" method="POST"> account: <input type="text"> <input type="text"><br> <button type="button" (click)="viewMyForm(myForm)" </button> </form>Copy the code
  • willngFormOn behalf of theFormGroupInstance assignment to a template local variablemyForm.

Write the click event for the view template local variables button in the component:


viewMyForm(obj: any) {
    console.log(obj);
}Copy the code

Output content:

  • You can see that although we entered the content, we did not get the data in the value attribute because we have not yet bound the attribute to the model tag in the form.

So here we can look at reactive formsFormGroupandNgFormMost of the attributes and methods are the same:

3.4. Submission of template forms

When Angular takes over the form, it blocks the native Submit event by default and replaces it with the Angular ngSubmit event:

<form #myForm="ngForm" (ngSubmit)="onSubmit(myform.value)"> <input type="text"><br> <button type="button" (click)="viewMyForm(myForm)" </button> </form>Copy the code
  • Can bengSubmitThe event is bound to a custom method in the component, which is then passed to a template local variable representingNgFormThe value property argument, which is equivalent to passing form-filling data to the component.

3.5. Template form bindingngModel

I just showed you how to use the value property of ngModel to get the content of the form, but I didn’t actually get anything because I didn’t bind any tags to get the data. To retrieve the data of each form label, add the ngModel directive to the corresponding label and specify the value of the name attribute, which corresponds to the attribute name of the model data.

// template <form #myForm="ngForm" (ngSubmit)="onSubmit(myform.value)"> <input #account="ngModel" type="text"><br> <input ngModel name=" PWD "type="text"><br> <input ngModel name="repwd" type="text"><br> </div> <button type="button" (click)="viewMyForm(myForm)"> </button> </form>Copy the code
  • #account="ngModel": Can also be singlengModelIs bound to a template local variable for use in templates.
  • ngModelGroupYou can achieve the same functionality in reactive forms, combining a set ofngModelPut it together.

Click the “View template local variables” button to see the printed data:

4. Reactive form verification

4.1. Angular validator

Both reactive and template form validations use the Anglar validator. The Angular validator is a method that takes an Argument of type AbstractControl, FormControl, FormGroup, and FormArray are instance objects that inherit from this parameter type, that is, they can be passed in to any of the three models and return a value of any object as long as the key of the object is a string.


xxoo(control: AbstractControl): {[key: string]: any} {
  return {xo: true};
}Copy the code

Angular built-in Validators exist in the Validators class, such as validators. required and validators.minLength (). This document can be viewed by Angular’s built-in validator.

4.2. Use of validators

Regist = this.fb.group({// account: [' account ', Validators. Required], account: [' id ', [Validators. Required, Validators. MinLength (8)]], password: this.fb. [' password ', Validators. Required], repwd: [' duplicate password ', Validators. Required]},{validator: [validators.max]}), address: This. Fb. Array ([' address 1 ', '2' address, '], [the Validators. Required])});Copy the code
  • useFormBuilderReactive forms can be validated by substitutingFormControlThe second argument is passed to the arrayFormControlVerify.
  • The validator can be one or more. If it is one, the validator is passed in as an argument. If it is more than one, it must be written as an array.
  • FormGroupThe validator passed to the validator requires an object passed in the second argument, with the property ofvalidator, the parameter is the validator.
  • FormArrayThe validator andFormControlThe same is true when one or more validators are passed in the second argument.

4.3. Acquisition of validator information

You can obtain verification results and verification errors for individual fields:

onSubmit() { const isValid: boolean = this.regist.get('account').valid; Console. log(' account verification result :${isValid} '); const err: any = this.regist.get('account').errors; Console. log(' check error :${json.stringify (err)} '); }Copy the code
  • .validRetrieves the result of the current field, which is a bool value.
  • .errorsGet error information about failed field validation. The result is an object of type (any).

4.4. Implement a customized validator

Create a new TS file in which to write generic validators:

user-info-valids.ts:

import { FormControl, FormGroup, FormArray } from '@angular/forms'; /** * export function emailValidator(control: FormControl): any { const reg = /[\w!#$%&'*+/=?^_`{|}~-]+(? :\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(? :[\w](? :[\w-]*[\w])? \.) +[\w](? :[\w-]*[\w])? /; const valid = reg.test(control.value); return valid ? Null: {email: 'email format is incorrect '}; } @param group FormGroup */ export function passwordEqualValidator(group: FormGroup): any { const pwd: FormControl = group.get('pwd') as FormControl; const repwd: FormControl = group.get('repwd') as FormControl; // Return the result of password comparison const valid: Boolean = pwd.value === repwd.value; return valid ? Null: {equal: 'Two different passwords entered'}; Export function addressValidator(array: FormArray): any {for (let I = 0; i < array.controls.length; i++) { const val = array.controls[i] as FormControl; if (! Val. value) {return {address: 'Address cannot be empty'}; } } return null; }Copy the code

Then call the test.component.ts component:

import { emailValidator, passwordEqualValidator, addressValidator } from '.. /myValid/user-info-valids'; . Constructor (private fb: FormBuilder) {} regist = this.fb. Group ({// account: [' account ', Validators. Required], account: [' username ', emailValidator], password: this.fb. Group ({PWD: [' username ', emailValidator. MinLength (8)], repwd: [' username ', emailValidator], {this.fb. PasswordEqualValidator}), address: this.fb.array([' address1 ', 'address2 ', '], addressValidator)}); */ onSubmit() {const isValid: Boolean = this.regist.valid; Console. log(' the form validates :${isValid} '); const err1: any = this.regist.get('account').errors; Console. log(' check error :${json.stringify (err1)} '); const err2: any = this.regist.get('password').errors; Console. log(' password check error :${json.stringify (err2)} '); const err3: any = this.regist.get('address').errors; Console. log(' address check error :${json.stringify (err3)} '); }Copy the code

Page display:

  • As you can see, the validation result of the entire form is false whenever any element fails;
  • If the validation fails, it will show the return object that we have customized in the validator. Getting this data will help us personalize the validation.

4.5. Display verification information in the template

To display the validation information in a template, you need to get the validation result from the template.

Following the code in the test.component.ts file above, we use the HIDDEN property of HTML in the test.component.html file to simulate the display of error messages:

<form [formGroup]="regist" (submit)="onSubmit()"> Regist. hasError('email','account')"> Account must be a valid email address! </p> <div formGroupName="password"> Password: <input formControlName=" PWD "> <p [hidden]="! Regist.haserror (' minLength ',['password',' PWD '])"> Password must contain at least 8 characters </p> Duplicate password: <input formControlName="repwd"> <p [hidden]="! Regist. hasError('equal','password')"> Two different passwords entered </p> </div> <br> <div> <ul formArrayName="address"> <li *ngFor="let a of this.regist.get('address').controls; let i=index;" <input type="text" [formControlName]=" I "> </li> <p [hidden]="! </p> </ul> <button type="button" (click)="addAddress()"> </button> </div> < form>Copy the code
  • Model objecthasError()The validator () function gets whether the specified validator has an error.
  • inFormControlOn the callhasError()Function returns the key of the object if the verifier fails (Note that this is not the name of the validator, but the name of the key of the object returned when the validator fails), we can see that the key of the object returned when the account verifier fails isemail; The second parameter is for the field currently being validatedformControName.
  • checkFormGroupInside a field when passed inhasError()The second argument to is changed, and you need to pass in an array whose first argument is the current oneformGroupName, the second is the current validation fieldformControlName; Look at the code above, because we marked the field where “password” isValidators.minLength(8)Validator, so it’s inFormGroupInternal check; The two inconsistent passwords belong toFromGroupThe checksum, which can be passed directly to the frontFormControlTwo similar parameters are used for verification.

Take a look at this on the page:

4.6. Write verification failure information in the validator

We can also define it in the validator and bind it directly with interpolation expressions in HTML, which is more flexible:

The user info – valids. Ts:

/** * export function emailValidator(control: FormControl): any { const reg = /[\w!#$%&'*+/=?^_`{|}~-]+(? :\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(? :[\w](? :[\w-]*[\w])? \.) +[\w](? :[\w-]*[\w])? /; const valid = reg.test(control.value); return valid ? Null: {email: {errDesc: 'The email format is not correct! '}}; }Copy the code
  • We’ve changed the value of the property of the object that we return from a checksum failure to an object, and the name of the property of the object can be anything, so I’m going to call iterrDesc, whose value is the error message you want to display.

test.component.html:

<input formControlName="account"> <p [hidden]="! regist.hasError('email','account')"> {{regist.getError('email','account')? .errDesc}} </p>Copy the code
  • We hardcoded the previous one in

    Error messages in the tag have been replaced with interpolation bindings.

  • Here’s a new way to do itgetError()And its parameters are the same as beforehasErrorExactly the same, except that the return value is different,getError()The return value of the validator is defined in the failure of the returned object, and then call the property name of the returned object, you can get the defined error message; To avoid returning an empty object, use?Avoid error reporting.

When you open the page again, you will see that the error message is displayed as we set it in the validator.

4.7. Asynchronous validator

Reactive forms also support an “asynchronous validator” that can be used when a remote server validation is called. Written like a normal validator, it returns a response stream (Observable), about which we’ll see in the next section, “HTTP Services.”

Change the email validator in user-info-valids.ts to an asynchronous validator:

import { of } from 'rxjs'; import { delay } from 'rxjs/operators'; /** * export function emailValidator(control: FormControl): any { const reg = /[\w!#$%&'*+/=?^_`{|}~-]+(? :\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(? :[\w](? :[\w-]*[\w])? \.) +[\w](? :[\w-]*[\w])? /; const valid = reg.test(control.value); return of(valid ? Null: {email: {errDesc: 'The email format is not correct! '} }).pipe(delay(3000)); }Copy the code
  • The above code only changes the return value, returning an observable stream, used for demonstration purposesdelay(3000)3 seconds delay to return results;
  • This is what we’re using hererxjsThe new syntax of version 6, version 5 and version 6 changes are relatively large, okCheck it out hereThe changes. Or usePromise wayCan also.

Viewing on the page is no different from the previous validation, in this case it will wait 3 seconds before displaying an error message.

4.8. Status field of the validator

In the previous error message display section, we saw that the error message appears at the beginning of the page, and that proper validation is usually performed when the user selects it or when the cursor leaves. Angular’s status field retrieves several states of the validation field:

4.8.1. touched/untouched

Untouched: indicates whether an element has a focal point. Touched is true and untouched is false. And vice versa.

Account: <input formControlName="account"> <div [hidden]="regist.get('account').valid||regist.get('account').untouched"> <p [hidden]="! regist.hasError('email','account')"> {{regist.getError('email','account')? .errDesc}} </p> </div>Copy the code
  • useFormFrouptheget()Function to get the validation status of the formvalidandtouched/untouchedField;
  • Here we judge that the validation passes (valid=true) or the user does not click the input box (untouched=true) to hide error messages

4.8.2. pristine/dirty

Pristine (error-form); error-form (error-form); error-form (error-form); error-form (error-form); error-form (error-form); error-form (error-form)

Account: <input formControlName="account"> <div [hidden]="regist.get('account').valid||regist.get('account').pristine"> <p [hidden]="! regist.hasError('email','account')"> {{regist.getError('email','account')? .errDesc}} </p> </div>Copy the code
  • The way it’s called is the same as before, it’s all inFormGrouptheget()Function call;
  • Only when we change the value of the field (which must be the changed value) will validation be triggered and an error message be displayed.

4.8.3. pending

It is used to obtain the status of asynchronous verification. When a field is in the process of asynchronous verification, the value of pending is true. In this case, a verification prompt can be displayed as required to inform the user and obtain better user experience.

Account: <input formControlName="account"> <div [hidden]="regist.get('account').valid||regist.get('account').pristine"> <div [hidden]="! Regist.get ('account').pending"> Checking whether the format of mailbox is correct, please wait... </div> <p [hidden]="! regist.hasError('email','account')"> {{regist.getError('email','account')? .errDesc}} </p> </div>Copy the code
  • We added a “please wait” prompt in the previous validation, which will appear during the asynchronous validation and will be hidden after the asynchronous validation.

4.9. Class attribute with state

Angular forms default to adding or subtracting class attributes on form components such as input fields based on state, and you can customize these class attributes to customize the look and feel of field DOM elements.

  • When we first enter the page without doing anything to the form field element, we can see that the class attribute of the form element has three class attributes by default:
    • ng-untouched: indicates that the style is not in focus. You can set it freely.
    • ng-pristine: indicates that the value of the field has not been modified. You can also use the editing style freely.
    • ng-invalid: indicates that the current validation is invalid because no value has been entered.

  • The class attribute of the field element changes dynamically when we enter a well-formed mailbox in the account field:
    • ng-dirty: indicates that the field has been modified
    • ng-valid: indicates that the field passes the verification
    • ng-touched: indicates that the field has been in focus

  • An additional one is added when asynchronous verification occursng-pendingClass property, will be automatically removed after verification, this can also control the style.

5. Template form verification

Validation of template forms requires special directives. Angular has some built-in validation directives and can also customize them.

Directives are similar to components, except that they do not have templates and can perform the same functions as components.

5.1. Angular built-in validation directives

Presents a built-in digits of the few check instructions required/minlength/maxlength, specific can see in this part of the document.

Template form instructions are easy to use, either custom or built-in, and can be written directly to the HTML element’s tag:

<input ngModel name="account" type="text" Required maxLength ="50">Copy the code

5.2. Custom instructions

Custom directives need to create directives:


ng g directive myDriective/emailValidDriectiveCopy the code

Look at the generated command email-valid-driud-drictive. Ts:


import { Directive } from '@angular/core';

@Directive({
  selector: '[appEmailValidDriective]'
})
export class EmailValidDriectiveDirective {

  constructor() { }

}Copy the code
  • Instruction decoratorselectorThe value is an attribute of the tag:

  <input selectorValue>Copy the code
  • Component decoratorselectorThe value of the tag selector is:

  <selectorValue></selectorValue>Copy the code

5.2.1. Instruction validator

Only an instruction generated cannot be used as a verifier. To realize an instruction that can be used for verification, it is necessary to wrap a common verifier so that the instruction has the function of the wrapped verifier.

We wrapped the previous emailValidator as an instruction:

import { Directive } from '@angular/core'; import { NG_VALIDATORS } from '@angular/forms'; import { emailValidator } from '.. /myValid/user-info-valids'; @Directive({ selector: '[appEmailValidDriective]', providers: [{provide: NG_VALIDATORS, useValue: emailValidator, multi: true}] }) export class EmailValidDriectiveDirective { constructor() { } }Copy the code
  • Add one inside the command decoratorprovidersProperty, passing in an object with three properties inside its value:
    • provideThis property stores a token, usually a constant provided by AngularNG_VALIDATORSRepresents the current command.
    • useValueThis property binds the validator we want to wrap.
    • multi: this field indicates whether it is possible to wrap multiple validators under the same token, since we would be in the same tokenNG_VALIDATORSWrap multiple validators, so true means multiple validators are allowed.
  • There is no need for any implementation inside the instruction, which is just a shell, and the real verification is the internal implementation of the verifier.

Note that to make the custom instruction validator take effect, you need to import the instruction into the root module of app.module.ts. Generally, any instruction generated by ng g drictive name will be automatically imported into the root module, so there is no need for manual import.


import { EmailValidDriectiveDirective } from './myDriective/email-valid-driective.directive';
@NgModule({
  declarations: [
    ...
    EmailValidDriectiveDirective
  ],
  ...
})
export class AppModule { }Copy the code

5.2.1.1. Use of instruction validators

As described earlier, we simply write the selector value of the instruction verifier we wrote on the tag of the element to be checked.

5.3. Obtain the verification result

Since the template form does not write a data model object, it cannot be directly called by the FormGroup object in the component to know whether the form has passed the validation. We can use a template local variable to pass the validation result to a method parameter for use in the component:

/ / template < form # myForm = "ngForm" (ngSubmit) = "onSubmit (myForm. Value, myForm. Valid)" > account: <input ngModel name="account" type="text" required maxlength="50" appEmailValidDriective><br> <div <input ngModel name=" PWD "type="text"><br> repeat password: <input ngModel name="repwd" type="text"><br> </div> <button type="button" (click)="viewMyForm(myForm)"> </button> </form>Copy the code
  • We define a template local variable in the formmyFormBy moving themyFormThe variablevalueandvalidAttribute passed toonSubmitFunction to get form values and validation results from components:

onSubmit(val: any, valid: boolean) {
  console.log(val);
  console.log(valid);
}Copy the code

Page effect:

5.4. Error messages

The hasError function can still be used to retrieve error messages in a template form, with the same arguments as the reactive form, except that it is called with the form attribute of a template local variable:

<input ngModel name="account" type="text" required MaxLength ="50" appEmailValidDriective><br> <div [hidden]="! myForm.form.hasError('email','account')"> {{myForm.form.getError('email','account')? .errDesc}} </div>Copy the code
  • You can see in addition to using template local variablesformCalling ahasError()Function andgetError()The first argument of the two functions is still the key of the object returned by the validator failure, and the second argument is still the name of the field.

5.5. Status field

State fields can also be called using template local variables. The type of state is the same as the types of reactive forms. Let’s take a look at how they are used

Account: <input ngModel name="account" type="text" required maxlength="50" appEmailValidDriective><br> <div [hidden]="myForm.valid||myForm.untouched"> <div [hidden]="! myForm.form.hasError('email','account')"> {{myForm.form.getError('email','account')? .errDesc}} </div> </div>Copy the code

6. Reference materials

www.angular.cn/guide/react… www.angular.cn/guide/forms www.angular.cn/guide/form-… www.joshmorony.com/username-av…