【问题标题】:Angular 14 strictly typed reactive forms - How to type FormGroup model using existing interfaceAngular 14 严格类型的反应形式 - 如何使用现有接口键入 FormGroup 模型
【发布时间】:2022-07-01 18:04:12
【问题描述】:

我只是在玩新的 angular typed forms api,如果不声明特定的“FormInterface”女巫必须与原来的匹配,我无法弄清楚如何输入FormGroup

也许我遗漏了一些东西,或者根本不可能这样做。

我能够让事情正常工作(下面的示例),但我不喜欢这里的是 UserForm 接口女巫的声明没有引用 User 接口并且必须是它但带有FormControl<> 字段。

在这种情况下是否可以仅使用User 接口输入FormGroup?。

stackblitz 上提供了完整的工作示例

user.model.ts

export interface User {
  firstName: string;
  lastName: string;
  email: string;
  age: number | null;
}

user.service.ts

@Injectable()
export class UserService {

  add(user: User): void {
    console.log('Add user', user);
  }

}

app.component.ts

interface UserForm {
  firstName: FormControl<string>;
  lastName: FormControl<string>;
  email: FormControl<string>;
  age: FormControl<number | null>;
}

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
})
export class AppComponent implements OnInit {
  form: FormGroup<UserForm>;

  constructor(private userService: UserService) {}

  ngOnInit(): void {
    this.form = new FormGroup({
      firstName: new FormControl<string>('', { nonNullable: true }),
      lastName: new FormControl('', { nonNullable: true }),
      email: new FormControl('', { nonNullable: true }),
      age: new FormControl<number | null>(null),
    });
  }

  submitForm(): void {
    this.userService.add(this.form.getRawValue());
  }
}

【问题讨论】:

  • 我们在 Angular 团队听到你的声音。我们正在考虑在未来的 Angular 版本中减少样板文件数量的方法。现在,您必须声明一个使用控件的单独接口。另一种选择是在声明站点实例化FormGroup,而不是构造函数,并依靠类型推断自动推断类型。

标签: angular strong-typing angular14


【解决方案1】:

您可以尝试对表单变量使用明确的赋值。下面是代码:

form!: FormGroup;

您还可以使用您的声明来初始化表单控件,因为您正在表单上“发布”值并且尚未更新。

form: FormGroup = new FormGroup({
      firstName: new FormControl<string>('', { nonNullable: true }),
      lastName: new FormControl('', { nonNullable: true }),
      email: new FormControl('', { nonNullable: true }),
      age: new FormControl<number | null>(null),
    });

【讨论】:

  • 你是对的,除了你说接口占用资源的部分。它们在转译为 JS 时被完全删除,因此它们根本不占用空间。
  • 声明 form: FormGroup 将阻止类型被推断,因为您将类型声明为只是 FormGroup 而没有类型参数。您应该改用类型推断,而不是指定 : FormGroup
【解决方案2】:

您不能使用User 作为FormGroup 的类型。 但是,您可以使用 enumsUserForm 中引用 User,然后使用 UserForm 作为 FormGroup 的类型。在带有Angular 14 的强类型表单上查看#72500855,因为您的问题可能与它重复。

【讨论】:

    【解决方案3】:

    如 RFC 中所述,

    FormGroup 的泛型类型是一个描述类型的接口 它的每个内部控件。

    也许你可以创建helper 方法来减少样板代码

    function makeNonNullableFormControl<T>(value: T): FormControl<T> {
      return new FormControl(value, {nonNullable:true});
    }
    
    function makeFormControl<T>(value: T | null): FormControl<T | null> {
      return new FormControl(value);
    }
    
    this.form = new FormGroup({
            firstName: makeNonNullableFormControl(''),
            lastName: makeNonNullableFormControl(''),
            email: makeNonNullableFormControl(''),
            age: makeFormControl<number>(null),
    });
    

    【讨论】:

      【解决方案4】:

      比较有效的方法是使用FormBuilder 的类型干扰。

      const form = this.formBuilder.group<User>({ /* your form definition */ });
      

      这将创建一个类型安全的表单,该表单会干扰来自User 接口的控件及其类型。

      例如

      form.patchValue({ age: "15" }); // shows error because `age` has to be a number
      form.patchValue({ password: "myPassword"}); // shows error because `password` does not exist on `User` interface.
      

      但缺少的是 FormGroup 类的简单类型定义,例如当FormBuilder 的分配仅在稍后发生时。 Angular 在FormBuilder.create 中使用的类型定义是

      group<T extends {}>(...): FormGroup<{[K in keyof T]: ɵElement<T[K], null>}>
      

      这不是我想在我的代码中包含的东西,但也许一个简单的帮助器类型已经可以解决这个问题。

      【讨论】:

        猜你喜欢
        • 2023-01-31
        • 1970-01-01
        • 1970-01-01
        • 2019-07-29
        • 2020-11-07
        • 2021-02-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多