2

I wrapped an ngx-datatable component in a form tag so I can validate inputs in the table cells. Due to the nature of how the table is populated I set the inputs name properties dynamically

<form #tableForm="ngForm">
  <ngx-datatable
    [rows]="_rows">
        <ng-container *ngFor="let column of rowDeffinition; let columnIndex=index">
          <ngx-datatable-column [prop]="column.key" [name]="column.label">
            <ng-template ngx-datatable-cell-template let-rowIndex="rowIndex" let-value="value" let-row="row">
              <input
                class="cell-input"
                (blur)="updateCellValue($event, column.key, rowIndex)"
                type="text"
                [ngModel]="value"
                [name]="rowIndex + '-' + column.key"
              />

              ...

            </ng-template>
          </ngx-datatable-column>
        </ng-container>
  </ngx-datatable>
</form>

Normally, the name property would creates a local variable in the template and you can access the inputs control properties via the variable name.

<input type="text" name="name" [(ngModel)]="name" required minlength="4" />
<div *ngIf="name.invalid && name.touched">

I wonder how can I do this dynamically in the same manner I set the inputs name. So far I was able to access the input controls via the form reference but this becomes quite wordy

<span *ngIf="!tableForm.controls[rowIndex + '-' + column.key]?.valid && 
      !tableForm.controls[rowIndex + '-' + column.key]?.pristine"
      class="[ c-validation-message ]">
  required
</span>

2 Answers 2

1

You can wrap your input in new component and you can access these generated components via @ViewChildren(...) in parent components .ts file:

@ViewChildren(NgxDatatableInput) datatableInputs: QueryList<NgxDatatableInput>;

I recommend to create method in parent component, which retrieves concrete datatableInput from datatableInputs by name as parameter. After that you can use this method in generated, new ValidationSpanComponent:


<ValidationSpan [control]="getDatatableInput(dynamicName)">
</ValidationSpan>

Template of ValidationSpanComponent:

<span *ngIf="!control.valid && 
      !control.pristine"
      class="[ c-validation-message ]">
  required
</span>
Sign up to request clarification or add additional context in comments.

2 Comments

I could get the inputs control reference by using a function the whole time, but this is considered to be a bad practice. I am more seeking for a syntax which would allow me to access the declared variable. One solution may be using a template and ngTemplateOutletContext
Tbh I dislike optimization just for the sake of optimization, since profits of it are minimal. I guess your proposed solution will be less readable than just creating new components and using ViewChildren : )
0

Inspired by this answer I came up with following solution

<form #tableForm="ngForm">
  <ngx-datatable
    [rows]="_rows">
        <ng-container *ngFor="let column of rowDeffinition; let columnIndex=index">
          <ngx-datatable-column [prop]="column.key" [name]="column.label">
            <ng-template ngx-datatable-cell-template let-rowIndex="rowIndex" let-value="value" let-row="row">
              <!-- Helper template allowing to define few variables for more readable property binding-->
              <ng-template #cellContent [ngTemplateOutlet]="cellContent"              
                let-cellName="cellName" let-isValidInput="isValidInput" let-isPristineInput="isPristineInput"
                let-isRequiredInput="isRequiredInput"
                [ngTemplateOutletContext]="{
                  cellName: rowIndex + '-' + column.key,
                  isValidInput: tableForm.controls[rowIndex + '-' + column.key]?.valid,
                  isPristineInput: tableForm.controls[rowIndex + '-' + column.key]?.pristine,
                  isRequiredInput: column.input?.required
                }">

                <input
                  class="cell-input"
                  (blur)="updateCellValue($event, column.key, rowIndex)"
                  type="text"
                  [ngModel]="value"
                  [name]="cellname"
                />

                ...

              </ng-template>
            </ng-template>
          </ngx-datatable-column>
        </ng-container>
  </ngx-datatable>
</form>

This improves the general readability vastly, since my table has a very complex logic, and i track the state of the cell in a structure like:

interface EditTableRowStatus {
    editing: { [columnId: string]: boolean},
    changed: { [columnId: string]: boolean},
    addedRow: boolean;
  }

let rowsStates = EditTableRowStatus[]

which led to super complex property binding in the template

<input
  class="cell-input"
  *ngIf="column.input?.type === INPUT_TYPES.TEXT && (rowsStates[rowIndex]?.editing?.[column.key] || rowsStates[rowIndex]?.addedRow)"
  [autofocus]="!rowsStates[rowIndex]?.addedRow || columnIndex === 0 "
  (blur)="updateCellValue($event, column.key, rowIndex)"
  type="text"
  [ngModel]="value"
  [ngClass]="{'has-changes': rowsStates[rowIndex]?.changed?.[column.key]}"
  [name]="rowIndex + '-' + column.key"
  [required]="column.input?.required"
/>

now becoming much more readable

<input
  class="cell-input"
  *ngIf="inputType === INPUT_TYPES.TEXT && (isEditing || isAddedRow)"
  [autofocus]="!isAddedRow || columnIndex === 0 "
  (blur)="updateCellValue($event, column.key, rowIndex)"
  type="text"
  [ngModel]="value"
  [ngClass]="{'has-changes': isChanged}"
  [name]="cellName"
  [required]="isRequiredInput"
/>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.