2021-12-15 15:41:21 +01:00
# Forms
Angular offers two different approach to retrieving data from a from.
- Template-driven (predefined)
- Reactive (Manual setup but flexible)
## Template driven
### Creating the Form and registering the controls
Import the Form module in the `app.module.ts` .
```ts
import { FormsModule } from '@angular/forms';
@NgModules ({
imports: [ FormsModule ]
})
```
Register the imput with the `ngModel` directive.
< img src = "../img/form_ngModel.png" alt = "overview" width = "450" / >
### Submitting and using the Form
app.component.html
```html
< form ( ngSubmit )=" onSubmit ( f )" #f =" ngForm " >
```
app.component.ts
```ts
onSubmit(form: ngForm) {
console.log(form);
}
```
< img src = "../img/ngForm_json.png" alt = "overview" width = "300" / >
#### @ViewChild
Here we can retrive the data also without submitting the form
```ts
import { ViewChild } from '@angular/core';
export class AppComponent {
@ViewChild ('f') signupForm: ngForm;
onSubmit() {
console.log(this.signupForm);
}
```
### FormValidation
By adding an attribute like `required` , `email` to the html form. Angular detects as a directive and validates it automatically.
[Angular Validators ](https://angular.io/api/forms/Validators )
#### Using the Form state
```html
< button
type="submnit"
[disabled]="!f.valid">Submit< / button >
```
The `f` referes to the local reference declaredy§ in the form.
Visually can be showen that a input is invalid by adding this CSS definition.
```css
input.ng-invalid.ng-touched {
border: 1px solid red;
}
```
It shows only a red boder when the input field has been modified.
Showing some hint text
```html
< label for = "email" > Mail< / label >
< input
type="email"
id="email"
class="form-control"
ngModel
name="email"
required
email
#email ="ngModel">
< span class = "help-block" * ngIf = "!email.valid && email.touched" > Please enter a valid email!< / span >
```
`setValue` and `patchValue` helps to preset the form with predefined values. With `setValue` overrides the whole form, and `patchValue` updates only a specific inputfield. `patchValue` is aviable only from the `form` (`signupForm.form.patchValue({userData: { username: 'bliblubla'}});`).
## Reactive driven
Import the Form module in the `app.module.ts` .
```ts
import { ReactiveFormsModule } from '@angular/forms';
@NgModules ({
imports: [ ReactiveFormsModule ]
})
```
app.component.html
```html
< div class = "container" >
< div class = "row" >
< div class = "col-xs-12 col-sm-10 col-md-8 col-sm-offset-1 col-md-offset-2" >
< form [ formGroup ] = " signupForm " ( ngSubmit ) = " onSubmit ( ) " >
< div formGroupName = "userData" >
< div class = "form-group" >
< label for = "username" > Username< / label >
< input
type="text"
id="username"
formControlName="username"
class="form-control">
< span
*ngIf="!signupForm.get('userData.username').valid & & signupForm.get('userData.username').touched"
class="help-block">
< span * ngIf = "signupForm.get('userData.username').errors['nameIsForbidden']" > This name is invalid!< / span >
< span * ngIf = "signupForm.get('userData.username').errors['required']" > This field is required!< / span >
< / span >
< / div >
< div class = "form-group" >
< label for = "email" > email< / label >
< input
type="text"
id="email"
formControlName="email"
class="form-control">
< span
*ngIf="!signupForm.get('userData.email').valid & & signupForm.get('userData.email').touched"
class="help-block">Please enter a valid email!< / span >
< / div >
< / div >
< div class = "radio" * ngFor = "let gender of genders" >
< label >
< input
type="radio"
formControlName="gender"
[value]="gender">{{ gender }}
< / label >
< / div >
< div formArrayName = "hobbies" >
< h4 > Your Hobbies< / h4 >
< button
class="btn btn-default"
type="button"
(click)="onAddHobby()">Add Hobby< / button >
< div
class="form-group"
*ngFor="let hobbyControl of signupForm.get('hobbies').controls; let i = index">
< input type = "text" class = "form-control" [ formControlName ] = " i " >
< / div >
< / div >
< span
*ngIf="!signupForm.valid & & signupForm.touched"
class="help-block">Please enter valid data!< / span >
< button class = "btn btn-primary" type = "submit" > Submit< / button >
< / form >
< / div >
< / div >
< / div >
```
app.component.ts
```ts
import { Component, OnInit } from '@angular/core';
import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms';
import { Observable } from 'rxjs/Observable';
@Component ({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
genders = ['male', 'female'];
signupForm: FormGroup;
forbiddenUsernames = ['Chris', 'Anna'];
constructor() {}
ngOnInit() {
this.signupForm = new FormGroup({
'userData': new FormGroup({
'username': new FormControl(null, [Validators.required, this.forbiddenNames.bind(this)]),
'email': new FormControl(null, [Validators.required, Validators.email], this.forbiddenEmails)
}),
'gender': new FormControl('male'),
'hobbies': new FormArray([])
});
// this.signupForm.valueChanges.subscribe(
// (value) => console.log(value)
// );
this.signupForm.statusChanges.subscribe(
(status) => console.log(status)
);
this.signupForm.setValue({
'userData': {
'username': 'Max',
'email': 'max@test.com'
},
'gender': 'male',
'hobbies': []
});
this.signupForm.patchValue({
'userData': {
'username': 'Anna',
}
});
}
onSubmit() {
console.log(this.signupForm);
this.signupForm.reset();
}
onAddHobby() {
const control = new FormControl(null, Validators.required);
(< FormArray > this.signupForm.get('hobbies')).push(control);
}
forbiddenNames(control: FormControl): {[s: string]: boolean} {
if (this.forbiddenUsernames.indexOf(control.value) !== -1) {
return {'nameIsForbidden': true};
}
return null;
}
forbiddenEmails(control: FormControl): Promise< any > | Observable< any > {
const promise = new Promise< any > ((resolve, reject) => {
setTimeout(() => {
if (control.value === 'test@test.com') {
resolve({'emailIsForbidden': true});
} else {
resolve(null);
}
}, 1500);
});
return promise;
}
}
```