"Disappearing Act: Updated data in Angular Datatable vanishes during export, sorting

I recently started working with angular datatables and I have a table that displays different dog breeds. Users can toggle between read-only and editable fields, as well as delete entries. Additionally, I need to export this data to excel or pdf formats. The issue I'm facing is that after editing or deleting an entry (which triggers an API call to update the database), the changes are not reflected in the exported file. Only the original data from when the page first loaded is included in the export...
This is how it currently functions: there's an edit icon that allows the user to make changes -> user edits the field and confirms -> API call is made through the module service -> data is updated in the backend -> however, the Excel/Pdf export still shows the old data.HTML

<table datatable [dtOptions]="dtExportButtonOptions" class="table table-bordered table-striped mb-0">
            <thead>
                <tr>
                    <th class="text-center">Breed Name</th>
                    <th class="text-center">Options</th>
                </tr>
            </thead>
            <tbody>
                <tr *ngFor="let breed of BREEDS">
                    <td class="text-center">
                        <input class="form-control" type="text" placeholder="Insert Breed Name"
                            [(ngModel)]="breed.BREED_NAME" [readonly]="!breed.Edit">
                        <span style="display: none;" >{{breed.BREED_NAME}}</span>
                    </td>

                    <td class="text-center">
                        <div class="row align-items-center m-l-0">
                            <div class="col-sm-6 text-right">
                                <button *ngIf="!breed.Edit" type="button" class="btn btn-icon btn-outline-info mr-2"
                                    (click)="ToggleEdit(breed)"><i class="feather icon-edit-2"></i></button>
                                <button *ngIf="breed.Edit" type="button" class="btn btn-icon btn-outline-success mr-2"
                                    (click)="EditBreed(breed)"><i class="feather icon-check-circle"></i></button>
                            </div>
                            <div class="col-sm-6 text-left">
                                <button *ngIf="!breed.Edit" type="button" class="btn  btn-icon btn-outline-danger"
                                    (click)="OpenDeleteModal(breed)"><i class="feather icon-trash-2"></i></button>
                                <button *ngIf="breed.Edit" type="button" class="btn  btn-icon btn-outline-danger"
                                    (click)="ToggleEdit(breed)"><i class="feather icon-slash"></i></button>
                            </div>
                        </div>
                    </td>
                </tr>
            </tbody>
        </table>

TYPESCRIPT

addCusForm: FormGroup;
  dtExportButtonOptions: any = {};
  DeletedBreed: Breed;
  _params_EditBreed: Breed = new Breed();
  _params_DeleteBreed: Params_Delete_Breed = new Params_Delete_Breed();
constructor(
    private proxy: Proxy,
    private fb: FormBuilder,
    private CmSvc: CommonService,
    private GSSVC: GeneralsetService
  ) { }

  ngOnInit(): void {
    this.addCusForm = this.fb.group({
      BREED_ID: [-1],
      OWNER_ID: [1],
      BREED_NAME: [, [Validators.required]],
    });

    var buttonCommon = {
      exportOptions: {
        // format: {
        //   body: function (data, row, column, node) {
        //     return node.firstChild.tagName === "INPUT" ?
        //       node.firstElementChild.value :
        //       data;
        //   }
        // },
        columns: [0],
      }
    };

    this.dtExportButtonOptions = {
      dom: 'Bfrtip',
      buttons: [
        $.extend(true, {}, buttonCommon, {
          extend: 'copyHtml5'
        }),
        $.extend(true, {}, buttonCommon, {
          extend: 'excelHtml5',
          titleAttr: 'Export to Excel',
        }),
        $.extend(true, {}, buttonCommon, {
          extend: 'print',
          titleAttr: 'Print',
        }),
      ]
    };
  }
ToggleEdit(breed) {
    breed.Edit = !breed.Edit;
  }

  EditBreed(breed: Breed) {
    if (breed.BREED_NAME.length > 0) {
      this._params_EditBreed = breed;
      console.log(this._params_EditBreed);

      this.proxy.Edit_Breed(this._params_EditBreed).subscribe((result) => {
        if (result) {
          this.CmSvc.ShowMessage('Breed ' + this._params_EditBreed.BREED_NAME + ' has been successfully Updated.',
            3500);
          this.addCusForm.get('BREED_NAME').reset();
          this.GSSVC.resolve();
        }
      });
      this.ToggleEdit(breed);
    }
  }

  SubmitBreed() {
    if (this.addCusForm.valid) {
      this._params_EditBreed = this.addCusForm.getRawValue() as Breed;
      console.log(this._params_EditBreed);

      this.proxy.Edit_Breed(this._params_EditBreed).subscribe((result) => {
        if (result) {
          this.CmSvc.ShowMessage('Breed ' + this._params_EditBreed.BREED_NAME + ' has been successfully submitted.',
            3500);
          this.addCusForm.get('BREED_NAME').reset();
          this.GSSVC.resolve();
        }
      });
    }
  }

DeleteBreed() {
    this._params_DeleteBreed.BREED_ID = this.DeletedBreed.BREED_ID;
    this.proxy.Delete_Breed(this._params_DeleteBreed).subscribe((result) => {
      if (!result) {
        this.CmSvc.ShowMessage(
          'Breed ' + this.DeletedBreed.BREED_NAME + ' has been successfully removed.',
          3500
        );
        this.modalDelete.hide();
        this.DeletedBreed = null;
        //this.ToggleEdit(breed);
        this.GSSVC.resolve();
      }
    })
  }

It's worth noting that I added a hidden span in the table HTML to display input fields properly. It served as a workaround for the display issue. I also attempted another solution mentioned in the TypeScript file comments but it didn't allow for sorting or searching functionalities.
Here's a link to a "non-working" stackblitz example for a better understanding of the overall structure: https://stackblitz.com/edit/angular-bppwap?embed=1&file=src/app/hello.component.html
I hope this provides enough information, and thank you in advance for any assistance.

Answer №1

If you need to update the data in an Angular DataTable, make sure to include a dtTrigger on the table and invoke next() when you want to reload the data:

<table datatable 
[dtOptions]="dtExportButtonOptions" 
[dtTrigger]="dtTrigger" 
class="table table-bordered table-striped mb-0">
import { Subject } from 'rxjs';
import { DataTableDirective } from 'angular-datatables';

---

  dtTrigger: Subject<any> = new Subject<any>();
  @ViewChild(DataTableDirective, { static: false })
  dtElement: DataTableDirective;

 // Use this method to reload the data
 reRender(): void {
    this.dtElement.dtInstance.then((dtInstance: DataTables.Api) => {
      // Destroy the current table instance
      dtInstance.destroy();
      // Trigger dtTrigger to render the table again
      this.dtTrigger.next();
    });
  }

Answer №2

Utilizing Maria's reRender function was a step in the right direction, but I found it necessary to make additional adjustments for optimal functionality. Initially, I transferred the API data retrieval function from the parent component to the child component. This change allowed me to trigger the reRender function upon receiving the data. (Alternatively, I could have utilized @ViewChild in the parent component to achieve the same result, but I opted for simplicity).

The first step involved adding the following code:

ngAfterViewInit(): void {
    this.dtTrigger.next();
    this.isfirst = false;
  }

The isfirst variable helps differentiate between the initial page load and subsequent loads to prevent errors when attempting to reRender during OnInit.

Next, I implemented the reRender function as shown below:

reRender(): void {
    this.dtElement.dtInstance.then((dtInstance: DataTables.Api) => {
      // Destroy the table before rerendering
      dtInstance.destroy();
      // Trigger dtTrigger to rerender the table
      this.dtTrigger.next();
    });
  }

Upon retrieving the data, the following logic is executed:

this.GSSVC.onDog_breedChange
      .pipe(takeUntil(this._unsubscribAll)).subscribe(breed => {
        if (breed) {
          this.BREEDS = breed;
          if (this.BREEDS.length === 0) {
            this.EmptyData = true;
          }
          else {
            this.EmptyData = false;
          }
          if (!this.isfirst)
            this.reRender();
        }
      });

Note that I had previously included an ngIf directive on the table element to handle empty data scenarios. However, this caused issues when attempting to add entries after clearing all data. To resolve this, consider removing the ngIf or replacing it with [hidden] to avoid errors.

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

Guide to utilizing Angular's translate i18n key as a dynamic parameter within HTML

Looking to pass an i18n translate service key as a parameter function on an HTML component. Attempted the following, but instead of getting the text, it's returning the key value. Created a variable assigned with the title in the component.ts file. ...

Utilizing the output of one function as an input parameter for another function: A guide

Having this specific shape const shape = { foo: () => 'hi', bar: (arg) => typeof arg === 'string' // argument is expected to be a string because foo returns a string } Is there a way to connect the return type of foo to the ...

When is the AbortSignal in the TanStack useQuery function considered as undefined?

The TanStack React Query documentation (visit here) provides insights on utilizing the queryFn property with an object containing a signal property for query cancellation. Check out this example: const query = useQuery({ queryKey: ['todos'], ...

Guide on how to execute jasmine tests coded in TypeScript for Node.js applications

I am eager to test my express application developed in TypeScript. I am utilizing jasmine for writing test cases, webpack for bundling TypeScript files to JavaScript, and karma as the test runner. Please locate the following files: // about.service.ts - ...

Building Silent Authentication in React Native with the help of Auth0: A Step-by-Step Guide

I am currently working on my first React Native app, and I have integrated Auth0 for authentication purposes. My goal is to implement silent authentication using refresh tokens. So far, I have attempted to use the checkSession() method but encountered an ...

The performance of ternary operators in Typescript-based Reactjs fell short of my expectations

As a newcomer to TypeScript+ReactJS, I am facing an issue with the Ternary operator in my code. Here is the code snippet: import React, { SyntheticEvent,useRef,useState } from "react"; import Result from './Result'; //main application c ...

The Vue CLI project, using Typescript, is facing challenges with building and running Mocha tests

My Vue 2 project, created using Vue CLi, is encountering numerous errors. While it compiles fine for development purposes, running unit tests or building for production results in a cascade of issues. Displayed below are some sample errors, along with sni ...

Cypress and Cucumber synergy: Experience automatic page reloads in Cypress with each test scenario in the Describe block

Hey, I'm facing an unusual issue. I have a dialog window with a data-cy attribute added to it. In my cucumber scenarios, I have one like this: Scenario: Users open dialog window When the user clicks on the open dialog button I've written Cypre ...

`"Unable to execute the 'ng build --env=prod' command"`

I have a JavaScript website that I need to rebuild with some changes I made. In order to build the application, I was instructed to run this command from the source files directory: ng build –env=prod However, when I try to do so, I keep encountering t ...

Troubleshooting Issue with Mongoose Virtual Field Population

I am currently facing an issue with my database due to using an outdated backend wrapper (Parse Server). The problem arises when dealing with two collections, Users and Stores, where each user is associated with just one address. const user = { id: &q ...

The program is requesting an expression involving the user's username

https://i.stack.imgur.com/tf1QD.png What is causing the issue with trying to use user.username? as an expression? While user.username returns a string of the User's username, I am unable to index it into listOfPlayers[]. client.on("messageReacti ...

What is the best way to encode a type that necessitates a specific superclass and interface implementation?

In order to achieve my goal, I need to extend a library class that is part of the React components I am creating. Here's an example of the original library class I'm working with: class ReactComponent<T, U> { render() { return "some ht ...

``In Angular 12, what are the best ways to tackle the CORS problem?

I am currently working on a project using Angular and I have encountered an issue with CORS error while trying to submit the login API. I have also included a screenshot of the error for reference. Any advice or suggestions would be greatly appreciated. AP ...

Solutions for Utilizing Generic Mixins in Typescript

As a newcomer to Typescript, I have encountered an issue with mixins and generics. The problem became apparent when working on the following example: (Edit: I have incorporated Titian's answer into approach 2 and included setValue() to better showcas ...

The SDK directory for TypeScript 1.3 in Visual Studio 2013 does not include the necessary tsc.exe file

Exciting news! Typescript v1.3 has been officially announced today. To fully utilize this update, I quickly installed the power tools update for VS2013. Upon completion of the installation, my Visual Studio environment now recognizes the "protected" keywo ...

Leveraging Enums in Angular 8 HTML template for conditional rendering with *ngIf

Is there a way to implement Enums in an Angular 8 template? component.ts import { Component } from '@angular/core'; import { SomeEnum } from './global'; @Component({ selector: 'my-app', templateUrl: './app.componen ...

Angular Material - Data Table Kit

Struggling with setting custom styling for a mat-table in Angular Material. I want to adjust the border radius of the table and change the spacing inside to "space-between". The design I'm aiming for is shown here: Design Any tips or suggestions woul ...

creating duplicate tables in an angular json array

My code structure is set up in a way that the data is coming in as an array, but it's creating more than one table. I suspect there might be an issue on the HTML side. Is there a way to fix this? When I attempt to import the data from the JSON into a ...

Angular 2's Component background color styling

Within my Home component, there is a carousel component. I want the background color of the home section to be Gray and the background color of the carousel to be blue. Unfortunately, I am having difficulty setting a background color for the body tag of t ...

The background image is not being properly applied by React's makeStyles

Even after attempting various methods to load the image for the backgroundImage property, it still does not appear on the page. Interestingly, loading external images (such as those from Google) works just fine. I experimented with the following: backgro ...