The saved editable input number is automatically pushed even without needing to click on save or cancel

I am working with a datatable, chart, and a label that shows the latest added value.

  • The table and chart display time-series data for the last 30 minutes, including the timestamp and a random numerical value between 0 and 999.
  • Every 10 seconds, a new data object is added to the array while the oldest one is removed, ensuring that only the last 30 minutes of data are visible. The Latest Value label reflects the most recent addition.

My problem arises when attempting to edit rows in the table. After editing a cell by inputting numbers, if I cancel the edit without saving (by clicking X), the values in the table and chart update after 10 seconds. This behavior is unexpected as the edit was canceled. Similarly, just typing in the input field without saving or canceling the edit also triggers changes in the displayed values.

I suspect this issue has to do with two-way binding in the table, but I am unsure how to resolve it.

I tried the following solution, but encountered the same results:

clonedData = [];

ngOnChanges() {
this.clonedData = [...tableData]}

In the HTML table, instead of using tableData for the value, I attempted using clonedData as shown in the code snippet above.

Here is a link to the StackBlitz project: https://stackblitz.com/edit/angular-43vasb?file=src%2Fapp%2Fmain%2Ftable%2Ftable.component.ts,src%2Fapp%2Fmain%2Fmain.component.ts,src%2Fapp%2Finterface.ts,src%2Fapp%2Fdata.service.ts,src%2Fapp%2Fmain%2Ftable%2Ftable.component.html

Table TypeScript code:

export class TableComponent {
  @Input() tableData: IData[] = [];

  editedData: { [s: string]: IData } = {};

  constructor(private dataService: DataService) {}

  onRowEditInit(data: IData) {
    this.editedData[data.id] = { ...data };
    console.log(this.editedData[data.id]);
  }

  editRow(data: IData, row: any) {
    this.tableData[row] = data;
    this.dataService.setData(this.tableData);
    delete this.editedData[data.id];
  }

  onRowEditCancel(data: IData, row: number) {
    this.tableData[row] = this.editedData[data.id];
    delete this.editedData[data.id];
  }
}

Table HTML code:

<div class="container">
  <p-table
    [value]="tableData"
    dataKey="id"
    editMode="row"
    scrollHeight="flex"
    styleClass="p-datatable-gridlines"
    [rows]="8"
    [scrollable]="true"
  >
    <ng-template pTemplate="header">
      <tr>
        <th class="time">Time</th>
        <th class="value">Value</th>
        <th class="action"></th>
      </tr>
    </ng-template>
    <ng-template
      pTemplate="body"
      let-data
      let-editing="editing"
      let-ri="rowIndex"
    >
      <tr [pEditableRow]="data">
        <td class="p-column-title">
          <p-cellEditor>
            <ng-template pTemplate="input">
              <input pInputText type="text" [(ngModel)]="data.time" />
            </ng-template>
            <ng-template pTemplate="output">
              {{ data.time | date : "yyyy-MM-dd HH:mm:ss" }}
            </ng-template>
          </p-cellEditor>
        </td>
        <td>
          <p-cellEditor>
            <ng-template pTemplate="input">
              <input pInputText type="number" [(ngModel)]="data.value" />
            </ng-template>
            <ng-template pTemplate="output">
              {{ data.value | number : "1.2-2" }}
            </ng-template>
          </p-cellEditor>
        </td>
        <td class="action-button">
          <div class="flex align-items-center justify-content-center gap-2">
            <button
              *ngIf="!editing"
              pButton
              pRipple
              type="button"
              (click)="onRowEditInit(data)"
              pInitEditableRow
              icon="pi pi-pencil"
              class="p-button-rounded p-button-text"
            ></button>
            <button
              *ngIf="editing"
              pButton
              pRipple
              (click)="editRow(data, ri)"
              type="button"
              pSaveEditableRow
              icon="pi pi-check"
              class="p-button-rounded p-button-text p-button-success mr-2"
            ></button>
            <button
              *ngIf="editing"
              pButton
              pRipple
              (click)="onRowEditCancel(data, ri)"
              type="button"
              pCancelEditableRow
              icon="pi pi-times"
              class="p-button-rounded p-button-text p-button-danger"
            ></button>
          </div>
        </td>
      </tr>
    </ng-template>
  </p-table>
</div>

Answer №1

[Please take note that most examples for PrimeNG do not include live charts, which may not address your specific use-case. This could explain why you are encountering issues if you are following those tutorials.]

The main issue lies in the binding: by directly binding the time and value to the actual table data, any edits made - whether accepted or cancelled - will affect the original table data. To resolve this, it is advisable to bind to separate variables. I have made modifications to your stackblitz code as follows:

  1. I have created three new variables idBeingEdited, timeBeingEdited, and valueBeingEdited in [table.comp.ts] and bound them in the template [table.comp.html] instead of using data.time and data.value. This approach ensures that the 'original' data remains unchanged (particularly crucial for row order) while working solely with these local/bound values.
  2. Your onRowEditInit() method has been updated to set the values of these variables to match the data being edited.
  3. The onRowEditCancel() method has been removed entirely since we now manipulate non-original data, and no alterations (in logic or data) are needed if you simply cancel the operation.
  4. The editRow() method has been modified to update only the original data at the specified id without making any other changes.

Changes in table.com.ts:

idBeingEdited: number = null;
timeBeingEdited: number = null;
valueBeingEdited: number = null;

(...)

onRowEditInit(data: IData) {
    this.idBeingEdited = data.id;
    this.timeBeingEdited = data.time;
    this.valueBeingEdited = data.value;
}

editRow() {
    this.tableData.forEach((row, i) => {
      if (row.id == this.idBeingEdited) {
        this.tableData[i].time = this.timeBeingEdited;
        this.tableData[i].value = this.valueBeingEdited;
      }
    });
    this.dataService.setData(this.tableData);
}

In table.comp.html:

  • The input on line 28 (
    <input pInputText type="text" [(ngModel)]="data.time" />
    ) should be changed to
    <input pInputText type="text" [(ngModel)]="timeBeingEdited" />
  • The input on line 38 (
    <input pInputText type="number" [(ngModel)]="data.value" />
    ) should be changed to
    <input pInputText type="number" [(ngModel)]="valueBeingEdited" />
  • The click event on line 61 (
    (click)="editRow(data, ri)"
    ) should be changed to (click)="editRow()"

You can view a demo of these changes on stackblitz.

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

When working with TypeScript, it's important to note that an implicit 'any' type may occur when trying to use an expression of type 'string' to index a specific type

Currently, I'm attempting to transfer a custom hook used in Vue for handling i18n from JavaScript to TypeScript. However, I am encountering a persistent error message: An element is implicitly assigned the type 'any' due to an expression o ...

Display data in a template upon receiving a response from the server

Hello, I am currently in the process of developing my very first Angular application and could use some assistance. Specifically, I am working on a component that serves as an image search box. When a user inputs a search query, a request is sent to an API ...

Validating minimum and maximum values with Angular 2 FormBuilder

I am currently developing a form using Angular 2 Formbuilder and I want to ensure that users can only input positive values into the amount field (with a minValue of 0 and maxValue of 100). How can I go about implementing minimum and maximum value validati ...

Checking for Object Equality in TypeScript

I'm working on a TypeScript vector library and encountered my first failed test. The issue revolves around object equality in TypeScript/JavaScript, and despite searching through TypeScript's official documentation (http://www.typescriptlang.org ...

Is AWS CDK generating nested cdk.out directories during synthesis?

Whilst working on my AWS CDK project for educational purposes, I found myself immersed in learning TypeScript, node.js, npm, and all related concepts simultaneously. Despite the mishap that occurred, requiring me to restart from the Github repository rathe ...

What is the best way to access automatically generated JavaScript variables in TypeScript?

I am currently facing an issue with integrating a Google login API in my React project and I need some help. The problem arises when the user already has an active session, rendering the API unnecessary. The Javascript solution provided is as follows: co ...

Concealing the sidebar in React with the help of Ant Design

I want to create a sidebar that can be hidden by clicking an icon in the navigation bar without using classes. Although I may be approaching this incorrectly, I prefer to keep it simple. The error message I encountered is: (property) collapsed: boolean ...

Avoid Inferring as a Union Type

I am currently working on implementing a compact type-safe coordinate management system in TypeScript. It revolves around defining the origin of the coordinate as a type parameter, with functions that only accept one specific origin type. Below is a short ...

Warning: Typescript is unable to locate the specified module, which may result

When it comes to importing an Icon, the following code is what I am currently using: import Icon from "!svg-react-loader?name=Icon!../images/svg/item-thumbnail.svg" When working in Visual Studio Code 1.25.1, a warning from tslint appears: [ts] Cannot ...

Encountering a 404 error while attempting to test a contact form on a Next.js website using a local server

Trying to test a contact form in Next.js where the data is logged but not sent to the API due to an error. "POST http://localhost:3000/app/(pages)/api/contact/route.tsx 404 (Not Found)" Troubleshooting to identify the issue. [directory setup] ...

Deleting a key from a type in TypeScript using subtraction type

I am looking to create a type in TypeScript called ExcludeCart<T>, which essentially removes a specified key (in this case, cart) from the given type T. For example, if we have ExcludeCart<{foo: number, bar: string, cart: number}>, it should re ...

Authenticate using oidc-client-js and automatically redirect to a different web application

Having some trouble with the authentication process. I'm not entirely convinced it's well thought out. https://i.stack.imgur.com/62ju6.png I've got an Angular app specifically for user login and registration. When a user wants to log in 1, ...

Unable to resolve every parameter

I am facing an issue while trying to inject a service into my component, resulting in the following error: https://i.stack.imgur.com/zA3QB.png news.component.ts import { Component,OnInit } from '@angular/core'; import { NewsService } from &apo ...

Encountered a problem with regular expressions in Angular 2 - a Module parse error due to an octal literal in strict mode

Greetings, I have encountered an issue with a regular expression in my environment.ts file. export const environment = { passwordPolicy: "^(?!.*(.)\1\1)(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}.*$" }; Unfortunately, whe ...

How to integrate a toggle switch into an Angular datepicker component

Can the toggle switch be placed inside the calendar? I would like to have it positioned at the top of the open calendar so that users can choose to view date details using the toggle switch <form #uploadForm="ngForm" (keydown.enter)="$event.preventDe ...

What is the reason behind TypeScript rejecting the syntax of checkbox input elements?

When trying to use the following checkbox in TypeScript, I encountered a warning: <input type="checkbox" onClick={(event: React.MouseEvent<HTMLInputElement>) => { setIsTOSAccepted(event.target.checked); }} defaultChecked={ ...

Ways to access a property within an object using TypeScript

Can you help me extract the "attributes" array from this object and store it in a new variable? obj = { "_id": "5bf7e1be80c05307d06423c2", "agentId": "awais", "attributes": [ // that array. { "created ...

Tips for obtaining a subset of `keyof T` where the value, T[K], refers to callable functions in Typescript

Is there a way to filter keyof T based on the type of T[keyof T]? This is how it should function: type KeyOfType<T, U> = ... KeyOfType<{a: 1, b: '', c: 0, d: () => 1}, number> === 'a' | 'c' KeyOfType<{a: ...

PageObjectModel Playwright, execute the locator().all() function - 'The execution context has been terminated, possibly due to navigating to another...'

Hey there, I'm currently working on a basic test using POM. Here is a snippet from one of my PageObjects Class: import { Expect, Page, Locator } from "@playwright/test"; export class InventoryPage { readonly page: Page; readonly addToC ...

Is there an issue with my approach to using TypeScript generics in classes?

class Some<AttributeType = { bar: string }> { foo(attrs: AttributeType) { if (attrs.bar) { console.log(attrs.bar) } } } Unable to run TypeScript code due to a specific error message: Property 'bar' d ...