【问题标题】:Angular 2 custom input component validation (new form api)Angular 2 自定义输入组件验证(新表单 api)
【发布时间】:2016-12-17 13:32:14
【问题描述】:

我目前正在使用 Angular 2 RC4 和新的表单 API 开发一个相当长的表单。

我有自己的输入组件,想更进一步处理验证。

我正在尝试让我的验证器处理我的输入,但是验证器会更新组件 InputFieldComponent 而不是我的输入字段...

这是我的html:

<label [attr.for]="inputId"><ng-content></ng-content></label>
<small class="text-muted">{{helptext}}</small>
<small [hidden]="(input.valid) || (input.untouched && input.pristine)" class="text-danger">
  {{errormsg}}
</small>
<input
       class="form-control custom-input"
       [id]="inputId"
       [required]="required"
       [type]="type"
       [attr.name]="name"
       [(ngModel)]="value"
       [pattern]="pattern"
       #input="ngModel"
       >

调用
<custom-input-field
  name="birthDate"
  [(ngModel)]="model.birthDate"
  placeholder="DD/MM/YYYY"
  helptext="Date of birth"
  required="true"
  pattern="^[0-3]?[0-9]\/[01]?[0-9]\/[12][90][0-9][0-9]$"
  errormsg="The date of birth is mandatory and should be entered as dd/mm/yyyy"
>
  Date of birth

</custom-input-field>

这是我的代码:

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

const noop = () => {};

let nextUniqueId = 0;

export const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: any = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => InputFieldComponent),
  multi: true
};

@Component({
  moduleId: module.id,
  selector: 'custom-input-field',
  styleUrls: ['input-field.component.css'],
  templateUrl: 'input-field.component.html',
  providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR]
})


export class InputFieldComponent implements ControlValueAccessor {
  //the external properties
  @Input() id: string = `custom-field-${nextUniqueId++}`;
  @Input() helptext: string = null;
  @Input() placeholder: string = null;
  @Input() required: boolean = false;
  @Input() type: string = 'text';
  @Input() name: string = null;

  //The internal data model
  private innerValue: any = '';

  //Placeholders for the callbacks which are later provided
  //by the Control Value Accessor
  private onTouchedCallback: () => void = noop;
  private onChangeCallback: (_: any) => void = noop;

  //get accessor
  get value(): any {
    return this.innerValue;
  };

  //set accessor including call the onchange callback
  set value(v: any) {
    if (v !== this.innerValue) {
      this.innerValue = v;
      this.onChangeCallback(v);
    }
  }

  //From ControlValueAccessor interface
  // The writeValue function allows you to update your internal model with incoming values,
  // for example if you use ngModel to bind your control to data.
  writeValue(value: any) {
    if (value !== this.innerValue) {
      this.innerValue = value;
    }
  }

  //NOTE: Both following functions are later provided by Angular 2 itself. But we need to register dummy functions to be
  // able code and transpile it without errors.

  //From ControlValueAccessor interface
  registerOnChange(fn: any) {
    this.onChangeCallback = fn;
  }

  //From ControlValueAccessor interface
  registerOnTouched(fn: any) {
    this.onTouchedCallback = fn;
  }

  //** Read-only properties */
  get inputId(): string { return `${this.id}-input`; }
};

【问题讨论】:

    标签: angular angular2-forms


    【解决方案1】:

    我终于找到了实现我想要的解决方法。

    • 因为模板变量不能是动态的
    • 而且我很难访问我的组件属性并在 HTML 视图中动态测试它们的值(遇到类似Expression 'xxxx' was changed after it was checked 的问题)

    最终使用自定义组件 shadow dom 来测试组件是否有效并且它可以工作并使代码更轻。

    这是我的自定义输入组件及其 CSS 选择器的代码

    @Component({
      moduleId: module.id,
      selector: 'custom-input-field',
      templateUrl: 'input-field.component.html',
      providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR],
      directives: [DefaultValueAccessor, NgModel],
      host: {
        '(click)': 'focus()',
        '[class.custom-input]': 'true',
      },
    
      /**
       * This styles the input error msg that is integrated into our component using the automatic classes
       * that ngModel updates on the component.
       *
       * It uses the shadow-dom and therefore has to be declared line by line in the component file
       * (does not seem to work with Sass transpiler)
       */
      styles: [
        `
        :host.ng-touched.ng-invalid >>> input {
           border-left: 5px solid #a94442; /* red */
        }
    
        :host.ng-touched.ng-valid >>> input {
            border-left: 5px solid #42A948; /* green */
        }
    
        :host.ng-valid:not([required]).ng-touched.ng-dirty >>> input {
            border-left: 5px solid #42A948; /* green */
        }
    
        :host.ng-pristine >>> .error-msg {
            display:none;
        }
    
        :host.ng-valid >>> .error-msg {
            display:none;
        }
    
        :host.ng-untouched >>> .error-msg {
            display:none;
        }
    
        :host.ng-touched.ng-invalid >>> .error-msg {
           display:inline;
        }
    
        .text-danger { font-weight: 500; }
    }
      `]
    })
    

    这里是模板内容(丰富了更多属性):

    <label [attr.for]="inputId"><ng-content></ng-content></label>
    <small class="text-muted">{{helptext}}</small>
    <small class="error-msg text-danger">
      <ng-content select="input-error"></ng-content>
    </small>
    
    <input #input
           class="form-control custom-input"
           [disabled]="disabled"
           [id]="inputId"
           [attr.list]="list"
           [attr.max]="max"
           [attr.maxlength]="maxLength"
           [attr.min]="min"
           [attr.minlength]="minLength"
           [readonly]="readOnly"
           [required]="required"
           [spellcheck]="spellCheck"
           [attr.step]="step"
           [attr.tabindex]="tabIndex"
           [type]="type"
           [attr.name]="name"
           (focus)="_handleFocus($event)"
           (blur)="_handleBlur($event)"
           (change)="_handleChange($event)"
           [(ngModel)]="value"
    >
    

    我这样称呼我的组件:

    <custom-input-field
                  name="birthDate"
                  [(ngModel)]="model.birthDate"
                  placeholder="JJ/MM/AAAA"
                  helptext="format jj/mm/aaaa"
                  #birthDate="ngModel"
                  required="true"
                  pattern="^[0-3]?[0-9]\/[01]?[0-9]\/[12][90][0-9][0-9]$"
                >
                  Date of birth
                  <input-error><br />The date of birth is mandatory and should be entered as dd/mm/yyyy</input-error>
                </custom-input-field>
    

    【讨论】:

      猜你喜欢
      • 2017-09-14
      • 2016-04-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-16
      • 1970-01-01
      • 2018-01-26
      • 2017-08-28
      相关资源
      最近更新 更多