3

I am loading data into the mat-table data source from an API. There is an addnewrow() method which would add a new row at the top. I am actually trying to make the first column of the table editable. (i.e) user can give input only if they create a new row. The rest of the data in the table should not be editable. So for that I use a local flag variable false by default. Based on the flag, I am creating ng-template with ng-container.. If it's a new row, the first column is the only editable column, otherwise it should not be editable. I do not know why this is not working.

expected:

  • Only first column of newly added row should be editable
  • save in local storage.

Please check minimal demo - stackblitz, as I don't have rights share actual snippets, my apologies.

Kindly share any working examples and best approach(es) to achieve the goal.

snippet

<table mat-table #methedofaccept [dataSource]="dataSource" class="mat-elevation-z8" id= "tbl">              
  <ng-container *ngIf= "isColumnEditable == true; then showInputField  else normalColumn ">
    <th mat-header-cell *matHeaderCellDef> List of Values </th>
  </ng-container>
            
  <ng-template #showInputField>
    <td mat-cell matColumnDef="title" *matCellDef="let element" >
      <mat-form-field >
        <input matInput  [value]="element.title" [(ngModel)]="element.title">
      </mat-form-field>
    </td>
  </ng-template>

  <ng-template #normalColumn>
    <div matColumnDef="title">                    
      <td mat-cell *matCellDef="let element"> {{element.title}} </td>
    </div>
  </ng-template>

              
  <ng-container matColumnDef="action">
    <th mat-header-cell *matHeaderCellDef> Action </th>
    <td mat-cell *matCellDef="let element">                        
      <i *ngIf="element.endDate == null " class="material-icons clickable" (click)="deleteRow(element)">delete</i>
      <i *ngIf="element.endDate != null" class="material-icons clickable ct-blue undo-icon" (click)="undoRow(element)">undo</i>          
    </td>
  </ng-container>
  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns;" [ngClass]="{'row-deleted': row['isDeleted']" >
  </tr>
</table>

ERROR TypeError: Cannot read property 'template' of undefined

Thank you

Community
  • 1
  • 1
Mr. Learner
  • 978
  • 2
  • 18
  • 48

1 Answers1

2

What i got from your question:

  1. make first column of table editable i.e. user can give input only if they create new row. rest of the data in table should not be editable
  2. save in local storage.

In your stackblitz, make the table-filtering-example.html to be:

<div class="example-container mat-elevation-z8">


  <mat-table #table [dataSource]="dataSource">

    <!-- Position Column -->
    <ng-container matColumnDef="position">
      <mat-header-cell *matHeaderCellDef> No. </mat-header-cell>
      <mat-cell *matCellDef="let element"> 
        <ng-container *ngIf='element.editable'>
        <input type="number" [(ngModel)]=element.position (ngModelChange)="inputChanged($event)" />
        </ng-container>
        <ng-container *ngIf='!element.editable'>
        {{element.position}} 
        </ng-container>
      </mat-cell>
    </ng-container>

    <!-- Name Column -->
    <ng-container matColumnDef="name">
      <mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
      <mat-cell *matCellDef="let element"> {{element.name}} </mat-cell>
    </ng-container>

    <!-- Weight Column -->
    <ng-container matColumnDef="weight">
      <mat-header-cell *matHeaderCellDef> Weight </mat-header-cell>
      <mat-cell *matCellDef="let element"> {{element.weight}} </mat-cell>
    </ng-container>

    <!-- Symbol Column -->
    <ng-container matColumnDef="symbol">
      <mat-header-cell *matHeaderCellDef> Symbol </mat-header-cell>
      <mat-cell *matCellDef="let element"> {{element.symbol}} </mat-cell>
    </ng-container>

    <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
    <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
  </mat-table>
</div>
<button (click)="addNewRow()">Add new row</button>

in your stackblitz, make the table-filtering-example.ts to be:

import { Component, ViewChild,  AfterViewInit } from '@angular/core';
import { MatTableDataSource, MatTable } from '@angular/material';

/**
 * @title Table with filtering
 */
@Component({
  selector: 'table-filtering-example',
  styleUrls: ['table-filtering-example.css'],
  templateUrl: 'table-filtering-example.html',
})
export class TableFilteringExample implements AfterViewInit {
  @ViewChild('table') table: MatTable<Element>;
  displayedColumns = ['position', 'name', 'weight', 'symbol'];
  // dataSource = new MatTableDataSource([]);
  data: Element[] = [];

  constructor(){  
    console.log(this.data);
  }

  ngAfterViewInit(){
    if (this.table){
      //console.log('got table');
      if (this.table.dataSource) {
        this.data = (this.table.dataSource as Element[]);
      }  
      this.data.push(ELEMENT_DATA[this.data.length]);
      this.data.push(ELEMENT_DATA[this.data.length]);
      this.data.push(ELEMENT_DATA[this.data.length]);
      this.data.push(ELEMENT_DATA[this.data.length]);
      this.table.dataSource = this.data;
      this.table.renderRows();
    }
  }

  inputChanged(event){
    this.updateLocalStorage();
  }

  updateLocalStorage(){
      localStorage.setItem('myArray', JSON.stringify(this.table.dataSource) );
  }

  addNewRow() {
    let data: Element[] = [];
    if (this.table.dataSource) {
      data = (this.table.dataSource as Element[]);
    }
    ELEMENT_DATA[data.length].editable =true;
    data.push(ELEMENT_DATA[data.length]);
    this.table.dataSource = data;
    this.table.renderRows();
  }
}

export interface Element {
  name: string;
  position: number;
  weight: number;
  symbol: string;
  editable: boolean;
}

const ELEMENT_DATA: Element[] = [
  {position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H', editable: false},
  {position: 2, name: 'Helium', weight: 4.0026, symbol: 'He', editable: false},
  {position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li', editable: false},
  {position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be', editable: false},
  {position: 5, name: 'Boron', weight: 10.811, symbol: 'B', editable: false},
  {position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C', editable: false},
  {position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N', editable: false},
  {position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O', editable: false},
  {position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F', editable: false},
  {position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne', editable: false},
  {position: 11, name: 'Sodium', weight: 22.9897, symbol: 'Na', editable: false},
  {position: 12, name: 'Magnesium', weight: 24.305, symbol: 'Mg', editable: false},
  {position: 13, name: 'Aluminum', weight: 26.9815, symbol: 'Al', editable: false},
  {position: 14, name: 'Silicon', weight: 28.0855, symbol: 'Si', editable: false},
  {position: 15, name: 'Phosphorus', weight: 30.9738, symbol: 'P', editable: false},
  {position: 16, name: 'Sulfur', weight: 32.065, symbol: 'S', editable: false},
  {position: 17, name: 'Chlorine', weight: 35.453, symbol: 'Cl', editable: false},
  {position: 18, name: 'Argon', weight: 39.948, symbol: 'Ar', editable: false},
  {position: 19, name: 'Potassium', weight: 39.0983, symbol: 'K', editable: false},
  {position: 20, name: 'Calcium', weight: 40.078, symbol: 'Ca', editable: false},
];
Akber Iqbal
  • 14,487
  • 12
  • 48
  • 70
  • i just changed as you said. but nothing happend.. column is still not editable. pls check this https://stackblitz.com/edit/angular-dnj6gj-tgirxz?file=app%2Ftable-filtering-example.ts – Mr. Learner Apr 02 '19 at 09:55
  • 1
    check this https://stackblitz.com/edit/angular-rwbczv?file=app%2Ftable-basic-example.ts – Akber Iqbal Apr 02 '19 at 09:59
  • my copy pasting mistake.. its working bro.. thanks alot – Mr. Learner Apr 02 '19 at 10:01
  • you're welcome, i would appreciate a vote up and accepted answer :) – Akber Iqbal Apr 02 '19 at 10:01
  • Igbal .. just want to learn . could you pls give technical explanation, why my approach doesn't work. want to know whether my understanding about ng-container and ng-template is right or wrong. – Mr. Learner Apr 02 '19 at 12:49
  • I gotta check this tomorrow and respond... My instinct was to use ng-container like I used to from my angularJS days... – Akber Iqbal Apr 02 '19 at 15:17