2017-03-16 146 views
1

我试图强制验证基于一个字段的变化到FormGroup内的另一个字段内的FormArray的多个实例的这个组。我在其中一个字段上使用mydatepicker。例如,如果更改日期,那么我希望该组中的“更改原因”字段仅用于验证,以确保未选择第一个选项(值为0)。我有两个问题:angular2 formarray有条件验证

  1. 当我更改日期时,更改原因字段不会立即检查有效性。它只发生在我将字段的值更改为1然后更改为0.它的默认值设置为0,并且它应该在更改日期时立即选取此字段。
  2. 当它最终意识到窗体无效时,它会为所有更新按钮执行此操作,而不仅仅是FormGroup中Date窗口已更改的窗体。

TS文件代码:

subscribeDateChange(formGroup){ 
(<any>this.rfcActionTasksForm).controls.tasks.controls[0].controls['DueDate']; 
const tasks = formGroup; 


const changes$ = tasks.controls['DueDate'].valueChanges; 



changes$.subscribe(dd => { 
    var arrayControl = this.rfcActionTasksForm.get('tasks') as FormArray; 
    var item = arrayControl.at(1); 
    console.log(item); 
    if(tasks.value['ReasonForChangeId'] == '0'){ 

    tasks.controls['ReasonForChangeId'].setValidators(Validators.pattern(/([1-9])/)); 

    } 


}); 
} 

ngOnInit() { 
this.rfcActionTasksForm = this._fb.group({ 
    tasks: this._fb.array([this.buildTask()]) 

}); 


} 
buildTask(): FormGroup { 
    return this._fb.group({ 
     Id: '', 
     Action: ['', Validators.required], 
     Step: '', 
     AssignedToId: ['', Validators.required], 
     AssignedToColour: '', 
     DueDate: ['', Validators.required], 
     ReasonForChangeId: '', 
     OriginalDueDate: '', 
     Completed: '', 
     OverDue: '' 
    },{ 
    validator: (formGroup: FormGroup) => { 
     //return this.validateDays(formGroup); 
     //console.log(formGroup.controls['DueDate']); 
     //this.subscribeDateChange(formGroup.controls['DueDate'], formGroup.controls['ReasonForChangeId']); 
    return this.subscribeDateChange(formGroup); 
    } 
    }); 

} 

HTML:

<form class="multi-col implementation" *ngIf="rfc.Plan [formGroup]="rfcActionTasksForm"> 
    <div class="task-item" formArrayName="tasks" *ngFor="let task of tasks.controls; let i = index"> 
    <div [formGroupName]="i"> 
     <div class="row header-row"> 
      <div class="col-md-12"> 
      <h5 class="no-margin">Step {{i + 1}}</h5> 
      <input 
       [style.display]="'none'" 
       formControlName="Step"> 
      <input 
       [style.display]="'none'" 
       formControlName="AssignedToColour"> 
      <input 
       [style.display]="'none'" 
       formControlName="Id"> 
      </div> 
     </div> 
     <div class="input-row row"> 
      <div class="col-md-11"> 
      <label for="{{'task' + i}}">Action:</label> 
       <div 
        [ngClass]="{'has-error': (tasks.get(i + '.Action').touched || tasks.get(i + '.Action').dirty) && !tasks.get(i + '.Action').valid }"> 
       <textarea 
       rows="6" 
       id="{{'task' + i}}" 
       formControlName="Action"></textarea> 

       <span class="help-block" *ngIf="(tasks.get(i + '.Action').touched || tasks.get(i + '.Action').dirty) && tasks.get(i + '.Action').errors"> 
         <span *ngIf="tasks.get(i + '.Action').errors.required"> 
          Please enter a title. 
         </span> 
        </span> 

       </div> 
      </div> 

      <div class="col-md-1 text-center"> 
      <label>Status</label> 
      <i *ngIf="tasks.get(i + '.Completed').value" class="glyphicon glyphicon-ok-sign ok" title="Completed"></i> 
      <i *ngIf="!tasks.get(i + '.Completed').value && !tasks.get(i + '.OverDue').value" class="glyphicon glyphicon-minus-sign pending" title="In progress"></i> 
      <i *ngIf="!tasks.get(i + '.Completed').value && tasks.get(i + '.OverDue').value" class="glyphicon glyphicon-exclamation-sign text-danger" title="Overdue!"></i> 
      </div> 

     </div> 
     <div class="input-row row"> 
     <div class="col-md-3 assigned-to"> 
      <div 
       [ngClass]="{'has-error': (tasks.get(i + '.AssignedToId').touched || tasks.get(i + '.AssignedToId').dirty) && !tasks.get(i + '.AssignedToId').valid }"> 
       <label for="{{'assignedTo' + i}}">Assigned to:</label> 
       <div class="color-block" [style.background]="tasks.get(i + '.AssignedToColour').value"></div> 
       <label class="fa select"> 
       <select 
        *ngIf="users" 
        id="{{'assignedTo' + i}}" 
        formControlName="AssignedToId"> 
        <option 

        *ngFor="let user of users | trueValueFilter: 'IsActive'" 
        [value]="user.Id">{{user.Name}}</option> 
       </select> 
       </label> 

       <span class="help-block" *ngIf="(tasks.get(i + '.AssignedToId').touched || tasks.get(i + '.AssignedToId').dirty) && tasks.get(i + '.AssignedToId').errors"> 
        <span *ngIf="tasks.get(i + '.AssignedToId').errors.required"> 
         Please select a user. 
        </span> 
       </span> 

      </div> 
     </div> 
     <div class="col-md-3"> 
      <div 
       [ngClass]="{'has-error': (tasks.get(i + '.DueDate').touched || tasks.get(i + '.DueDate').dirty) && !tasks.get(i + '.DueDate').valid }"> 
       <label for="{{'dueDate' + i}}">Due date:</label> 
       <my-date-picker 
        class="datepicker" 
        type="text" 
        id="{{'dueDate' + i}}" 

        formControlName="DueDate" 
        [options]="myDatePickerOptions"></my-date-picker> 

        <span class="help-block" *ngIf="(tasks.get(i + '.DueDate').touched || tasks.get(i + '.DueDate').dirty) && tasks.get(i + '.DueDate').errors"> 
        <span *ngIf="tasks.get(i + '.DueDate').errors.required"> 
         Please set a due date. 
        </span> 
        </span> 

      </div> 
     </div> 
     <div class="col-md-2"> 


      <label for="{{'reason' + i}}">Reason for change:</label> 
      <label class="fa select"> 
       <select 
       class="reason-select" 
       *ngIf="reasons" 
       id="{{'reason' + i}}" 
       formControlName="ReasonForChangeId"> 
       <option 

        *ngFor="let reason of reasons" 
        [value]="reason.Id">{{reason.Reason}}</option> 
       </select> 
      </label> 


     </div> 
     <div class="col-md-3"> 
      <div class="text-center"> 
       <label for="{{'OriginalDueDate' + i}}">Original due date:</label> 
       <span>{{tasks.get(i + '.OriginalDueDate').value | dateToStringFilter}}</span> 
       <input 
       readonly 
       type="text" 
       id="{{'OriginalDueDate' + i}}" 
       [style.display]="'none'" 
       formControlName="OriginalDueDate"> 

      </div> 
     </div> 
     <div class="col-md-1"> 
      <div class="text-center"> 
       <label for="{{'completed' + i}}">Completed:</label> 

        <div class="checkbox-group"> 
        <input type="checkbox" 
         type="checkbox" 
         id="{{'completed' + i}}" 
         formControlName="Completed"> 
        <label class="checkbox" for="{{'completed' + i}}"></label> 

       </div> 

      </div> 
     </div> 
     </div> 

     <div class="row"> 
     <div class="col-md-12"> 
      <div> 

      <button 
       class="glyphicon glyphicon-plus-sign btn-icon add" 
       title="Insert task after this one" 
       (click)="insertTaskField(i)"></button> 

      <button 
       class="glyphicon glyphicon-remove-sign btn-icon delete" 
       title="Delete this task" 
       (click)="removeTaskField(i, tasks.get(i + '.Id')?.value)"></button> 

       <button 
       *ngIf="!tasks.get(i + '.Id').value" 
       (click)="saveNewTask(rfc.Id, i);" 
       [disabled]="!rfcActionTasksForm.valid" 
       class="pull-right">Save new</button> 
       <button 
       *ngIf="tasks.get(i + '.Id')?.value" 
       [disabled]="!rfcActionTasksForm.valid" 
       (click)="updateTask(i, tasks.get(i + '.Id')?.value)" 
       class="pull-right">Update</button> 

      </div> 
     </div> 
     </div> 
    </div> 
    </div> 
    <div class="row last"> 
    <div class="col-md-12"> 
     <button 
     class="pull-right" 
     [disabled]="enableUpdateAll === false" 
     (click)="reOrderTasks()">Update All</button> 
    </div> 
    </div> 

</form> 

回答

0

我看到多个问题在这里:

一个验证应该是这样的形式:

(control: AbstractControl): {[key: string]: any} => { 
    if(isValid(control)) 
     return null; 
    else 
     return {"myValidator":"invalid thing detected"}; 
} 

您每次都退回undefined,因此无法工作。 您无法订阅,因为这意味着每次您在表单中进行更改时,您都会重新订阅整个组的价值变化,这是无意义的。

忘记valueChanges,你需要同步做你的控制。检查是否有错误,并使用子控件的setError()方法。

类似:

(group: FromGroup): {[key: string]: any} => { 
    if(!isValid(group)){ 
     group.get("myChildControl").setErrors({"localError":"error detected !"}); 
     return {"groupError":"error detected !"}; 
    } 
    return null; 
} 

我不知道,如果你要使用的setError(errors,{emitEvent:false})第二个参数,以避免传播与否。

+0

谢谢。这个验证功能需要在哪里准确添加? – squeakie

+0

当你构建它时,你的组中将会替换你的箭头函数'validator:(formGroup:FormGroup)=> {...}。 – n00dl3