Reputation: 4828
I'm developing a simple grid component for an Angular 6 application. Basically, it consists of a main component, app-grid
, which is an HTML table. Inside of it there are a determinate number of rows, that are another component (app-row
). Finally, inside of each row, there are some cells that hold objects, and are inside another component: app-cell
.
Here's a diagram of the architecture:
In an Angular-like template notation, the code for building the above components looks like:
<app-grid>
<app-row *ngFor="let row of grid.rows; let i=index">
<tr [ngClass]="{ 'row-highlighted' : row.highlighted }">
<td> #{{ i }} </td>
<app-cell *ngFor="let cell of row.cells">
<td [ngClass]="{ 'cell-highlighted' : cell.highlighted }">
</td>
</app-cell>
</tr>
</app-row>
</app-grid>
The internal data structure to build this is stored in the app-grid
component, and it looks like this:
grid = {
title: 'SAMPLE GRID',
rows: [
{
row_id: 20,
cells: [
{ cell_id: 201, cell_data: { DATA IN HERE } },
{ cell_id: 202, cell_data: { DATA IN HERE } },
{ cell_id: 203, cell_data: { DATA IN HERE } }
]
},
{
row_id: 30,
cells: [
{ cell_id: 301, cell_data: { DATA IN HERE } },
{ cell_id: 302, cell_data: { DATA IN HERE } },
{ cell_id: 303, cell_data: { DATA IN HERE } }
]
}
]
};
To improve performance, both app-row
and app-cell
components are coded using changeDetection: ChangeDetectionStrategy.OnPush
, so I trigger the change detection manually.
I'd like to be able to highlight an specific cell (for example, Cell [3, 2] in the picture above) and the row that contains it. To do it, I simply add an special class to the <tr>
of the app-row
and the <td>
of the wanted app-cell
, as you can see in the code sample above.
The problem I'm having is that the <tr>
style is immediately applied so the row is highlighted, but the cell doesn't apply the style until I interact with it (for example, if I hover it). For doing it, I've created a method in the app-grid
component with the following pseudo-code:
highlightRowAndCell(row_id, cell_id) {
Search 'grid.rows' for a row with 'row_id' identifier;
If found
Set row.highlighted to true;
Search 'row.cells' in the found row for a cell with 'cell_id' identifier;
If found
Set cell.highlighted to true;
// FORCE CHANGE DETECTION
this.appRef.tick();
this.changeDetectorRef.detectChanges();
}
Neither appRef.tick()
nor changeDetectorRef.detectChanges()
update the cell style, but the row's is immediately changed. If I hover the cell, then the style is applied.
How could I tell Angular to update the view of the affected components (or all of them, it doesn't matter)?
Thanks,
Upvotes: 2
Views: 4162
Reputation: 4828
The app-row
component has the <tr>
element of the rows and the <td>
of the cells, as stated above, so it's ideal to set the highlighted visual style to both row and cell. As I told before, I use a method (highlightRowAndCell
) in the app-grid
component to apply the visual style to the appropriate row and cell. The problem is that when I set row.highlighted = true
and cell.highlighted = true
, Angular doesn't detect these changes because they are properties of objects inside an array. So, to force it detecting the changes, I've added the following @Input
to the app-row
component:
app-grid.component.html template:
<app-row [...]
[highlighted]="row.highlighted">
</app-row>
app-row.component.ts:
export class AppRowComponent implements OnInit, OnDestroy {
[...]
@Input() highlighted: boolean;
[...]
}
Now, running this.changeDetectorRef.detectChanges();
in the highlightRowAndCell
method inside the app-grid
component makes Angular refresh the affected app-row
and the styles are applied immediately.
Upvotes: 0
Reputation: 27441
All primitive types are passed by value. Objects, arrays, and functions are also passed by value
So in order to trigger a change detection in our component, we need to change the object reference.
highlightRowAndCell(row_id, cell_id) {
Search 'grid.rows' for a row with 'row_id' identifier;
If found
//Set row.highlighted to true;
this.row={
highlighted=true;
}
Search 'row.cells' in the found row for a cell with 'cell_id' identifier;
If found
// Set cell.highlighted to true;
this.cell={highlighted=true}
}
Ref:https://netbasal.com/a-comprehensive-guide-to-angular-onpush-change-detection-strategy-5bac493074a4
Upvotes: 0
Reputation: 8605
Did you have a performance problem that prompted you to use ChangeDetectionStrategy.OnPush
? I think the much more common use case for that is for input controls, where you have a very specific event where you expect to trigger the change detection.
In your case, there isn't really such an event and I would rather forego the complexity altogether by using ChangeDetectionStrategy.Default
. It works really well for most use cases.
Also, if you are going to trigger change detection for all components, then you're not getting any benefit from the push model anyway - in fact, your performance will very likely be worse.
Upvotes: 0
Reputation: 5040
As far I know change Detection is for playing with data and data change. So this will not be a better approach for this purpose.
better to you [ngClass]="{someCondition: 'someClass'}"
or [ngStyle] and bind it to some variable.
Upvotes: 1