提交有效表单和更改样式

Submitting valid form and changing style

提问人:Elliott Alderson 提问时间:10/4/2023 最后编辑:Elliott Alderson 更新时间:10/6/2023 访问量:34

问:

我有一个 Angular 15 应用程序。我正在使用 Angular 材质进行造型设计。 我只想提交有效的表格,即没有一个字段应该是空的,电子邮件字段必须是电子邮件。

这是我的表单的样子:




`       <form #form="ngForm" (ngSubmit)="onFormSubmit()" class="py-3">
            <div class="row">

              <mat-form-field class="col">
                <mat-label>First name</mat-label>
                <input matInput placeholder="Ex: Jhon" [formControl]="fnameFormControl" [errorStateMatcher]="matcher" [(ngModel)]="model.fname" name="fname">
                <mat-error *ngIf="fnameFormControl.hasError('required')">
                  Field is <strong>required</strong>
                </mat-error>
              </mat-form-field>
              <mat-form-field class="col">
                <mat-label>Last name</mat-label>
                <input matInput placeholder="Ex: Doe" [formControl]="lnameFormControl" [errorStateMatcher]="matcher" [(ngModel)]="model.lname" name="lname">
                <mat-error *ngIf="lnameFormControl.hasError('required')">
                  Field is <strong>required</strong>
                </mat-error>
              </mat-form-field>
            </div>
            <div class="row pb-3">
              <div class="col">
                <input type="tel" id="phone" class="w-100 form-control" [(ngModel)]="model.phone" name="phone">
                <span id="error-msg" class="d-none error-msg"></span>
              </div>
            </div>

            <mat-form-field class="form-group w-100">
              <mat-label>Email</mat-label>
              <input type="email" matInput [formControl]="emailFormControl" [errorStateMatcher]="matcher" [(ngModel)]="model.email" name="email"
                     placeholder="Ex. [email protected]">
              <!-- <mat-hint>Errors appear instantly!</mat-hint> -->
              <mat-error *ngIf="emailFormControl.hasError('email') && !emailFormControl.hasError('required')">
                Please enter a valid email address
              </mat-error>
              <mat-error *ngIf="emailFormControl.hasError('required')">
                Email is <strong>required</strong>
              </mat-error>
            </mat-form-field>

              <mat-form-field class="w-100">
                <mat-label>Choose a breed</mat-label>
                <mat-select [formControl]="breedSelectFormControl" [errorStateMatcher]="matcher" [(ngModel)]="model.breed" name="breed">
                  <mat-option *ngFor="let breedof breeds" [value]="breed.value">
                    {{breed.viewValue}}
                  </mat-option>
                </mat-select>
                <mat-error *ngIf="breedSelectFormControl.hasError('required')">
                  Please select on option
              </mat-error>
              </mat-form-field>

              <mat-form-field class="w-100">
                <mat-label>Share your story with us</mat-label>
                <textarea matInput placeholder="Write it here..." [formControl]="storyFormControl" [errorStateMatcher]="matcher" [(ngModel)]="model.story" name="story"></textarea>
                <mat-error *ngIf="storyFormControl.hasError('required')">
                  Field is <strong>required</strong>
                </mat-error>
              </mat-form-field>

              <div class="form-group pt-3">
                  <button class="btn btn-warning btn-block w-100 " id="contact-btn">Get a free consultation</button>
              </div>
          </form>`



同一组件的.ts逻辑如下所示:




import { Component } from '@angular/core';
import {
  FormControl,
  FormGroupDirective,
  NgForm,
  Validators,
  FormsModule,
  ReactiveFormsModule,
} from '@angular/forms';
import {MatSelectModule} from '@angular/material/select';
import {ErrorStateMatcher} from '@angular/material/core';
import {NgIf, NgFor} from '@angular/common';
import {MatInputModule} from '@angular/material/input';
import {MatFormFieldModule} from '@angular/material/form-field';
import { addBreed Request } from '../model/add-breed-request.model';

/** Error when invalid control is dirty, touched, or submitted. */
export class MyErrorStateMatcher implements ErrorStateMatcher {
  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
    const isSubmitted = form && form.submitted;
    return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
  }
}

interface Breed {
  value: string;
  viewValue: string;
}

@Component({
  selector: 'app-contact-card',
  templateUrl: './contact-card.component.html',
  styleUrls: ['./contact-card.component.css'],
  standalone: true,
  imports: [FormsModule, MatSelectModule , MatFormFieldModule, MatInputModule, ReactiveFormsModule, NgIf , NgFor],
})
export class ContactCardComponent {
  //base model
  model: addBreedRequest;

  emailFormControl = new FormControl('', [Validators.required, Validators.email]);

  fnameFormControl = new FormControl('',[Validators.required]);

  lnameFormControl = new FormControl('',[Validators.required]);

  breedSelectFormControl = new FormControl('',[Validators.required]);

  storyFormControl = new FormControl('',[Validators.required]);

  matcher = new MyErrorStateMatcher();

  breeds: Breed[] = [
    {value: '1', viewValue: 'German Shepherd'},
    {value: '2', viewValue: 'German Longhaired Pointer'},
    {value: '3', viewValue: 'German Pinscher'},
    {value: '4', viewValue: 'German Shepherd Dog'},
    {value: '5', viewValue: 'German Shorthaired Pointer'},
    {value: '6', viewValue: 'German Wirehaired Pointer'},
    {value: '7', viewValue: 'German Spitz'}
  ];

  constructor(){
      this.model = {
        fname: '',
        lname: '',
        email: '',
        phone: '',
        breed: '',
        story: ''
      }
  }

  onFormSubmit(){
    // here I want to check if form is valid.
    console.log(this.model);
    // expectations:
    if(form.isValid()){
    // submit request and change form style.
   }
  }
}




视觉验证工作得很好,但是当我尝试将表单传递给 onFormSubmit() 时,即使有空字段,它也显示表单有效。

我尝试了这样的事情:

<form #form="ngForm" (ngSubmit)="onFormSubmit(**form**)" class="py-3">

// and in .ts file
  onFormSubmit(form:FormControl){
    console.log(form.isValid()); // outputing valid all the time.
  }

更新:此外,我在浏览器控制台中收到此消息:

看起来您正在与 formControl 在同一表单字段上使用 ngModel。 支持将 ngModel 输入属性和 ngModelChange 事件与 反应式表单指令已在 Angular v6 中弃用,将被删除 在 Angular 的未来版本中。

有关更多信息,请参阅此处的 API 文档:https://angular.io/api/forms/FormControlDirective#use-with-ngmodel

打字稿 表单 验证 angular-material angular15

评论


答:

0赞 Yong Shun 10/6/2023 #1

你不应该将 Angular Form (NgForm) 与 Reactive Form 混合在一起。

当您将验证规则(例如)设置为 时,验证将仅在 中进行。虽然实例不应用验证,但返回并与 s 的结果冲突。Validators.requiredFormControlFormControlNgFormform.validtrueFormControl

提供了将当前代码迁移到反应式表单的答案。

  1. 从 HTML 中删除。[(ngModel)]

  2. 创建包含多个 s 的实例。FormGroupFormControl

  3. 在元素中添加属性。[formGroup]="form"<form>

  4. 替换为 。[formControl]formControlName

  5. 将所有变量修改为一个 getter 函数,该函数从实例中检索相应的变量。FormControlFormControlformFormGroup

  6. 您可以将该值修补为 via .modelFormGrouppatchValue()

您的最终代码应如下所示:

<form [formGroup]="form" (ngSubmit)="onFormSubmit()" class="py-3">
  <div class="row">
    <mat-form-field class="col">
      <mat-label>First name</mat-label>
      <input
        matInput
        placeholder="Ex: Jhon"
        formControlName="fname"
        [errorStateMatcher]="matcher"
        name="fname"
      />
      <mat-error *ngIf="fnameFormControl.hasError('required')">
        Field is <strong>required</strong>
      </mat-error>
    </mat-form-field>
    <mat-form-field class="col">
      <mat-label>Last name</mat-label>
      <input
        matInput
        placeholder="Ex: Doe"
        formControlName="lname"
        [errorStateMatcher]="matcher"
        name="lname"
      />
      <mat-error *ngIf="lnameFormControl.hasError('required')">
        Field is <strong>required</strong>
      </mat-error>
    </mat-form-field>
  </div>
  
  <div class="row pb-3">
    <mat-form-field class="col">
      <mat-label>Phone</mat-label>
      <input
        matInput
        type="tel"
        class="w-100 form-control"
        formControlName="phone"
        [errorStateMatcher]="matcher"
        name="phone"
      />
      <mat-error *ngIf="phoneFormControl.hasError('required')">
        Field is <strong>required</strong>
      </mat-error>
    </mat-form-field>
  </div>

  <mat-form-field class="form-group w-100">
    <mat-label>Email</mat-label>
    <input
      type="email"
      matInput
      formControlName="email"
      [errorStateMatcher]="matcher"
      name="email"
      placeholder="Ex. [email protected]"
    />
    <!-- <mat-hint>Errors appear instantly!</mat-hint> -->
    <mat-error
      *ngIf="emailFormControl.hasError('email') && !emailFormControl.hasError('required')"
    >
      Please enter a valid email address
    </mat-error>
    <mat-error *ngIf="emailFormControl.hasError('required')">
      Email is <strong>required</strong>
    </mat-error>
  </mat-form-field>

  <mat-form-field class="w-100">
    <mat-label>Choose a breed</mat-label>
    <mat-select
      formControlName="breed"
      [errorStateMatcher]="matcher"
      name="breed"
    >
      <mat-option *ngFor="let breed of breeds" [value]="breed.value">
        {{breed.viewValue}}
      </mat-option>
    </mat-select>
    <mat-error *ngIf="breedSelectFormControl.hasError('required')">
      Please select on option
    </mat-error>
  </mat-form-field>

  <mat-form-field class="w-100">
    <mat-label>Share your story with us</mat-label>
    <textarea
      matInput
      placeholder="Write it here..."
      formControlName="story"
      [errorStateMatcher]="matcher"
      name="story"
    ></textarea>
    <mat-error *ngIf="storyFormControl.hasError('required')">
      Field is <strong>required</strong>
    </mat-error>
  </mat-form-field>

  <div class="form-group pt-3">
    <button class="btn btn-warning btn-block w-100" id="contact-btn">
      Get a free consultation
    </button>
  </div>
</form>
form = new FormGroup({
  email: new FormControl('', [Validators.required, Validators.email]),
  fname: new FormControl('', [Validators.required]),
  lname: new FormControl('', [Validators.required]),
  phone: new FormControl('', [Validators.required]),
  breed: new FormControl('', [Validators.required]),
  story: new FormControl('', [Validators.required]),
});

get emailFormControl() {
  return this.form.controls['email'] as FormControl;
}

get fnameFormControl() {
  return this.form.controls['fname'] as FormControl;
}

get lnameFormControl() {
  return this.form.controls['lname'] as FormControl;
}

get phoneFormControl() {
  return this.form.controls['phone'] as FormControl;
}

get breedSelectFormControl() {
  return this.form.controls['breed'] as FormControl;
}

get storyFormControl() {
  return this.form.controls['story'] as FormControl;
}

ngOnInit() {
  this.form.patchValue(this.model);
}

onFormSubmit() {
  console.log(this.form.valid);
}

演示 @ StackBlitz