自定义Angular2 Validator仅在页面加载时计算,而不在对象更新时计算

时间:2016-12-21 18:17:49

标签: validation angular typescript

我尝试使用标记验证表单,其中列表必须包含至少一个有效的标记。但它只评估页面何时加载,而不是更新。

https://plnkr.co/edit/umnhybKhNEjswrUJGh3q?p=preview

验证功能:

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

  return null;
}

使用验证器的组件和模板:

@Component({
  selector: 'my-app',
  template: `
    <form [formGroup]="myForm">
    <div>
    (Comma Separated, no duplicates allowed. Enter also submits a tag.)
    <div tags formControlName="list" [(ngModel)]="list"> </div>
       <div *ngIf="myForm.get('list').valid">List 1 Not Empty.</div>
    <div tags formControlName="list2" [(ngModel)]="list2"> </div>
       <div *ngIf="myForm.get('list2').valid">List 2 Not Empty.</div>
    </div>
    </form>
  `,
})
export class App {
  list:Array<string>;
  list2:Array<string>;
  myForm:FormGroup;
  myList:FormControl;
  myList2:FormControl;
  constructor(private fb: FormBuilder) {
    this.list = [];
    this.list2 = ["test"];
    this.myList = fb.control('', notEmpty);
    this.myList2 = fb.control('', notEmpty);
    this.myForm = fb.group({
      list: this.myList,
      list2: this.myList2
      });
  }

  addItem(item:string) {
    this.list.push(item);
  }
}

标记组件和其他子组件:

const MY_PROVIDER = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(()=> Tags),
    multi: true
  };

@Component({
  selector: 'tags, [tags]',
  template: ` 
  <div>
    <tag-item *ngFor="let item of tagsList" item="{{item}}" (remove)="removeTag(item)"></tag-item>
    <input class="tagInput" #tagInput
         (focus)="focus()"
         [(ngModel)]="current"
         (keydown)="keyDown($event)"
         (keyup)="keyUp($event)"
         (blur)="blur()"
         placeholder="+ Tag"/>
  </div>
  `,
  providers: [MY_PROVIDER]
})
export class Tags implements ControlValueAccessor {
  tagsList : Array<string>;
  current : string;
  @ViewChild('tagInput') child;
  inFocus : boolean = false;

  constructor() {
    this.current = "";
    this.tagsList = new Array<string>();
  }

  focus() {
    this.child.nativeElement.focus();
    this.inFocus = true;
  }

  keyDown(event:KeyboardEvent) {
    if (event.keyCode === 188 || event.keyCode === 13) { //188 is Comma, 13 is Enter, 32 is Space.
      this.pushTag();
    }  else if (event.keyCode === 8 && this.current.length == 0 && this.tagsList.length > 0){
      this.current = this.tagsList.pop();
    }
  }

  keyUp(event:KeyboardEvent) {
    if(event.keyCode === 188) {
      this.current = '';
    }
  }

  pushTag() {
    let str = this.current;
    this.current = '';
    if(str.trim() != '') {
      for(let s of str.split(',')) {
        s = this.sanitize(s);
        if(s.trim() != '') {
          if(!this.tagsList.some(x => x.toLowerCase() === s.toLowerCase()))
            this.tagsList.push(s);
        }
      }
    }
  }

  sanitize(str: string) : string {
    let s = str;
    s = s.replace('\'', '').replace('"', '').replace(';', '');
    return s;
  }

  blur() {
    this.pushTag();
    this.inFocus = false;
  }

  removeTag(value) {
    let index = this.tagsList.indexOf(value, 0);
    if (index > -1) {
       this.tagsList.splice(index, 1);
     }
  }

  clear() {
    this.tagsList = new Array<string>();
  }

    get value(): Array<string> { return this.tagsList; };
    set value(v: Array<string>) {
      if (v !== this.tagsList) {
        this.tagsList = v;
        this.onChange(v);
        this.onTouched();
      }
    }

    writeValue(value: Array<string>) {
      this.tagsList = value;
      this.onChange(value);
    }

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

@Component({
  selector: 'tag-item, [tag-item]',
  template: `{{item}}   <delete-me (click)="removeTag(item)">x</delete-me>`
})
export class TagItem {
  @Input() item : string;
  @Output() remove : EventEmitter<string> = new EventEmitter();

  removeTag(item) {
    this.remove.emit(item);
  }
}

@Component({
  selector:'delete-me',
  template:'x'
})
export class DeleteIcon {

}

1 个答案:

答案 0 :(得分:0)

这似乎有效。

...//Tags Component pushMethod
pushTag() {
    let str = this.current;
    this.current = '';
    if(str.trim() != '') {
      for(let s of str.split(',')) {
        s = this.sanitize(s);
        if(s.trim() != '') {
          if(!this.tagsList.some(x => x.toLowerCase() === s.toLowerCase())) {
            this.tagsList.push(s);
            this.pushed.emit(s); // created an EventEmitter<string>
          }
        }
      }
    }
  }

在我的主要部分中:

@Component({
  selector: 'my-app',
  template: `
    <form [formGroup]="myForm">
    <div>
    (Comma Separated, no duplicates allowed. Enter also submits a tag.)
    <div tags formControlName="list" (pushed)="update()" [(ngModel)]="list"> </div>
       <div *ngIf="myForm.get('list').valid">List 1 Not Empty.</div>
    <div tags formControlName="list2" (pushed)="update()" [(ngModel)]="list2"> </div>
       <div *ngIf="myForm.get('list2').valid">List 2 Not Empty.</div>
    </div>
    </form>
  `,
})
export class App {
  list:Array<string>;
  list2:Array<string>;
  myForm:FormGroup;
  myList:FormControl;
  myList2:FormControl;
  constructor(private fb: FormBuilder) {
    this.list = [];
    this.list2 = ["test"];
    this.myList = fb.control('', notEmpty);
    this.myList2 = fb.control('', notEmpty);
    this.myForm = fb.group({
      list: this.myList,
      list2: this.myList2
      });
  }

  update() {
    this.myList.updateValueAndValidity();
    this.myList2.updateValueAndValidity();
  }

  addItem(item:string) {
    this.list.push(item);
  }
}
但是,看起来有点像hacky,所以如果有的话,我仍然会想要一个更好的答案。