今天,在成功验证Angular反应形式后,我遇到了角度布线问题。我要实现的是验证表单中的密码字段,如果成功重定向到另一个组件('/customers')
,或者如果密码不相同,我们应该什么也不做,并给用户提供机会再次输入正确的密码。
RegistrationComponent.ts
@Component({
selector: 'app-registration',
templateUrl: './registration.component.html',
styleUrls: ['./registration.component.css']
})
export class RegistrationComponent implements OnInit {
registerForm: FormGroup;
submitted = false;
email$: Observable<string>;
constructor(private formBuilder: FormBuilder, private customerService: CustomerService, private router: Router) {
}
ngOnInit() {
this.registerForm = this.formBuilder.group({
username: ['', Validators.required],
email: ['', Validators.required],
name: ['', Validators.required],
surname: ['', Validators.required],
phoneNumber: ['', Validators.required],
nip: ['', Validators.required],
password: ['', Validators.required],
confirmPassword: ['', Validators.required],
}, {
validator: MustMatch('password', 'confirmPassword')
});
}
get form() {
return this.registerForm.controls;
}
onSubmit() {
this.submitted = true;
const registeredCustomer: RegistrationForm = {
username: this.registerForm.controls.username.value,
email: this.registerForm.controls.email.value,
name: this.registerForm.controls.name.value,
surname: this.registerForm.controls.surname.value,
phoneNumber: this.registerForm.controls.phoneNumber.value,
password: this.registerForm.controls.password.value,
confirmPassword: this.registerForm.controls.confirmPassword.value,
};
this.email$ = this.customerService
.register(registeredCustomer)
.pipe(map(customer => customer.email));
if (this.registerForm.invalid) {
return;
} else {
setTimeout(() => this.router.navigate((['/customers'])), 5000);
}
alert('User successfully registered' + JSON.stringify(this.registerForm.value));
}
}
RegistrationComponent.html
<div class="jumbotron">
<div class="container">
<div class="row">
<div class="col-md-6 offset-md-3">
<h3>Fill in the form below to complete the registration process </h3>
<form [formGroup]="registerForm" (ngSubmit)="onSubmit()">
<div class="form-group">
<label>Username</label>
<input type="text" formControlName="username" class="form-control" [ngClass]="{ 'is-invalid': submitted && form.username.errors }" />
<div *ngIf="submitted && form.username.errors" class="invalid-feedback">
<div *ngIf="form.username.errors.required">Username is required</div>
</div>
</div>
<div class="form-group">
<label>Name</label>
<input type="text" formControlName="name" class="form-control" [ngClass]="{ 'is-invalid': submitted && form.name.errors }" />
<div *ngIf="submitted && form.name.errors" class="invalid-feedback">
<div *ngIf="form.name.errors.required">Customer name is required</div>
</div>
</div>
<div class="form-group">
<label>Surname</label>
<input type="text" formControlName="surname" class="form-control" [ngClass]="{ 'is-invalid': submitted && form.surname.errors }" />
<div *ngIf="submitted && form.surname.errors" class="invalid-feedback">
<div *ngIf="form.surname.errors.required">Customer surname is required</div>
</div>
</div>
<div class="form-group">
<label>Phone Number</label>
<input type="text" formControlName="phoneNumber" class="form-control" [ngClass]="{ 'is-invalid': submitted && form.phoneNumber.errors }" />
<div *ngIf="submitted && form.phoneNumber.errors" class="invalid-feedback">
<div *ngIf="form.phoneNumber.errors.required">Phone number is required</div>
</div>
</div>
<div class="form-group">
<label>Email</label>
<input type="text" formControlName="email" class="form-control" [ngClass]="{ 'is-invalid': submitted && form.email.errors }" />
<div *ngIf="submitted && form.email.errors" class="invalid-feedback">
<div *ngIf="form.email.errors.required">Email is required</div>
<div *ngIf="form.email.errors.email">Email must be a valid email address</div>
</div>
</div>
<div class="form-group">
<label>Password</label>
<input type="password" formControlName="password" class="form-control" [ngClass]="{ 'is-invalid': submitted && form.password.errors }" />
<div *ngIf="submitted && form.password.errors" class="invalid-feedback">
<div *ngIf="form.password.errors.required">Password is required</div>
<div *ngIf="form.password.errors.minlength">Password must be at least 6 characters</div>
</div>
</div>
<div class="form-group">
<label>Confirm Password</label>
<input type="password" formControlName="confirmPassword" class="form-control" [ngClass]="{ 'is-invalid': submitted && form.confirmPassword.errors }" />
<div *ngIf="submitted && form.confirmPassword.errors" class="invalid-feedback">
<div *ngIf="form.confirmPassword.errors.required">Confirm Password is required</div>
<div *ngIf="form.confirmPassword.errors.mustMatch">Passwords must match</div>
</div>
</div>
<div class="form-group">
<button class="btn btn-primary">Register</button>
</div>
</form>
</div>
</div>
</div>
</div>
我基于本教程:Registration tutorial
但是经过少量修改,我当前的解决方案无法正常工作。
我正尝试替换此
setTimeout(() => this.router.navigate((['/customers'])), 5000);
前面:
if (this.registerForm.invalid) {
return;
}
并检查是否为布尔值submitted = false
,但没有产生期望的结果。
在此先感谢您的帮助。
编辑:应用程序的Stackblitz链接。
答案 0 :(得分:1)
我们一直在研究库,以便轻松处理表单并具有更高的类型安全性(对于TS和HTML!)。该库称为ngx-sub-form
,您可以在这里找到它:https://github.com/cloudnc/ngx-sub-form
我使用ngx-sub-form
演示了您的应用,可以在此处进行尝试:
https://stackblitz.com/edit/angular-genpiv
现在,一些解释。
首先,我们要为您的表单定义一个适当的接口(为胜利输入安全性!):
export interface RegistrationForm {
username: string;
email: string;
name: string;
surname: string;
phoneNumber: string;
nip: string;
password: string;
confirmPassword: string;
}
然后,我们创建应用程序所需的所有组件:
-app/app.component.ts
-app/registration/registration.component.ts
-app/registration/registration-form/registration-form.component.ts
-app/customers.component.ts
现在,我们需要为我们的应用程序定义路由。我们的app.module.ts
应该如下所示:
const ROUTES: Routes = [
{
path: '',
redirectTo: '/registration',
pathMatch: 'full'
},
{
path: 'registration',
component: RegistrationComponent
},
{
path: 'customers',
component: CustomersComponent
},
];
@NgModule({
imports: [BrowserModule, ReactiveFormsModule, RouterModule.forRoot(ROUTES)],
declarations: [AppComponent, RegistrationFormComponent, RegistrationComponent, CustomersComponent],
bootstrap: [AppComponent]
})
export class AppModule { }
app.component.html
应该只是<router-outlet></router-outlet>
。
RegistrationComponent
将充当智能组件。它将注入路由器,并等待发送表格。该组件不想不想知道表单本身,而只是知道已发送的对象。
@Component({
selector: 'app-registration',
templateUrl: './registration.component.html',
styleUrls: ['./registration.component.css']
})
export class RegistrationComponent {
constructor(private router: Router) {}
public register(registrationForm: RegistrationForm): void {
// here you probably want to inject a `RegistrationService` instead of the `Router`
// and call a method that will make an XHR call to your backend and on success
// would do that `navigate` from the service too
// for the sake of simplicity here I'm just doing the navigate directly
this.router.navigate(['/customers']);
}
}
及其HTML:
<app-registration-form (register)="register($event)"></app-registration-form>
RegistrationFormComponent
将是负责处理表单的组件。该组件是唯一需要使用ngx-sub-form
库的组件。
@Component({
selector: 'app-registration-form',
templateUrl: './registration-form.component.html',
styleUrls: ['./registration-form.component.css']
})
export class RegistrationFormComponent extends NgxSubFormComponent<RegistrationForm> {
@Output() register: EventEmitter<RegistrationForm> = new EventEmitter();
protected getFormControls(): Controls<RegistrationForm> {
return {
username: new FormControl(null, [Validators.required]),
email: new FormControl(null, [Validators.required]),
name: new FormControl(null, [Validators.required]),
surname: new FormControl(null, [Validators.required]),
phoneNumber: new FormControl(null, [Validators.required]),
nip: new FormControl(null, [Validators.required]),
password: new FormControl(null, [Validators.required]),
confirmPassword: new FormControl(null, [Validators.required]),
}
}
protected getFormGroupControlOptions(): FormGroupOptions<RegistrationForm> {
return {
validators: [
formGroup => {
if (formGroup.value.password !== formGroup.value.confirmPassword) {
return {
passwordsMustMatch: true,
};
}
return null;
},
],
};
}
public onSubmit(): void {
this.register.emit(this.formGroupValues)
}
}
这里要注意的事情:
export class RegistrationFormComponent extends NgxSubFormComponent<RegistrationForm>
,我们正在扩展NgxSubFormComponent
,并传递表单接口。这将为我们提供许多实用程序以及一些类型安全性
方法getFormControls
希望您基本上提供一个对象来创建表单。我认为这是不言自明的,因为它看起来像您在创建FormGroup
getFormGroupControlOptions
是NgxSubFormComponent
提供的挂钩,可让您在FormGroup
级别设置验证器或asyncValidators
最后,onSubmit
方法是当用户单击Register
按钮(一旦表单完全有效)时将调用的方法。
现在,最后丢失的部分是HTML表单(为简单起见,我将仅在响应中显示第一个字段和密码检查,因为两者之间的内容几乎相同)
<div class="jumbotron">
<div class="container">
<div class="row">
<div class="col-md-6 offset-md-3">
<h3>Fill in the form below to complete the registration process</h3>
<form [formGroup]="formGroup" (ngSubmit)="onSubmit()">
<div class="form-group">
<label>Username</label>
<input type="text" [formControlName]="formControlNames.username" class="form-control" [ngClass]="{ 'is-invalid': formGroupErrors?.username }" />
<div *ngIf="formGroupErrors && formGroupControls.username.touched" class="invalid-feedback">
<div *ngIf="formGroupErrors?.username?.required">Username is required</div>
</div>
</div>
...
<div class="form-group">
<label>Confirm Password</label>
<input type="text" [formControlName]="formControlNames.confirmPassword" class="form-control" [ngClass]="{ 'is-invalid': formGroupErrors?.confirmPassword }" />
<div *ngIf="formGroupErrors && formGroupControls.confirmPassword.touched" class="invalid-feedback">
<div *ngIf="formGroupErrors?.confirmPassword?.required">Confirm Password is required</div>
<div *ngIf="formGroupErrors?.formGroup?.passwordsMustMatch">Passwords must match</div>
</div>
</div>
<div class="form-group">
<button class="btn btn-primary" [disabled]="formGroup.invalid">Register</button>
</div>
</form>
</div>
</div>
</div>
</div>
HTML注意事项
<form [formGroup]="formGroup"
我们还没有自己定义formGroup
。它是由NgxSubFormComponent
<input type="text" [formControlName]="formControlNames.username"
formControlNames
属性也由NgxSubFormComponent
定义,其主要目的是带来某种类型的安全性!如果尝试放置formControlNames.random
,则在使用AOT进行编译时会遇到运行时错误和Typescript错误!
<div *ngIf="formGroupErrors?.username?.required">
属性formGroupErrors
也由NgxSubFormComponent
提供,使您可以访问格式错误。最好的部分是它可以与嵌套子表单一起使用,并且类型安全!即使这里我们没有任何子表单,我仍邀请您检查ngx-sub-form
的Github页面以了解有关此内容的更多信息
该应用程序现已完全准备就绪,具有很多类型安全性!任何影响主界面的重构都将要求您更新表单(TS + HTML),否则Typescript会引发错误。
这是它的样子:
然后生效:
然后,当我们单击注册时:
请不要忘记在此处查看实时示例:https://stackblitz.com/edit/angular-genpiv
答案 1 :(得分:0)
要激活路由,必须将路由模块注入app.module.ts中(即根模块)。 还要在路由模块文件中将路由的路径定义为:
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [
{path: 'customers' , component: CustomersComponent}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Aslo不要要求包含<router-outlet></router-outlet>
标签,因为在路由成功后它将在子视图中加载它。