Emphasize cells and headers when a cell's value deviates from its initial value

I implemented cell editing in my primeng table. Here is the code:

<div class="card">
    <p-table [value]="products" dataKey="id" [tableStyle]="{ 'min-width': '50rem' }">
        <ng-template pTemplate="header">
            <tr>
                <th style="width:25%">Code</th>
                <th style="width:25%">Name</th>
                <th style="width:25%">Inventory Status</th>
                <th style="width:25%">Price</th>
            </tr>
        </ng-template>
        <ng-template pTemplate="body" let-product let-editing="editing">
            <tr>
                <td [pEditableColumn]="product.code" pEditableColumnField="code">
                    <p-cellEditor>
                        <ng-template pTemplate="input">
                            <input pInputText type="text" [(ngModel)]="product.code" />
                        </ng-template>
                        <ng-template pTemplate="output">
                            {{ product.code }}
                        </ng-template>
                    </p-cellEditor>
                </td>
                <td [pEditableColumn]="product.name" pEditableColumnField="name">
                    <p-cellEditor>
                        <ng-template pTemplate="input">
                            <input pInputText type="text" [(ngModel)]="product.name" required />
                        </ng-template>
                        <ng-template pTemplate="output">
                            {{ product.name }}
                        </ng-template>
                    </p-cellEditor>
                </td>
                <td [pEditableColumn]="product.inventoryStatus" pEditableColumnField="inventoryStatus">
                    <p-cellEditor>
                        <ng-template pTemplate="input">
                            <input pInputText [(ngModel)]="product.inventoryStatus" />
                        </ng-template>
                        <ng-template pTemplate="output">
                            {{ product.inventoryStatus }}
                        </ng-template>
                    </p-cellEditor>
                </td>
                <td [pEditableColumn]="product.price" pEditableColumnField="price">
                    <p-cellEditor>
                        <ng-template pTemplate="input">
                            <input pInputText type="text" [(ngModel)]="product.price" />
                        </ng-template>
                        <ng-template pTemplate="output">
                            {{ product.price | currency: 'USD' }}
                        </ng-template>
                    </p-cellEditor>
                </td>
            </tr>
        </ng-template>
    </p-table>
</div>

TS:

import { ProductService } from '../../service/productservice';

@Component({
    selector: 'table-cell-edit-demo',
    templateUrl: 'table-cell-edit-demo.html'
})
export class TableCellEditDemo implements OnInit {
    products!: Product[];

    constructor(private productService: ProductService) {}

    ngOnInit() {
        this.productService.getProductsMini().then((data) => {
            this.products = data;
        });
    }
}

Stackblitz:

https://stackblitz.com/run?file=src%2Fapp%2Fdemo%2Ftable-cell-edit-demo.html

The cell edit feature works perfectly. I wish to visually highlight cells and headers in red when a cell's value is changed from its original value. How can I achieve this?

Answer №1

To add color to the modified cell in your component's HTML, you can follow these steps:

  1. Include a template reference variable #rows in the <tr> element to access the rows using @ViewChildren. This allows you to interact with the rows in your component.

    <tr #rows>
    
  2. Update the <td> elements where edits need to be tracked by adding an [id] attribute. The [id] attribute should be a combination of the property and rowIndex, for example: [id]="'name'+index".

    <td pEditableColumn [id]="'name' + ri">
    
  3. Incorporate (ngModelChange) within the <input> element representing the editable field to detect changes in the input value. Upon value change, call a method like

    colorizeEditedCell(rowIndex, fieldName)
    passing the rowIndex and fieldName as arguments.

    <input pInputText type="text" [(ngModel)]="product.name" (ngModelChange)="colorizeEditedCell(ri,'name')" required />
    
  4. Add an @ViewChildren decorator to your component class to access the rows using the template reference variable #rows:

    @ViewChildren('rows') rows: QueryList<ElementRef<HTMLTableRowElement>>;
    
  5. Inject the Renderer2 into the constructor of your component to utilize it for DOM manipulation:

    constructor(private _renderer2: Renderer2) {}
    
  6. Define the function colorizeEditedCell within your component to set the background color of the edited cell. This function receives the rowIndex and fieldName as parameters.

    colorizeEditedCell(rowIndex: number, fieldName: string) {
      const cols = this.rows.get(rowIndex).nativeElement.cells;
      for (let colIndex = 0; colIndex < cols.length; colIndex++) {
        const col = cols.item(colIndex);
        if (col.id == fieldName + rowIndex) {
          this._renderer2.setStyle(col, 'backgroundColor', '#ececec');
          break;
        }
      }
    }
    

The provided code will iterate through the cells of the specified row and compare the col.id with the given fieldName + rowIndex. Upon finding a match, the Renderer2 sets the background color of the cell to #ececec.

Note: Ensure to import necessary dependencies such as Renderer2 and ElementRef in your component file.

An event named onEditComplete is present in the editable table component which you can use by setting certain properties on the <td> tag, as explained in the documentation. Here’s an example:

<td [pEditableColumn]="rowData"
[pEditableColumnField]="'year'" [pEditableColumnRowIndex]="index"> 

In the above example, rowData corresponds to the product in our scenario, and 'year' represents the product property associated with this column. The columnsRowIndex property is assigned the value of index in this case.

However, note that the onEditComplete event triggers only when the input field loses focus (on blur). Thus, it won’t immediately notify about data changes without blur event activation.

If real-time updates or continuous tracking during user input are needed, alternative mechanisms like (ngModelChange) or (input) events must be utilized, as demonstrated in the previous code snippet.

Alternate solutions may exist, but this proposed method should effectively meet your needs.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The list in my "Hamburger" menu isn't loading properly

I have been attempting to add a "Hamburger" style menu to my webpage specifically for the "md" size. Although the hamburger menu displays, clicking on it does not trigger any action. I experimented with removing some classes from certain tags, but most of ...

Determine the height using jQuery before adding the class

I've been grappling with jQuery to accurately calculate the height of an element and then apply a specific CSS class to adjust its height. The issue I'm facing is that jQuery seems to be executing these calculations out of order, which is causing ...

The capability to scroll within a stationary container

Whenever you click a button, a div slides out from the left by 100%. This div contains the menu for my website. The problem I'm encountering is that on smaller browser sizes, some of the links are hidden because they get covered up. The #slidingMenu ...

Troubles with nesting ngFor in Angular 9 causing issues

I'm encountering an issue with utilizing nested ngFor loops in an Angular 9 application. The final ngFor loop mentioned in the code snippet below (line 12) appears to function sporadically, only on specific cycles of the main ngFor loop in the top row ...

What is the best way to have a sound play when the page is loaded?

Is there a way to automatically play a sound when the page loads? <audio src="song.mp3"> Your browser does not support the audio element. </audio> I've attempted it with the following method: <script type="text/javasc ...

Steps for including a component in a loop using Angular 6

I'm working on a component that contains two input fields. The goal is to have a row pop up every time the user clicks on the add button, with all the inputs eventually being collected in an array of objects. For example, the component template looks ...

What is the ideal location to store your JWT security and secret key?

Currently, I am in the process of creating a web application with .NET Core 2.1 and Angular. To enhance security measures, I am eager to integrate web tokens into the project. From a security standpoint, I am curious about the most optimal location for st ...

The mysterious case of the missing currentUserObj in Angular with rxjs Subject

I've encountered an issue while trying to pass data from my login component to the user-profile component using an rxjs subject. Despite calling the sendUser method in the login component and subscribing to the observable in the user-profile component ...

Finding the number of elements in a FirebaseListObservable involves accessing the `length` property

One of the tasks in my Angular 2 application involves retrieving data from a Firebase database and storing it in a FirebaseListObservable. I have a method called getStatus that is supposed to determine the number of elements in this FirebaseListObservable. ...

The Angular application is showing "&#40" instead of "("

When I make an HTTP request and receive JSON data, I encounter a problem when displaying it in my HTML using curly braces {{}}. Certain characters like "(" appear as "&#40". Is there a way to convert these special characters back to their normal form? ...

Angular6 HttpClient: Unable to Inject Headers in Get Request for Chrome and IE11

Under my Angular 6 application, I am attempting to make a GET request while injecting some custom Headers: Here is how my service is structured: @Injectable() export class MyService { constructor(public httpClient: HttpClient) { } getUserInfos(login): Ob ...

"The jQuery colorpicker function is not functioning properly when applied to newly added elements

I've got these amazing gadgets with a cool sliding box feature inside. Initially, there are two widgets visible on the page, but you have the option to add or delete a widget as needed. Within the sliding box, there is a color picker tool. Interestin ...

Altering the placement of a single element from floating to fixed positioning in CSS

Currently in the process of designing a basic website, I am looking to modify the behavior of the navigation bar on the left-hand side. I want it to remain fixed in its position so that users can scroll through the page without losing sight of it. My main ...

What is the best way to add a value to a nested JSON array in Angular 5?

Need help transferring nested JSON data format from Web API to Angular5 {"contractId":1, "contractName":"Temp", "contractServiceList":[ {"id":1, "serviceId":{"serviceId":1,"serviceName":"Emergency Room"}, "providerTier":"Tier 1", "coi ...

The confusion between <g:if> and <g:set> features in Grails is causing some issues

I am working with a gsp template that includes a variable. If this variable is true, I want to add an extra button; if it's false, nothing should happen. What could be causing my code to not work correctly? <g:set var="removeButton">{{ removeB ...

Creating a Dynamic System for Converting Numeric Digits to Alphabetical Grades

Utilizing the code provided below, you can calculate a user's grade point average ranging from A+ to E-. Is there a way, within the following fiddle, to not only display the GPA but also convert it into a corresponding letter grade from A+ to E-? Curr ...

jQuery mobile menu: Scroll to content after closing the menu

I am currently working on a project where I am using mmenu for the first time. It's functioning as expected, but there is one particular issue that I would really like to have resolved. Here is the URL: What I am hoping to achieve is that when a men ...

Should FormBuilder be utilized in the constructor or is it considered a poor practice?

section, you can find an example of implementation where declarations for formBuilder and services are done within the constructor(). While it is commonly known that using services inside the constructor() is not a recommended practice and should be done ...

Change the class of each item in a list individually by hovering over them with the mouse using JavaScript

How to toggle classes in a list item one by one using the mouseover event in JavaScript? const items = document.querySelectorAll("ul li"); let currentItem; for (const item of items) { item.addEventListener("mouseover", e => { currentItem &am ...

Error: The module parsing process failed due to the presence of an octal literal in strict mode. To resolve this issue,

I'm currently attempting to incorporate the following regular expression into Angular6: const regexp = new RegExp('^(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]))\\1|(?:(?:29|30)(\/|-|\.)(?:0?[1,3-9]|1[0-2])\\2))(? ...