成功进行角度形式验证后无重定向

时间:2019-05-31 14:34:03

标签: angular angular2-routing angular-reactive-forms

今天,在成功验证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链接。

component

stackblitz editor

2 个答案:

答案 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

  • 时要传递的对象
  • getFormGroupControlOptionsNgxSubFormComponent提供的挂钩,可让您在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会引发错误。

这是它的样子:

enter image description here

然后生效:

enter image description here

然后,当我们单击注册时:

enter image description here

请不要忘记在此处查看实时示例: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>标签,因为在路由成功后它将在子视图中加载它。