title: Angular2 - Custom validator using old data
body: <p>I'm having an issue with a custom validator that I've written.  The validator is used to validate all the data in a component.</p>
  
  <p>Here's the setup:</p>
  
  <p>The component displays a table with X rows of data.  Some of the rows are display-only mode and others will be in edit mode (certain cells in the table will have inputs, selects, etc).  Backing the table is an array of data objects (tableData).</p>
  
  <p>If a row is in edit mode, each input is two-way bound to the appropriate element in the tableData array.</p>
  
  <p>My custom validator is applied to the form tag and takes tableData as input.  Everything mostly works.  The validation data looks at each row in the table and does everything I need it to do.  </p>
  
  <p>The issue is that the data based to my custom validator is old data.  So if a row is in edit mode, when I change a value in a select, tableData is updated, but the version of it that's passed to the validator is before the update.  So I'm always validating an old version of tableData.</p>
  
  <p>I'm not sure how to get the validation to use the up to date version of tableData.  I think the issue may be related to the fact that the select binding changes a value of an object in the tableData array, but the tableData array itself doesn't actually change.  </p>
  
  <p>I tried adding callback to the (change) event on the select in the row being edited.  The method called on (change) manually triggers change detection using a ChangeDetectorRef, but that didn't work.</p>
  
  <p>I don't want to spam everyone with all the entire files, so I've tried to just add the important snippets.</p>
  
  <p>Here's the template:</p>
  
  <pre><code>&lt;form #f="ngForm" novalidate custom-validator="{{tableData | json}}"&gt;
      &lt;p-dataTable [value]="tableData"&gt;
      ...
  
          &lt;p-column [header]="'Program Name'"&gt;
              &lt;template let-row="rowData" let-idx="rowIndex" pTemplate type="body"&gt;
              &lt;span *ngIf="!row['edit']"&gt;
                  {{row['data'].programName}}
              &lt;/span&gt;
                  &lt;div *ngIf="row['edit']"&gt;
                      &lt;select #progName="ngModel" [(ngModel)]="row['data'].programCode"
                              title="Select Program" required (change)="onProgramChange($event, idx)"
                              name="programSelect-{{idx}}"&gt;
                          &lt;option [value]=""&gt;&lt;/option&gt;
                          &lt;option *ngFor="let prog of programList" [value]="prog.code"&gt;
                              {{prog.name}}
                          &lt;/option&gt;
                      &lt;/select&gt;
                  &lt;/div&gt;
              &lt;/template&gt;
          &lt;/p-column&gt;
      ...
  
      &lt;/p-dataTable&gt;
  &lt;/form&gt;
  </code></pre>
  
  <p>Here's the backing component:</p>
  
  <pre><code>//imports...
  
  ...
  
  private tableData: PersonAssignmentRowData[] = [];
  private programList: Program[] = [];
  
  ...
  
  onProgramChange(event: any, index: number) {
  
      for(let prog of this.programList) {
          if(prog.code == event.target.value) {
              this.tableData[index].data.programAcronym = prog.acronym;
              this.tableData[index].data.programLocation = prog.location;
              this.tableData[index].data.programName = prog.name;
              break;
          }
      }
  }
  
  ...
  </code></pre>
  
  <p>Here's the validator:</p>
  
  <pre><code>@Directive({
      selector: '[custom-validator]',
      providers:[{provide: NG_VALIDATORS, useExisting: CustomValidator, multi: true}]
  })
  export class CustomValidator implements Validator{
  
      @Input('custom-validator') tableDataString: string;
  
      validate(control: AbstractControl) {
          if(this.tableDataString == null || this.tableDataString.length == 0) {
              return null;
          }
  
          let tableData: PersonAssignmentRowData[] = [];
          tableData = JSON.parse(this.tableDataString);
  
          let message: string = '';
  
          //logic that tests the validity of the data and sets any error messages in the message variable
  
          if(message.length &gt; 0) {
              return {'validationMessage': message};
          } else {
              return null;
          }
      }
  }
  </code></pre>