Angular 2 - 将表单验证移至指令

Angular 2 - Move form validation to directive

我想将表单验证移至指令,并在表单中的所有字段中保留 ngForm 验证。 在以下示例中,我将以下验证 (1. Works) 的输入文本字段移至指令。但是当我将它移动到指令时,模型更新正确,但 form.valid 操作不受指令影响。 表单无效,因为输入文本没有值。当它被赋予一个值时,按钮被激活并且表单有效。

1.作品:

    <form (ngSubmit)="onSubmit()" #aliasForm="ngForm">

                        <div class="form-group">
                            <label for="firstname">Fornavn</label>
                            <input type="text" class="form-control" id="firstname"
                                   required
                                   [(ngModel)]="model.primaryAlias.firstName" name="firstname"
                                   #name="ngModel">
                            <div [hidden]="name.valid || name.pristine"
                                 class="alert alert-danger">
                                Fornavn er påkrævet
                            </div>
                        </div>


                        <div class="form-group col-sm-6">
                            <p-dropdown [style]="{'width':'150px'}" [options]="genders" [(ngModel)]="model.primaryAlias.gender" name="genders" [required]="true"></p-dropdown>
                        </div>

优化

2。不使用指令(p-输入):

<form (ngSubmit)="onSubmit()" #aliasForm="ngForm">
<p-input [(value)]="model.primaryAlias.firstName" directiveLabel="Fornavn"></p-input>
<div class="form-group">
    <p-dropdown [style]="{'width':'150px'}" [options]="genders" [(ngModel)]="model.primaryAlias.gender" name="genders" [required]="true"></p-dropdown>
</div>
<button type="submit" class="btn btn-success pull-right" [disabled]="!aliasForm.form.valid">Opret</button></form>

p-input.ts:

p-input.html:

<div class="form-group">
<label for="firstname">Fornavn</label>
<input type="text" class="form-control" id="firstname"
       required
       [(ngModel)]="value" (ngModelChange)="onChange($event)" ngControl="value" name="firstname"
       #name="ngModel">
<div [hidden]="name.valid || name.pristine"
     class="alert alert-danger">
    Fornavn er påkrævet
</div>

如何将验证移至指令并保持表单处理,使整个表单完好无损?

我会为您的自定义指令实施 ControlValueAccessor,例如:

p-input.ts

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

@Component({
  selector: 'p-input',
  template: `
    <div class="form-group">
      <label for="firstname">{{directiveLabel}}</label>
      <input type="text" class="form-control" id="firstname"
             required
             [(ngModel)]="value" 
             (ngModelChange)="onModelChange($event)" #name="ngModel">
      <div [hidden]="name.valid || name.pristine"
           class="alert alert-danger">
        Fornavn er påkrævet
      </div>
    </div>
  `,
  providers: [INPUT_VALUE_ACCESSOR]
})
export class InputTestComponent implements ControlValueAccessor {
  onChange = (_: any) => {};
  onTouched = () => {};

  value: string;
  @Input() directiveLabel: string;


  @ViewChild('name') inputRef: ElementRef;

  constructor(private renderer: Renderer2) {}

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

  onModelChange(newValue: string) {
    this.value = newValue;
    this.onChange(this.value);
  }

  writeValue(val: any): void {
    this.value = val;
  }

  setDisabledState(isDisabled: boolean): void {
    this.renderer.setProperty(this.inputRef.nativeElement, 'disabled', isDisabled);
  }
}

parent.html

<p-input required 
  name="firstName" [(ngModel)]="model.primaryAlias.firstName" directiveLabel="Fornavn">
</p-input>

Stackblitz Example

或手动向您的表单添加内部输入控件:

p-input.ts

@Component({
  selector: 'p-input',
  template: `
    <div class="form-group">
      <label for="firstname">{{directiveLabel}}</label>
      <input type="text" class="form-control" id="firstname"
             required
             [(ngModel)]="value" 
             (ngModelChange)="onChange($event)" name="firstname"
             #name="ngModel">
      <div [hidden]="name.valid || name.pristine"
           class="alert alert-danger">
        Fornavn er påkrævet
      </div>
    </div>
  `
})
export class InputTestComponent {
  @Input() value: string;
  @Input() directiveLabel: string;

  @Output() valueChange: EventEmitter<string> = new EventEmitter();

  @ViewChild(NgControl) inputControl: NgControl;

  constructor(@Optional() private controlContainer: ControlContainer) {}

  ngOnInit() {
    if(this.controlContainer) {
      this.controlContainer.formDirective.addControl(this.inputControl);
    }
  }

  onChange(newValue: string) {
    this.value = newValue;
    this.valueChange.emit(this.value);
  }
}

parent.html

<p-input [(value)]="model.primaryAlias.firstName" directiveLabel="Fornavn"></p-input>

Stackblitz Example