markdown Angular2 Snippets - 表格(反应式方法)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了markdown Angular2 Snippets - 表格(反应式方法)相关的知识,希望对你有一定的参考价值。
# Forms
## Reactive approach
* in app.module, instead of the `FormsModule` you need the `ReactiveFormsModule`:
```javascript
...
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
ReactiveFormsModule,
HttpModule
],
```
* In template driven approach we imported `NgForm`, that automatically created a `FormGroup` wrapper for our form (in Angular a form is just a group of controls)
* In reactive approach, `FormGroup` is created programmatically and sychronized/connected with our DOM.
* We use `FormControl` to create our form controls (for angular, they're simply "controls"), no matter if it is a radio button, text input, etc.
```javascript
...
import { FormGroup } from '@angular/forms';
@Component({
...
})
export class AppComponent {
genders = ['male', 'female'];
signUpForm: FormGroup; // new property of type 'FormGroup' that will hold our form.
// We'll initialize our form in a method inside ngOnInit,
// because we need to do it before rendering our template:
ngOnInit() {
this.signUpForm = new FormGroup({ // pass a javascript object that defines our controls with key/value pairs.
// Put your keys between ' ' to make sure that the name is kept during minification
// (because we are referencing it in our html code)
//
// We create our controls with 'FormControl'. Arguments:
// - first: initial state/value of this control
// - second: validator or [array of validators] you want to apply to this control
// - third: potential asynchronous validators
'usename': new FormControl(null),
'email': new FormControl(null),
'gender': new FormControl('male'),
});
}
}
```
* To synchronize our HTML code with our form we have to give the `form` tag directives to override the default Angular behaviour and receive our form instructions from our code:
```html
<form [formGroup]="signUpForm"> // property binding the 'formGroup' directive we tell Angular, 'don't create a form for me, use my own formGroup'
<div class="form-group">
<label for="username">Username</label>
<input
type="text"
id="username"
formControlName="username" // tell angular what's the name of this input in my form code
class="form-control">
</div>
```
* To submit the form we still use `ngSubmit` because we still want to react to the native HTML form `submit` event. But in this case we don't need the form local reference here, because we created the form on our code:
`<form [formGroup]="signUpForm" (ngSubmit)="onSubmit()">`
```javascript
onSubmit() {
console.log(this.signUpForm);
}
```
* This is the cool thing about the reactive approach: whatever you set in the object argument you pass to the `FormGroup`, you'll get out as a value when you submit the form, so you can bind it to your own model in your app, and make sure that your form structure matches the desired model structure.
### Adding validation
* Second argument in FormGroup allows you to configure one or several (array) validators via the `Validators` object:
```javascript
this.signUpForm = new FormGroup({
'usename': new FormControl(null, Validators.required), // don't add parenthesis after the call: you're only passing the reference to this method.
// Angular will execute it when detects changes in this form control.
'email': new FormControl(null, [Validators.required, Validators.email]),
..
});
```
* To display validation messages, we need to access the overall form and then with the `.get()` method access our controls easily.
* You specify the control name or a path to the control (in a nested form object).
* Then, `valid` and `touched` holds this control state:
```html
<span class="help-block"
*ngIf="!signupForm.get('email').valid && signupForm.get('email').touched">Please enter a valid email!</span>
```
* For the overall form we can simply check for `valid` and `touched`:
```html
<span class="help-block"
*ngIf="!signupForm.valid && signupForm.touched">Please enter valid data!</span>
```
### Grouping controls
```javascript
this.signUpForm = new FormGroup({
'userData': new FormGroup({
'username': new FormControl(null, Validators.required),
'email': new FormControl(null, [Validators.required, Validators.email]),
}),
'gender': new FormControl('male'),
});
```
* we have to replicate this in our html to keep in sync, and also update the routes to our controls:
```html
<form [formGroup]="signUpForm" (ngSubmit)="onSubmit()">
<div formGroupName="userData">
<div class="form-group">
<label for="username">Username</label>
<input ... formControlName="username">
<span class="help-block"
*ngIf="!signUpForm.get('userData.username').valid && signUpForm.get('userData.username').touched">Please enter a valid username!</span>
</div>
</div>
```
### Arrays of form controls (FormArray):
* Example: allow the user to add form controls:
```javascript
this.signUpForm = new FormGroup({
...
'gender': new FormControl('male'),
'hobbies': new FormArray([]), // 'FormArray' holds an array of 'FormControls'
// (you can initialize it with an empty array or with some form controls)
});
onAddHobby() {
const control = new FormControl(null, Validators.required);
(<FormArray>this.signUpForm.get('hobbies')).push(control); // we explictly cast to 'FormArray' type twith < > and placing all in parenthesis
// -this tells typescript that everything between parenthesis is treated as FormArray
}
```
* In our HTML, to keep in sync:
```html
<div formArrayName="hobbies"> //we need 'formArrayName', as we needed 'formGroupName', 'formControlName'...
<button type="button" class="btn btn-light"
(click)="onAddHobby()">Add hobby</button>
<div class="form-group"
*ngFor="let hobbyControl of signUpForm.get('hobbies').controls; let i = index"> //we loop over the controls in this formArray
<input type="text" class="form-control" [formControlName]="i"> // property binding because I'm not passing a string here
</div>
</div>
```
### Creating custom validators:
* Validators included with angular: [https://angular.io/api/forms/Validators](https://angular.io/api/forms/Validators)
* A validator is just a function that is executed by Angular automatically when a form control changes, to check its validity. Let's create a custom validator to avoid the use of forbidden usernames:
```javascript
forbiddenUsernames = ['Oscar', 'Anna'];
// our custom validator function:
// - arguments: the form control that we want to check
// - must return an object with a key:string and a value:boolean
// - if validation is successful, omit the return statement or return null.
forbiddenNames(control: FormControl): {[s: string]: boolean} {
if (this.forbiddenUsernames.indexOf(control.value) !== -1) { // if the form control value is in the array of forbiddenUsernames
return {'nameIsForbidden': true}; // any short error code you want
}
return null; // DON'T return {'nameIsForbidden': false}
}
```
Add our validator to the field:
```javascript
this.signUpForm = new FormGroup({
...
'username': new FormControl(null, [Validators.required, this.forbiddenNames.bind(this)]), // remember to only pass a reference (not calling the method),
// and also .bind(this) -if not, when Angular executes this function itself, 'this' wont be referring to our class
```
** You can also create your validators in external files, so you can re-use them. **
custom-validators.ts
```javascript
export class CustomValidators {
static forbiddenProjectNames(control: FormControl): {[s: string]: boolean} {
...
}
// 'static' keyword will create a method associated with the class, and not with an instance of the class.
// In other words, you can only reach a static method using the name of the class.
// So you don't have to instantiate the class to be able to access its methods.
}
```
Then in your Reactive Form definition:
```javascript
//...import custom-validator...
this.myReactiveForm = new FormGroup({
'projectName': new FormControl(null, [Validators.required, CustomValidators.forbiddenProjectNames]),
...
```
### Using error codes
Angular stores the form errors on the individual controls, in an `errors` object:
```html
<p class="help-block"
*ngIf="!signUpForm.get('userData.username').valid && signUpForm.get('userData.username').touched">
<span
*ngIf="signUpForm.get('userData.username').errors['nameIsForbidden']">This name is not allowed</span>
<span
*ngIf="signUpForm.get('userData.username').errors['required']">This field is required</span>
</p>
```
*You could improve this example mapping the errors to an object with messages.*
### Creating a custom async validator
* If you need to reach to a web server to check for some validations, you need *asynchronous* validators who are able to wait to a response
```javascript
forbiddenEmails(control: FormControl): Promise<any> | Observable<any> { // in this case we don't expect an object to be returned, but a Promise or Observable
const promise = new Promise<any>((resolve, reject) => {
setTimeout(() => {
if (control.value === 'test@test.com') {
resolve({'emailIsForbidden': true}); // since I am in a promise, I don't 'return', I 'resolve'
} else {
resolve(null);
}
}, 1500);
});
return promise;
}
```
Now to add it to our validators:
```javascript
'email': new FormControl(null, [Validators.required, Validators.email], this.forbiddenEmails), // you add it in the third argument (async validators)
```
*When checking the validity, the input field gets applied `ng-pending` class.
### Reacting to status or value changes
* For example in async validation it changes from `invalid` to `pending` and then to `valid`. You can observe this state changes in your code, both for the whole form and for individual form controls:
```javascript
ngOnInit() {
...
// listen to value changes (for the whole form)
this.signupForm.valueChanges.subscribe(
(value) => console.log(value); // prints the form object with all the control values
);
// listen to status changes (for the whole form)
this.signupForm.statusChanges.subscribe(
(status) => console.log(status); // prints INVALID / PENDING / VALID
);
// listen to value changes (for a single form control)
this.signupForm.get('username').valueChanges.subscribe(
(value) => console.log(value);
);
}
```
### Setting and patching values
* You can set/change values for your whole form or for individual controls:
```javascript
// Set initial values (whole form)
this.signUpForm.setValue({
'userData': {
'username': 'Max',
'email': 'max@test.com'
},
'gender': 'male',
'hobbies': []
});
// Set initial value of a control
this.signUpForm.patchValue({
'userData': {
'username': 'Anna',
}
});
```
You can also reset the form on submit with:
```javascript
this.signUpForm.reset()
```
*If you don't want to clear for example the radiobuttons, remember that you can pass an object to `reset()` to reset to specific values.*
以上是关于markdown Angular2 Snippets - 表格(反应式方法)的主要内容,如果未能解决你的问题,请参考以下文章
markdown [查看寻呼机不完整,添加片段示例] #android_snippet #android
Visual Studio Code配置Markdown文档的snippet不生效的解决