I have a table and I want to pass HTMLElement of the table cell to the component via click event hadler. At the very begininng I have the pointer to the correct table cell but after I init change detection manually the pointer points to the wrong cell (the next to the correct one)
I can't find out why it happens. I created example with console.log(tableCell)
before and after initialization of the change detection (method setEditMode
in AppComponent
)
https://stackblitz.com/edit/angular-module-sandbox-btszav?file=src/app/app.component.html
app.component.html
<button (click)="showClipboardText()">Fill the table</button>
<table id="table" (click)="showTarget($event)">
<thead>
<tr>
<th *ngFor="let colName of colNames">
{{ colName }}
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let rowData of data; let rowIndex = index">
<td
#tableCell
*ngFor="
let cellData of rowData.length
? rowData
: [].constructor(colNames.length);
let colIndex = index
"
[attr.data-col-name]="colNames[colIndex]"
(click)="setEditMode(rowIndex, colIndex, tableCell)"
>
{{ cellData?.value }}
<input
type="text"
(keydown)="saveEdit($event, rowIndex, colIndex)"
(blur)="saveEdit($event, rowIndex, colIndex)"
*ngIf="!!data[rowIndex][colIndex]?.isEditMode"
/>
</td>
<button (click)="addRow()">+</button>
</tr>
</tbody>
</table>
app.component.ts
import {
ChangeDetectorRef,
Component,
ElementRef,
QueryList,
ViewChildren,
} from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
@ViewChildren('tableCell', { read: ElementRef })
tableCells: QueryList<ElementRef>;
colNames = ['Client', 'Order', 'Total'];
data: Array<Array<{ isEditMode?: boolean; value?: string }>> = [
Array(this.colNames.length),
];
activeCell: { row: number; col: number };
constructor(private cd: ChangeDetectorRef) {}
addRow(): void {
this.data.push(Array(this.colNames.length));
}
setEditMode(row: number, col: number, tableCell: HTMLElement): void {
console.log(tableCell.dataset['colName']);
if (this.activeCell) {
const previousCellData =
this.data[this.activeCell.row][this.activeCell.col];
previousCellData.isEditMode = false;
}
if (!!this.data[row][col]) {
this.data[row][col].isEditMode = true;
this.data[row][col].value = '';
} else {
this.data[row][col] = { ...this.data[row][col], isEditMode: true };
}
this.activeCell = { row, col };
this.cd.detectChanges();
console.log(tableCell.dataset['colName']);
}
saveEdit(event: Event, row: number, col: number): void {
if ((<KeyboardEvent>event).key && (<KeyboardEvent>event).key !== 'Enter')
return;
const value = (<HTMLInputElement>event.target).value;
const previousCellData =
this.data[this.activeCell.row][this.activeCell.col];
previousCellData.isEditMode = false;
this.data[row][col].value = value;
}
}
Working stackblitz: https://stackblitz.com/edit/angular-module-sandbox-cdbpbq?file=src/app/app.component.ts
Your issue boils down to the fact that we go from having an array of size 3, but with no default values defined. photo:
Thus, each time we defined a value, the colIndex increased by one. You can check this if you set a data attribute for the colIndex
as you did for the colName
. The solution involves defining a default value for your row.
data: Array<Array<{ isEditMode?: boolean; value?: string }>> = [
[
{
isEditMode: false,
value: '',
},
{
isEditMode: false,
value: '',
},
{
isEditMode: false,
value: '',
},
],
];
However, this also means we need to update our add row method to look like so:
addRow(): void {
this.data.push([
{
isEditMode: false,
value: '',
},
{
isEditMode: false,
value: '',
},
{
isEditMode: false,
value: '',
},
]);
}
Previously all you did was just add an array of length three, but with no cells defined within it.