Angular 2自定义表单输入

Maksim Fomin:

如何创建可以像本机<input>标记一样工作的自定义组件我想使我的自定义窗体控件能够支持ngControl,ngForm,[(ngModel)]。

据我了解,我需要实现一些接口以使自己的表单控件像本机控件一样工作。

另外,似乎ngForm指令仅绑定<input>标签,对吗?我该如何处理?


让我解释一下为什么我完全需要这个。我想包装几个输入元素,使它们能够作为一个输入一起工作。还有其他方法可以解决吗?再过一次:我想像本地控件一样进行此控件。验证时,ngForm,ngModel两种方式绑定等。

ps:我用打字稿。

蒂埃里·坦维尔(Thierry Templier):

实际上,有两件事要实现:

  • 提供表单组件逻辑的组件。它不会因为它的输入将被提供ngModel自身
  • 一个ControlValueAccessor将实现此组件和ngModel/ 之间的桥梁的自定义ngControl

让我们取样。我想实现一个管理公司标签列表的组件。该组件将允许添加和删除标签。我想添加一个验证以确保标签列表不为空。我将在组件中定义它,如下所述:

(...)
import {TagsComponent} from './app.tags.ngform';
import {TagsValueAccessor} from './app.tags.ngform.accessor';

function notEmpty(control) {
  if(control.value == null || control.value.length===0) {
    return {
      notEmpty: true
    }
  }

  return null;
}

@Component({
  selector: 'company-details',
  directives: [ FormFieldComponent, TagsComponent, TagsValueAccessor ],
  template: `
    <form [ngFormModel]="companyForm">
      Name: <input [(ngModel)]="company.name"
         [ngFormControl]="companyForm.controls.name"/>
      Tags: <tags [(ngModel)]="company.tags" 
         [ngFormControl]="companyForm.controls.tags"></tags>
    </form>
  `
})
export class DetailsComponent implements OnInit {
  constructor(_builder:FormBuilder) {
    this.company = new Company('companyid',
            'some name', [ 'tag1', 'tag2' ]);
    this.companyForm = _builder.group({
       name: ['', Validators.required],
       tags: ['', notEmpty]
    });
  }
}

TagsComponent组件定义了添加和删除tags列表中元素的逻辑

@Component({
  selector: 'tags',
  template: `
    <div *ngIf="tags">
      <span *ngFor="#tag of tags" style="font-size:14px"
         class="label label-default" (click)="removeTag(tag)">
        {{label}} <span class="glyphicon glyphicon-remove"
                        aria-  hidden="true"></span>
      </span>
      <span>&nbsp;|&nbsp;</span>
      <span style="display:inline-block;">
        <input [(ngModel)]="tagToAdd"
           style="width: 50px; font-size: 14px;" class="custom"/>
        <em class="glyphicon glyphicon-ok" aria-hidden="true" 
            (click)="addTag(tagToAdd)"></em>
      </span>
    </div>
  `
})
export class TagsComponent {
  @Output()
  tagsChange: EventEmitter;

  constructor() {
    this.tagsChange = new EventEmitter();
  }

  setValue(value) {
    this.tags = value;
  }

  removeLabel(tag:string) {
    var index = this.tags.indexOf(tag, 0);
    if (index != undefined) {
      this.tags.splice(index, 1);
      this.tagsChange.emit(this.tags);
    }
  }

  addLabel(label:string) {
    this.tags.push(this.tagToAdd);
    this.tagsChange.emit(this.tags);
    this.tagToAdd = '';
  }
}

如您所见,此组件中没有输入,只有setValue一个(这里的名称并不重要)。我们稍后将使用它来提供从ngModel到组件的值该组件定义一个事件以通知组件(标签列表)的状态何时更新。

现在让我们实现此组件和ngModel/ 之间的链接ngControl这对应于实现该ControlValueAccessor接口的指令必须根据NG_VALUE_ACCESSOR令牌为此值访问器定义一个提供程序(不要忘记使用forwardRef该提供程序,因为该指令是在之后定义的)。

该指令将tagsChange在主机的事件上附加一个事件侦听器(即,该指令所附加的组件,即TagsComponent)。onChange事件发生时将调用方法。此方法对应于Angular2注册的方法。这样,它将知道更改并相应地更新关联的表单控件。

当中writeValue绑定的值ngForm更新时,将调用注入附加的组件(即,TagsComponent)后,我们将能够调用它以传递此值(请参见前面的setValue方法)。

不要忘记CUSTOM_VALUE_ACCESSOR在指令的绑定中提供

这是自定义的完整代码ControlValueAccessor

import {TagsComponent} from './app.tags.ngform';

const CUSTOM_VALUE_ACCESSOR = CONST_EXPR(new Provider(
  NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => TagsValueAccessor), multi: true}));

@Directive({
  selector: 'tags',
  host: {'(tagsChange)': 'onChange($event)'},
  providers: [CUSTOM_VALUE_ACCESSOR]
})
export class TagsValueAccessor implements ControlValueAccessor {
  onChange = (_) => {};
  onTouched = () => {};

  constructor(private host: TagsComponent) { }

  writeValue(value: any): void {
    this.host.setValue(value);
  }

  registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
  registerOnTouched(fn: () => void): void { this.onTouched = fn; }
}

这样,当我删除tags公司的所有内容时控件valid属性companyForm.controls.tagsfalse自动变为

有关更多详细信息,请参见本文(“与NgModel兼容的组件”一节):

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章