如何解决 Angular 中表单控件名称的无值访问器?

How to resolve No value accessor for form control name in Angular?

提问人:Jinglemahn 提问时间:10/18/2023 最后编辑:Jinglemahn 更新时间:10/18/2023 访问量:197

问:

我正在尝试实现以下目标,我可以将组件包装在标签中,在那里我可以使用 Angular 和 StorybookJS 动态绑定到 TestField 输入,如下所示。<app-test-field><form>formControlNameformGroup

但是,我遇到了以下错误:

  • core.mjs:11483 错误错误:NG01203:表单控件名称没有值访问器:“name”。
  • core.mjs:11483 错误错误:NG01050:formControlName 必须与父 formGroup 指令一起使用。您需要添加一个 formGroup 指令,并向其传递一个现有的 FormGroup 实例(您可以在类中创建一个)。

TestField.stories.js

这是我在 StorybookJS 中的 Angular 组件的模板。

<form [formGroup]="sampleForm">
  <!-- name input -->
  <app-test-field [label]="label" [formControlName]="formControlName"></app-test-field>  
  <!-- email input... -->  
  <!-- more inputs... --> 
</form>

test-field.component.html

<div class="form-group">
  <label for="{{ formControlName }}">{{ label }}</label>
  <input
    type="text"
    class="form-control"
    [formControlName]="formControlName"
    [id]="formControlName"
  /><br />
</div>

test-field.component.ts

import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-test-field',
  templateUrl: './test-field.component.html',
  styleUrls: ['./test-field.component.css'],
})
export class TestFieldComponent {
  @Input() label: string = '';
  @Input() formControlName: string = '';
}

我在网上研究了大多数示例,其中输入本身被组件本身中的标签包裹起来,如下所示:<form>

<form [formGroup]="registerForm">
  <div class="form-group">
    <label>Name</label>
    <input type="text" class="form-control" [ngClass]="inputDanger" formControlName="name"><br/>
    </span>
    <br/>
  </div>
</form>

但是我想删除标签,以便最终目标如下,其中输入本身就是一个包装在标签中的组件,如下所示:form<form>

<form [formGroup]="sampleForm">
  <!-- name input -->
  <app-test-field [label]="label" [formControlName]="formControlName"></app-test-field>  
  <!-- email input... -->  
  <!-- more inputs... --> 
</form>

有人可以帮我解决这个问题吗?有什么遗漏吗?

Angular Forms 输入 故事书

评论

0赞 Chintan Dhokai 10/18/2023
levelup.gitconnected.com/......我认为这对你很有帮助。
1赞 Jinglemahn 10/18/2023
谢谢你的链接!会读一读。这是一篇需要会员费才能访问此内容的文章。你也是一样的情况吗?
0赞 Eliseo 10/18/2023
或者制作一个自定义窗体控件或像这样使用 ViewProvider - 例如,在很多 -
0赞 Chintan Dhokai 10/18/2023
我已经添加了解决方案,您可以检查并让我知道是否有任何问题吗?
1赞 Pieterjan 10/18/2023
下面是值访问器的示例

答:

0赞 Sara 10/18/2023 #1

要在没有包装形式的单独组件中使用控件,您可以使用 Angular 的 NgControl

以下是该问题情况下的用法示例:

test-field.component.ts

ngControl 在构造函数中传递,并具有一些必需的函数

import { Component, Input, Optional, Self } from '@angular/core';
import { NgControl } from '@angular/forms';

@Component({
  selector: 'app-test-field',
  templateUrl: './test-field.component.html',
  styleUrls: ['./test-field.component.scss'],
})
export class TestFieldComponent {
  @Input() label: string = '';

  onChange: any = () => {};
  onTouch: any = () => {};

  constructor(@Optional() @Self() public ngControl: NgControl) {
    if (this.ngControl != null) {
      this.ngControl.valueAccessor = this;
    }
  }

  ngOnInit(): void {}

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouch = fn;
  }

  writeValue(value: any): void {}
}

现在,在 test-field.component.html 中使用 ngControl:

<div class="form-group">
  <label for="{{ ngControl?.name }}">{{ label }}</label>
  <input
    type="text"
    class="form-control"
    [formControl]="ngControl?.control"
    [id]="ngControl?.name"
  /><br />
</div>

TestField.stories.js中,您只能删除“formControlName”输入,因为不再需要它:

<form [formGroup]="sampleForm">
  <!-- name input -->
  <app-test-field [label]="label"></app-test-field>  
  <!-- email input... -->  
  <!-- more inputs... --> 
</form>

评论

0赞 Jinglemahn 10/18/2023
在我的组件 html 表单中,我得到.Type 'AbstractControl<any, any> | null | undefined' is not assignable to type 'FormControl<any>'
1赞 Chintan Dhokai 10/18/2023 #2

为您的组件创建一个:ControlValueAccessorapp-test-field

import { Component, Input, forwardRef, NG_VALUE_ACCESSOR } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'app-test-field',
  templateUrl: './test-field.component.html',
  styleUrls: ['./test-field.component.css'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => TestFieldComponent),
      multi: true,
    },
  ],
})
export class TestFieldComponent implements ControlValueAccessor {
  @Input() label: string = '';
  @Input() formControlName: string = '';

  // Implement ControlValueAccessor methods
  onChange: any = () => {};
  onTouched: any = () => {};

  writeValue(value: any) {}
  registerOnChange(fn: any) {
    this.onChange = fn;
  }
  registerOnTouched(fn: any) {
    this.onTouched = fn;
  }
}

更新 ur 以使用指令:test-field.component.htmlngModel

<div class="form-group">
  <label for="{{ formControlName }}">{{ label }}</label>
  <input
    type="text"
    class="form-control"
    [id]="formControlName"
    [ngModel]="value"
    (ngModelChange)="onChange($event)"
  /><br />
</div>

现在,在父组件中,当您使用 时,它应该按预期工作:app-test-field

<form [formGroup]="sampleForm">
  <app-test-field [label]="label" [formControlName]="formControlName"></app-test-field>
</form>