ng 表单
状态 | 为真时的 CSS 类 | 为假时的 CSS 类 |
---|---|---|
控件被访问过。 | ng-touched | ng-untouched |
控件的值变化了。 | ng-dirty | ng-pristine |
控件的值有效。 | ng-valid | ng-invalid |
响应式表单
在模块中导入 ReactiveFormsModule 模块
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [
// other imports ...
ReactiveFormsModule
],
})
export class AppModule { }
使用 FormControl 注册一个表单控件
<input type="text" [formControl]="name" /> {{ name.value }}
import { FormControl } from "@angular/forms";
name = new FormControl("ajanuw");
设置表单的值
ngOnInit() {
setTimeout(() => this.name.setValue("new name"), 2000);
}
表单控件分组 FormGroup
<form action="" [formGroup]="profileForm" (ngSubmit)="onSubmit()">
<input type="text" formControlName="name" />
<input type="text" formControlName="age" />
<button type="submit" [disabled]="!profileForm.valid">submit</button>
</form>
import { FormControl, FormGroup } from "@angular/forms";
profileForm = new FormGroup({
name: new FormControl("ajanuw"),
age: new FormControl("14"),
});
onSubmit() {
l(this.profileForm.value);
}
表单嵌套
<form action="" [formGroup]="profileForm" (ngSubmit)="onSubmit()">
<input type="text" formControlName="name" />
<input type="text" formControlName="age" />
<div formGroupName="address">
<input type="text" formControlName="street" />
<input type="text" formControlName="zip" />
</div>
<button type="submit" [disabled]="!profileForm.valid">submit</button>
</form>
import { FormControl, FormGroup } from "@angular/forms";
profileForm = new FormGroup({
name: new FormControl("ajanuw"),
age: new FormControl("14"),
address: new FormGroup({
street: new FormControl("街道"),
zip: new FormControl("123"),
}),
});
onSubmit() {
l(this.profileForm.value);
l(this.profileForm.get('name').value);
}
部分模型更新
this.profileForm.patchValue({
name: "new name",
address: {
zip: 233,
},
});
使用 FormBuilder 来生成表单控件
数组中的第一项是其初始值,第二项和第三项提供同步和异步验证器
import { FormBuilder } from "@angular/forms";
profileForm = this.fb.group({
name: ["ajanuw"],
age: ["14"],
address: this.fb.group({
street: ["街道"],
zip: ["123"],
}),
});
constructor(private fb: FormBuilder) {}
表单状态
无效的(invalid),有效的(valid)
{{ profileForm.status }}
FormArray
如果你事先不知道子控件的数量,这就是一个很好的选择
import { FormBuilder, Validators, FormArray } from "@angular/forms";
profileForm = this.fb.group({
name: ["", Validators.required],
age: ["14"],
address: this.fb.group({
street: ["街道"],
zip: ["123"],
}),
aliases: this.fb.array([this.fb.control("")]),
});
ngOnInit() {
setTimeout(() => {
this.profileForm.get("aliases") as FormArray;
}, 2000);
}
get aliases() {
return this.profileForm.get("aliases") as FormArray;
}
<div formArrayName="aliases">
<div *ngFor="let address of aliases.controls; let i = index">
<input type="text" [formControlName]="i" placeholder="alias" />
</div>
</div>
表单验证
<form action="" [formGroup]="info">
<input type="text" formControlName="name" />
<div
*ngIf="name.invalid && (name.dirty || name.touched)"
class="alert alert-danger"
>
<div *ngIf="!!name.errors.required">Name is required.</div>
<div *ngIf="!!name.errors.minlength">
Name must be at least 4 characters long.
</div>
<div *ngIf="!!name.errors.startKey">必须使用`@`开始</div>
</div>
<br />
<input type="text" formControlName="pwd" placeholder="密码" />
<div
*ngIf="pwd.invalid && (pwd.dirty || pwd.touched)"
class="alert alert-danger"
>
<div *ngIf="!!pwd.errors.required">pwd is required.</div>
<div *ngIf="!!pwd.errors.minlength">
pwd must be at least 6 characters long.
</div>
</div>
<br />
<input type="text" formControlName="rpwd" placeholder="确认密码" />
<div
*ngIf="rpwd.invalid && (rpwd.dirty || rpwd.touched)"
class="alert alert-danger"
>
<div *ngIf="!!rpwd.errors.required">rpwd is required.</div>
<div *ngIf="!!rpwd.errors.minlength">
rpwd must be at least 6 characters long.
</div>
</div>
<br />
<button type="submit" [disabled]="!info.valid">submit</button>
</form>
import { Component, OnInit } from "@angular/core";
import {
FormBuilder,
Validators,
FormControl,
AbstractControl,
} from "@angular/forms";
import { startKey } from "src/app/start-key.directive";
@Component({
selector: "app-welcome",
templateUrl: "./welcome.component.html",
styleUrls: ["./welcome.component.css"],
})
export class WelcomeComponent implements OnInit {
info = this.fb.group({
name: [
"",
{
updateOn: "blur",
validators: [
Validators.required,
Validators.minLength(5),
startKey("@"),
],
},
],
pwd: ["", [Validators.required, Validators.minLength(7)]],
rpwd: ["", [Validators.required, Validators.minLength(7)]],
});
get name() {
const name = this.info.get("name");
return name;
}
get pwd() {
return this.info.get("pwd");
}
get rpwd() {
return this.info.get("rpwd");
}
constructor(private fb: FormBuilder) {}
ngOnInit() {}
}
与Bootstrap的Form组合
<div
class="container vw-100 vh-100 d-flex align-items-center justify-content-center"
>
<form class="w-50" [formGroup]="loginForm" novalidate>
<div class="mb-3">
<label class="form-label">{{ "auth.username" | translate }}</label>
<input
class="form-control"
formControlName="username"
[ngClass]="{
'is-invalid': username.invalid,
'is-valid': username.valid
}"
/>
<div class="valid-feedback">ok.</div>
<div class="invalid-feedback">请填写用户名!</div>
</div>
<div class="mb-3">
<label class="form-label">{{ "auth.password" | translate }}</label>
<input
type="password"
formControlName="password"
class="form-control"
[ngClass]="{
'is-invalid': password.invalid,
'is-valid': password.valid
}"
/>
<div *ngIf="password.errors?.required" class="invalid-feedback">
请填写密码!
</div>
<div class="valid-feedback">ok.</div>
<div *ngIf="password.errors?.minlength" class="invalid-feedback">
密码最少长度为{{ password.errors?.minlength.requiredLength }}位!
</div>
</div>
<div class="mb-3 form-check">
<input
type="checkbox"
class="form-check-input"
id="check1"
formControlName="check"
/>
<label class="form-check-label user-select-none" for="check1">
{{ "auth.check" | translate }}</label
>
</div>
<button
class="btn btn-primary w-100"
type="button"
[disabled]="loading || loginForm.invalid"
(click)="loginForm.valid && login()"
>
<span
*ngIf="loading"
class="spinner-border spinner-border-sm"
role="status"
aria-hidden="true"
></span>
{{ "auth.login" | translate }}
</button>
</form>
</div>
import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { TranslateService } from '@ngx-translate/core';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.styl'],
})
export class LoginComponent implements OnInit {
constructor(
public readonly fb: FormBuilder,
public readonly translate: TranslateService,
private readonly http: HttpClient
) {}
loginForm = this.fb.group({
username: ['admin', Validators.required],
password: ['123', [Validators.required, Validators.minLength(3)]],
check: [true],
});
get username() {
return this.loginForm.get('username');
}
get password() {
return this.loginForm.get('password');
}
loading = false;
ngOnInit(): void {}
async login() {
this.loading = true;
const r = await this.http
.post('api/login', this.loginForm.value)
.toPromise();
this.loading = false;
}
}