Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Example Analysis of template-driven form of Angular 2 + form

2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly shows you the "Angular 2 + form template-driven form example analysis", the content is easy to understand, well-organized, hope to help you solve your doubts, the following let the editor lead you to study and learn "Angular 2 + form template-driven form sample analysis" this article.

Template-driven form

Template-driven forms are similar to AngularJS's handling of forms, binding instructions (such as ngModel), data values, and behavior constraints (such as require, minlength, etc.) to templates (templates are the template defined in component metadata @ Component), which is where the term template drives comes from. In general, this type of form leaves a lot of work to the template through binding.

Example of template driver

Let's use examples, for example, we have a user registration form, the user name is email, and the information we need to fill in is: address, password and duplicate password. This should be a more common information needed for registration. So the first step is to build a domain model:

/ / src/app/domain/index.tsexport interface User {/ / the new user id is usually automatically generated by the server, so it can be empty, using? Mark id?: string; email: string; password: string; repeat: string; address: Address;} export interface Address {province: string; / / Province city: string; / / City area: string; / / District addr: string; / / detailed address}

Next, let's create a template file, the simplest HTML template, without adding any binding or event handling:

Email address password confirmation province please select province city please select city district and county address to register

The rendered effect looks like this:

Simple Form

Data binding

For template-driven form processing, we first need to introduce FormsModule into the corresponding module, which must not be forgotten.

Import {NgModule} from'@ angular/core';import {CommonModule} from'@ angular/common';import {FormsModule} from "@ angular/forms"; import {TemplateDrivenComponent} from'. / template-driven/template-driven.component' @ NgModule ({imports: [CommonModule, FormsModule], exports: [TemplateDrivenComponent], declarations: [TemplateDrivenComponent]}) export class FormDemoModule {} a necessary step for template-driven form processing is to establish two-way binding of data, so we need to establish a member variable of type User in the component and assign the initial value. / / template-driven.component.ts// omits metadata and imported class library information export class TemplateDrivenComponent implements OnInit {user: User = {email:', password:', repeat:', address: {province:', city:', area:', addr:''}; / / omit other parts}

With such a member variable, we can bind using ngModel in the component template.

Confusing ngModel

We can use three forms of ngModel expressions in Angular: ngModel, [ngModel] and [(ngModel)]. Either way, if you want to use ngModel, you must specify a name property for the control (such as the following input). If you forget to add name, you will probably see an error like this:

ERROR Error: Uncaught (in promise): Error: If ngModel is used within a form tag, either the name attribute must be set or the form control must be defined as' standalone' in ngModelOptions.

NgModel and FormControl

If we are using ngModel without any square brackets and parentheses, this means that we have created an instance of FormControl that will track changes in values, user interactions, validation status, and keeping views and domain objects in sync.

If we put this control in a Form form, ngModel will automatically register the FormControl as a child control of Form. In the following example, we add the ngForm directive to declare that this is a form that Angular recognizes, and ngModel will register as a child control of the form, the child control's name is email, and ngModel will bind the value of the form based on the value of this child control, which is why it is necessary to explicitly declare name.

In fact, when we import FormsModule, all tags are considered to be a NgForm by default, so we do not need to explicitly write the ngForm instruction in the tag.

All of this is invisible now, so you may still be a little confused, so let's "visualize" it, which requires us to refer to the form object, so we use # f = "ngForm" so that we can output some of the features of the form in the template.

... {{f.value | json}}

At this point, if we type sss in email, we can see the form value in the form of JSON in the following figure:

The input value of the control is synchronized to the value of the form

Unidirectional data binding

So next, what's the use of [ngModel]? What if we want to set an initial value for the control? we need to do an one-way binding from the component to the view. What we can do is to set the email property to wang@163.com when initializing User

User: User = {email: 'wang@163.com',.}

And in the template, use [ngModel] = "user.email" for one-way binding, this syntax is actually the same as ordinary property binding, using square brackets to indicate that this is an attribute to be bound, and to the right of the equal sign is the value to be bound (here is user.email). Then we can get the following output that the initial value of email has been bound successfully!

Unidirectional data binding

Bidirectional data binding

But the problem with the above example is that the data binding is one-way, that is, when the input box is entered, the value of our user will not change accordingly. For better illustration, we output the values of user and the form at the same time

User: {{user | json}} form: {{f.value | json}}

At this point, if we change the default email to wang@gmail.com, the value of the form has changed, but the user has not changed.

The value entered affects the form, but does not affect the domain object

If we want the input value to affect the value of our user object in reverse, then we need to use two-way binding, that is, [(ngModel)] needs to play.

The values of the form and the realm object are synchronized

In any case, this [()] expression is really strange. In fact, this expression is a grammatical sugar. As long as we know that the following two writing methods are equivalent, we will clearly understand: with this syntax candy, you don't have to write both data binding and event binding.

What the heck is ngModelGroup?

If we take a closer look at the output above, we will find a problem: there is a nested object address in user, while there are no nested objects in the form. If we want to achieve that the structure in the form is consistent with the structure of the domain object, we have to invite out the ngModelGroup. NgModelGroup creates and binds a FormGroup to the DOM element. What is FormGroup? To put it simply, it is a set of FormControl.

Province Please select province {{province}}

In that case, let's take a look at the output, which is now exactly the same:

The structure of the form and the domain object are exactly the same.

Data verification

The validation of template-driven forms is also mainly handled by templates, and you need to define the validation rules before you see how to use them:

Three required items: email, password, and repeat

The form of email needs to conform to the standard of email.

Password and repeat must be consistent

Of course, in addition to these rules, we also want the submit button to be unavailable when the form is not validated.

Email address password confirmation password registration

There are several built-in supported validators (Validators) in Angular

Required-requires FormControl to have a non-null value

Minlength-requires FormControl to have a minimum length value

Maxlength-requires FormControl to have a value of maximum length

Pattern-values that require FormControl to match regular expressions

If we want to see the result, we can add the following code to the template to output the error as JSON.

Email verification: {{f.controls.email?.errors | json}}

We see that if the email is not filled in, the wrong JSON is {"required": true}, which tells us that there is currently a required rule that has not been met.

Verification result

When we enter the letter w, we will find that the error looks like the following. This is because we have applied multiple rules to email, and when the required items are met, the system will continue to check other validation results.

{"pattern": {"requiredPattern": "^ ([a-zA-Z0-9] + [_ | _ |.]?) * [a-zA-Z0-9] + @ ([a-zA-Z0-9] + [_ | |.]?) * [a-zA-Z0-9] +. [a-zA-Z] {2jue 4} $", "actualValue": "w"}}

After several experiments, we should be able to conclude that when the validation fails, the validator returns an object, key is the verification rule (such as required, minlength, etc.), and value is the verification result. If the validation passes, a null is returned.

Knowing this, we can actually make a hint of an error in validation. For convenience of reference, let's export ngModel to an email reference, and then we can access the various properties of the FormControl: the state of validation (valid/invalid), the state of the control (whether the focus has been obtained-touched/untouched, whether the content has been changed-pristine/dirty, etc.)

Email address email is required email format is incorrect

Custom validation

The built-in validator is not enough for comparing two passwords, so we need to define a validator ourselves. It's easier for responsive forms, but for template-driven forms, we need to implement an instruction to make the validator more generic and consistent. Because the way we want to implement it should be similar to that of required, minlength, etc., such as validateEqual= "repeat" like this

Password confirmation password

So to implement this form of validation, we need to create an instruction that implements the Validator interface. A basic framework is as follows:

Import {Directive, forwardRef} from'@ angular/core';import {NG_VALIDATORS, Validator, AbstractControl} from'@ angular/forms';@Directive ({selector:'[validateEqual] [ngModel]', providers: [{provide: NG_VALIDATORS, useExisting: forwardRef (() = > RepeatValidatorDirective), multi: true}]}) export class RepeatValidatorDirective implements Validator {constructor () {} validate (c: AbstractControl): {[key: string]: any} {return null;}}

We haven't officially started writing validation logic yet, but there are a few interesting points in the framework above:

One method that the 1.Validator interface requires must be implemented is validate (c: AbstractControl): ValidationErrors | null;. This is that we mentioned earlier that the verification returns null correctly, otherwise an object is returned, although there is no strict constraint, its key is generally used to indicate the name of the validator or the verification rule name, and value is usually the cause of failure or the verification result.

two。 Similar to the component, the instruction also has the selector metadata, which is used to select which element to apply the directive, so here we not only require the DOM element to apply validateEqual, but also need it to be a ngModel element, so it is a FormControl, and we are legal when we validate.

3. So what do those hideous-looking guys in providers do? Angular has an internal mechanism for performing validators on a FormControl: Angular maintains a multi provider with a token of NG_VALIDATORS (simply put, the form in which Angular injects multiple values into a single token is called multi provider). All built-in validators are added to the NG_VALIDATORS token, so when doing validation, Angular injects NG_VALIDATORS dependencies, that is, all validators, and then executes them one by one. So we also add ourselves to this NG_VALIDATORS here.

4. But if we just write useExisting: RepeatValidatorDirective, there will be a problem, RepeatValidatorDirective has not been generated, how can you use it in metadata? This requires the use of forwardRef to solve this problem, which takes a function that returns a class as an argument, but this function is not called immediately, but is called after the class is declared, thus avoiding the undefined situation.

Let's implement this verification logic, because the password and the confirmation password have a master-slave relationship, not a completely parallel relationship. In other words, the password is a benchmark comparison object, when the password changes, we should not prompt the password and confirm the password does not match, but should put the error in the confirmation password. So we give another attribute, reverse.

Export class RepeatValidatorDirective implements Validator {constructor (@ Attribute ('validateEqual') public validateEqual: string, @ Attribute (' reverse') public reverse: string) {} private get isReverse () {if (! this.reverse) return false; return this.reverse = = 'true'? True: false;} validate (c: AbstractControl): {[key: string]: any} {/ / the self value of the control let self = c.value; / / the value to be compared, that is, the value of the control in validateEqual= "ctrlname" let target = c.root.get (this.validateEqual) / / No reverse query with unequal values if (target & & self! = = target.value & & this.isReverse) {return {validateEqual: true}} / / reverse query with equal values if (target & & self = target.value & & this.isReverse) {delete target.errors ['validateEqual']; if (! Object.keys (target.errors) .length) target.setErrors (null) } / / reverse query with unequal values if (target & & self! = = target.value & & this.isReverse) {target.setErrors ({validateEqual: true})} return null;}}

After this modification, the verifier for password and confirmation password in our template file is as follows:

Verification error prompt after completion

Submission of the form

The submission of the form is relatively simple, just bind the ngSubmit event of the form

It is important to note, however, that button is treated as type= "submit" if it does not specify a type, so you need to explicitly specify type= "button" when the button is not to submit the form. And if you encounter a page refresh by clicking the submit button, it means that the default form submission event causes the browser to refresh, which needs to prevent the event from bubbling.

OnSubmit ({value, valid}, event: Event) {if (valid) {console.log (value);} event.preventDefault ();} above is all the content of the article "sample Analysis of template-driven forms for Angular 2 + forms". Thank you for reading! I believe we all have a certain understanding, hope to share the content to help you, if you want to learn more knowledge, welcome to follow the industry information channel!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report