angular自定义form拿不到值_理解 Angular 中的 ControlValueAccessor 接口

50d64e6db41d5c31df878c807b4b7f5a.png
Understanding Angular's Control Value Accessor Interface​dev.to
2f99ea8f89bcbd7460836cc7f54a8188.png
原作者:Jennifer Wadella​dev.to

译者:

知乎用户​www.zhihu.com
zhihu-card-default.svg

如果你经常使用 Angular form 进行表单内容处理,了解并学会 ControlValueAccessor 接口的使用技巧会对你大有裨益。ControlValueAccesor 接口是 DOM 元素与 FormControl 之间的桥梁。拓展了 ControlValueAccessor 接口的组件可以创建属于自己的自定义 FormControl,这些自定义的 FromControl 拥有与常规的 input/radio button 同样的行为表现(内部实现一致)。

为什么需要使用 ControlValueAccessor

有时,你可能需要创建自定义的表单元素,并将其作为常规的 FormControl 使用。如果你尚不了解 Angular FormControl 及其他 Angular Form 类,可以阅读我的另一篇文章。

比如,你想创建一个 5星评级的 UI,更新模型中的一个变量。下面我们就将使用这样的例子作为demo。

fc798f482b9942a9836ea35f618bc180.gif

上面的功能展示图中包含了许多内容 - 悬浮于星星上时其颜色发生了改变,并且对不同的评分会展示不同的文本信息,而我们关注的是背后的 rating value 的变化。

实现 ControlValueAccseeor

在一个组件中拓展 ControlValueAccessor 接口需要实现三个方法:writeValue,registerOnChangeregisterOnTouched。除此之外,还有一个名为 setDisabledState 的方法可选实现。

writeValue 函数在以下两种场景下被调用:

  • formControl 被初始化时
rating = new FormControl({value: null, disabled: false})
  • formControl 的值发生变化时:
rating.patchValue(3)

每当值发生变化时,registerOnChange 方法都会被触发。在我们的示例中,当星星被点击时,registerOnChange 函数就会被触发。

每当 UI 的部分发生交互时(比如 blur 事件),registerOnChange 函数都会被调用。如果你曾经使用过 BootStrap 或者 NGX-Bootstrap 的话,比较熟悉 onBlur 方法的你此时会有一些既视感。

setDisabledState 方法在以下两种场景下被调用:

  • formControl 初始化时使用了 disabled 的属性
rating = new FormControl({value: null, disabled: false})
  • formControl 的 disabled 状态发生了变化
rating.disable();
rating.enable();

对示例的评分组件实现 ControlValueAccessor 代码如下所示:

export class StarRaterComponent implements ControlValueAccessor {
  public ratings = [
    {
      stars: 1,
      text: 'must GTFO ASAP'
    },
    {
      stars: 2,
      text: 'meh'
    },
    {
      stars: 3,
      text: 'it's ok'
    },
    {
      stars: 4,
      text: 'I'd be sad if a black hole ate it'
    },
    {
      stars: 5,
      text: '10/10 would write review on Amazon'
    }
  ]
  public disabled: boolean;
  public ratingText: string;
  public _value: number;

  onChanged: any = () => {}
  onTouched: any = () => {}

  writeValue(val) {
    this._value = val;
  }

  registerOnChange(fn: any){
    this.onChanged = fn
  }
  registerOnTouched(fn: any){
    this.onTouched = fn
  }

  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

  setRating(star: any) {
    if(!this.disabled) {
      this._value = star.stars;
      this.ratingText = star.text
      this.onChanged(star.stars);
      this.onTouched();
    }
  }

}

在此之后,你必须告诉 Angular 组件实现的 ControlValueAccessor 是一个使用 NG_VALUE_ACCESSORforwardRefvalue accessor(注意接口不会被 Typescript 编译)

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

@Component({
  selector: 'gr-star-rater',
  templateUrl: './star-rater.component.html',
  styleUrls: ['./star-rater.component.less'],
  providers: [     
    {
      provide: NG_VALUE_ACCESSOR, 
      useExisting: forwardRef(() => StarRaterComponent),
      multi: true     
    }   
  ]
})
export class StarRaterComponent implements ControlValueAccessor {
...

使用新建的 ControlValueAccessor 组件

现在,就可以像使用常规的 FormControl 一样使用新的 ControlValueAccessor 组件了。

this.galaxyForm = new FormGroup({
  rating: new FormControl({value: null, disabled: true})
});
<form [formGroup]="galaxyForm" (ngSubmit)="onSubmit()">
  <h1>Galaxy Rating App</h1>
  <div class="form-group">
    <label>
      Rating:
      <gr-star-rater formControlName="rating"></gr-star-rater>
    </label>
  </div>
  <div class="form-group">
    <button type="submit">Submit</button>
  </div>
</form>

其实 ControlValueAccessor 并不复杂,希望你可以从本文中获得些许知识。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值