Home > Blockchain >  How to access dynamically set inputs name property in an Angular form in the template
How to access dynamically set inputs name property in an Angular form in the template

Time:05-31

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
                
                (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"
      >
  required
</span>

CodePudding user response:

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"
      >
  required
</span>

CodePudding user response:

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
                  
                  (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: { [coolumnId: string]: boolean},
    changed: { [coolumnId: string]: boolean},
    addedRow: boolean;
  }

let rowsStates = EditTableRowStatus[]

which led to super complex property binding in the template

<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
  
  *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"
/>
  • Related