Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.1k views
in Technique[技术] by (71.8m points)

html - Is there any way to edit specific Column of table using mat-table in Angular material

In my project i am using Angular Material's table for displaying value in table format, As per new requirement i have to perform in-line editing for last 2 column over other along with that if user click 1st column the other column get highlighted automatically and everything has to be done using Angular material

This is the last 2 column i want to perform in-line editing

>  <ng-container matColumnDef="weight">
>       <th mat-header-cell *matHeaderCellDef> Weight </th>
>       <td mat-cell *matCellDef="let element"> {{element.weight}} </td>
>     </ng-container>
>     <ng-container matColumnDef="symbol">
>       <th mat-header-cell *matHeaderCellDef> Symbol </th>
>       <td mat-cell *matCellDef="let element"> {{element.symbol}} </td>
>     </ng-container>
>   
>     
>     <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
>     <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>   </table>

How i achieved is as follows :

<tbody>
                <tr *ngFor="let data of dataSource">      
                   <td >{{data.position}}</td>
                   <td >{{data.name}}</td>
                   <td *ngIf="data.position === editRowId"> <input matInput [(ngModel)]="data.weight"></td>
                   <td *ngIf="data.position !== editRowId" (click)="editTableRow(data.position)">{{data.weight}}</td>
                   <td *ngIf="data.position === editRowId"> <input matInput [(ngModel)]="data.symbol"></td>
                   <td *ngIf="data.position !== editRowId" (click)="editTableRow(data.position)">{{data.symbol}}</td>
                </tr>
            </tbody>

TS file for above code :

export class AppComponent {
  showEditTable = false;
  editRowId: any = '';
  selectedRow;
  displayedColumns: string[] = ['position', 'name', 'weight', 'symbol'];
  dataSource = ELEMENT_DATA;
  editTableRow(val) {
  console.log('click event started val = ' + val);
  this.editRowId = val;
  console.log('click event ended with val = ' + val);
  }
}

I expect the result as table where last 2 column can be edited inline and at the same time i can send modified data to backend

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Naman, it's the same, you need use <ng-container>to avoid create extra tags, so your columns becomes like

  <!-- Weight Column -->
  <ng-container matColumnDef="weight">
    <th mat-header-cell *matHeaderCellDef> Weight </th>
    <td mat-cell *matCellDef="let element"> 
      <ng-container *ngIf="element.position!==editRowId">
      <span (click)="edit(element.position,'weigth')">{{element.weight}} </span>
      </ng-container>
      <ng-container *ngIf="element.position===editRowId">
      <input matInput name="weigth" [(ngModel)]="element.weight"> 
      </ng-container>
      </td>
  </ng-container>

Well, I call to function "edit" passign two arguments, the position and a string indicate the "name" of the input attributes. This allow us "focus" the input clicked. how?

We declare a ViewChildren of the MatInputs

  @ViewChildren(MatInput,{read:ElementRef}) inputs:QueryList<ElementRef>;

See that we get not the MatInput else the "ElementRef". This allow us, in out function edit get the element with the attributes name equal the string that pass as argument, and focus it. See that we need "enclosed" all in a setTimeout to allow Angular to show the input

  edit(row,element)
  {
    this.editRowId=row;
    setTimeout(()=>{
      this.inputs.find(x=>x.nativeElement.getAttribute('name')==element)
          .nativeElement.focus()
    })
  }

You can see the full example in stackblitz

Well, in the example the data is hardcoded. Let's go to imagine that the data (and the structure) comes from a service data. The data is easy imagine because it's the same. The "structure" we can imagine as an array of object with three properties: name,head ad fixed. if fixed is true, we only show the data, else we can edit. So the only thing we need is create the columns in a *ngFor

First we are going to see how our schema can be defined. It's only an array

[
   {name:'position',head:"No.",fixed:true},
   {name:'name',head:"Name",fixed:true},
   {name:'weight',head:"Weigth",fixed:false},
   {name:'symbol',head:"Symbol",fixed:false},

]

Our table becomes like

<table #table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
    <ng-container *ngFor="let column of schema;let last=last">
        <ng-container [matColumnDef]="column.name">
            <th mat-header-cell *matHeaderCellDef> {{column.head}} </th>
            <td mat-cell *matCellDef="let element">
                <ng-container *ngIf="element[schema[0].name]!==editRowId || column.fixed">
                    <span 
                      (click)="column.fixed?editRowId=-1:
                               edit(element[schema[0].name],column.name)">
                         {{element[column.name]}} 
                    </span>
      </ng-container>
      <ng-container *ngIf="element[schema[0].name]===editRowId && !column.fixed">
         <input matInput [id]="column.name"
              [(ngModel)]="element[column.name]"
              (blur)="last?editRowId=-1:null"> 
      </ng-container>
      </td>
    </ng-container>
  </ng-container>

  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>

See that we "replace" element.position by element[column[0].name] -I supouse the first element of the schema will be the "key" and how we using [(ngModel)]="elemen[column.name]". Yes, to refererd to element.position we can refered too as elemen["position"] and remember we are iterating over "schema"

Another thing is that we are going to use "id", not "name". this is because if we using name, really Angular put as attrib some like: ng-reflect-name, and this don't allow us focus the input.

Finally we are get the data in the ngOnInit. We are going to use forkJoin to get together the schema and the data. A forkJoin only call and array of observables (in this case this.dataService.getSchema() and this.dataServide.getData, and return in an array the response of all the observables. We use the way ([variable1,variable2]) to store in "variable1" the first result and in variable2 the second result

ngOnInit()
{
  forkJoin([this.dataService.getSchema(),this.dataService.getData()])
    .subscribe(([schema,data])=>{
      this.dataSource=data;
      this.displayedColumns=schema.map(x=>x.name)
      this.schema=schema
    })
}

The displayedColumns must be an array with the names of the columns, but we need to store in an array the "schema" too.

In the stackblitz I create a service and "simulate" the observable using the rxjs creation operator of, in a real application the data becomes from a httpClient.get(....)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

56.7k users

...