2017-03-16 100 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