17

Using Angular v2.4.8 and PrimeNg v1.1.4

I have a page with two components:

  1. Dropzone, for uploading files
  2. p-datatable to show the uploaded files

I configured Dropzone to send 5 files at a time and when it is finished with 5 files the event onDropZoneSendingMultiple is raised. When all files are uploaded onDropZoneQueueComplete is raised.

In both listeners I want to refresh the datatable which is in the second component. This is not working. I need to refresh the page to see the new files.

My HTML of the main page:

<div class="row" id="dropzoneContainer">
    <dropzone class="dropzone" #dz [config]="dropZoneConfig" 
              (error)="onDropZoneUploadError($event)"
              (sendingmultiple)="onDropZoneSendingMultiple($event)"
              (queuecomplete)="onDropZoneQueueComplete($event, dz);"
              (maxfilesreached)="onDropZoneMaxfilesReached($event)"
              (maxfilesexceeded)="onDropZoneMaxfilesExceeded"></dropzone>
</div>

<div class="row">
    <div class="col-md-12">
        <FilesList></FilesList>
    </div>
</div>

The Dropzone-component shows the dropzone. The FilesList shows the datatable. Part of the HTML:

<p-dataTable [hidden]="loading" [value]="files" selectionMode="single" (onRowSelect)="details($event)">

In my main ts-file I have:

@ViewChild(FilesListComponent)
public filesListComponent: FilesListComponent;

private reloadFileList() {
  this.filesListComponent.reload();
}

In my filelist ts I have

public files: File[];
public reload() {
    this.getFiles();
}
public getFiles() {
    this.fileService.getAll()
        .then(
        data => {
            this.files = data;
        });
}

getFiles is also called at page load. When I add console.log() statements I can see getFiles() is called and this.files is updated, but the table doesn't refresh.

Al-Mothafar
  • 7,949
  • 7
  • 68
  • 102
Paul Meems
  • 3,002
  • 4
  • 35
  • 66

7 Answers7

26

For anyone still looking for the new syntax of adding records to an array that is bound to a primeng table

this.arrayRecords= [...this.arrayRecords, newObject];

karthiks3000
  • 842
  • 8
  • 12
  • Thank you! But it still bugs me that the _way_ the records are altered decides of whether PrimeNG's table updates or not... If the element is pushed, the table doesn't update, and if the element is inserted using the spread operator, it does... Just why? – Jeremy Thille Jul 28 '17 at 08:08
  • 1
    I agree, I had to change all my code when I upgraded from angular 2 to angular 4 and this approach just doesn't seem right. – karthiks3000 Aug 04 '17 at 17:38
  • 4
    @JeremyThille The difference is caused by Angular's shallow change detection. If you use myArray.push(elem) the pointer to the array is not changed and Angular does not notice the change. By using myArray = [...myArray, elem] you instantiate a new array, so the pointer gets updated, and Angular will notice the change. Even though the spread operator solution is a bit less performant than push(), having Angular only do shallow change detection is a *LOT* more performant than deep change detection. – HammerNL Dec 08 '17 at 12:11
  • Is there a reason why this hasn't been marked as the correct answer? The developer on primeng says the solution is to create a new array. https://github.com/primefaces/primeng/issues/5698 – dotnetesse Jan 31 '19 at 19:56
  • Does not seem to work with Angular 10. – mneumann Jan 26 '22 at 12:41
15

Update: PrimeNG recently removed the DoCheck interface which was automatically detecting changes, see: https://www.primefaces.org/primeng-4-0-0-rc4-released/

The answer is to use the spread operator( [...arr] - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator ) to add items to your array instead of .push().


Original Answer: I had a similar issue which I solved using a slightly different approach. If you are using the same service to upload and retrieve your files you can use RxJS instead of listening to events across components.

On my services I want to reload across the app when I make a POST or PUT I use:

  private _shouldUpdateSource = new BehaviorSubject<boolean>(false);
  shouldUpdateObservable = this._shouldUpdateSource.asObservable();

In your POST and/or PUT methods

this.http.post(..).map( res => {this._shouldUpdateSource.next(true);});

Which allows you to subscribe to the fileService.shouldUpdateObservable in your components:

 this.shouldUpdateSub = this.fileService.shouldUpdateObservable
  .subscribe((shouldUpdate: boolean) => {
    if (shouldUpdate) {
      this.updateFiles();
    }
  });

This seems to be the best way to handle communication about a service between components I've seen/used.

-Edit- Here is the same concept in the official documentation:
https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service

-Edit 2- I ran into this issue again after updating to 4.0.0. FWIW, I ended up removing the PrimeNG Datatable, instead using a manual *ngFor and it worked fine. It seems that the PrimeNg documentation( https://www.primefaces.org/primeng/#/datatable ) tells you to:

"For example, use slice instead of splice when removing an item or use spread operator instead of push method when adding an item."

I'm not sure why they ask you to do this because it is contrary to what the official Angular documentation tells you to do but I believe it has something to do with why the [value] binding not working as you would expect.

Personally, I am moving away from PrimeNg in favor of the Covalent data table which has an explicit refresh() function: https://teradata.github.io/covalent/#/components/data-table

rbj325
  • 954
  • 8
  • 17
  • 1
    Thanks rbh325 for your reply. It seems I can listen to the shouldUpdate event, but still no refreshing of my table. In fact this event is called so many times it freezes my page. I tried to create a plunker page: https://plnkr.co/edit/PdlI6Ay9KIETTcI1Y6VJ but I'm also new to plunker and can't get it to work but hopefully you can see what I'm doing wrong. – Paul Meems Mar 12 '17 at 09:57
  • 1
    _shouldUpdateSource should be in file-service.ts. I don't see that file in the plunkr. The whole issue you are experience is cross-component communication. The idea here is to move it to a service and out of the component. You can't mock your service call in a component, it isn't handled the same way. – rbj325 Mar 12 '17 at 21:09
  • 2
    `The answer is to use the spread operator( [..arr] ) to add items to your array instead of .push().` what do you mean by this ? what is the syntax to use? – Dany Y May 27 '17 at 14:34
  • Use `this.dataArray= [...this.dataArray, this.randomForm.value];` instead of `this.dataArray.push(this.randomForm.value)` – Krishna Jun 17 '20 at 11:24
6

UPDATE: I took a better look at the documentation for p-dataTable, particularly the section titled Change Detection. I removed the [immutable]=false attribute work-around in the p-dataTable tag and instead I'm now getting the table to refresh by returning something like this when I modify the underlying array:

return myOriginalArray.slice();

ORIGINAL: I was having trouble with getting p-datatable to update/refresh when I removed an item from the underlying array used to populate the table. No clue as to why, but adding the following attribute to the p-datatable tag fixed things for me:

[immutable]=false

I'm using PrimeNG ^4.1.3

twosouth
  • 171
  • 3
  • 12
1

I suspect this has to do with how your child component is handling changes. You should implement the onChange event and set the files that way. Here is an example:

```

export class FilesListComponent implements OnChanges {
    @Input() files: File[];

    ngOnInit() {
    }

    // Subscribe to the change event and update the model
    ngOnChanges(changes: {[propName: string]: SimpleChange}) {
        let key = 'files';
        this.files = changes[key].currentValue;
    }
}

```

Ben Richards
  • 3,437
  • 1
  • 14
  • 18
  • 1
    Thanks Boyan for your reply. I can't get it to work. Somehow `ngOnChanges` is never called. I updated my import: `import { Component, OnInit, OnDestroy, OnChanges, SimpleChange, Input } from "@angular/core";` – Paul Meems Mar 10 '17 at 21:06
  • 1
    Paul, you trigger the change detection by simply assigning the files result array to the input "files." – Ben Richards Mar 10 '17 at 21:22
  • 1
    You mean what I do in my `getFiles()`? I assign `this.files = data;` or do I need to assign it somewhere else? – Paul Meems Mar 10 '17 at 21:52
  • Create an input on the FilesList component (let's say "Data") and set it's data with filesListComponent,Data = data; – Ben Richards Mar 14 '17 at 01:11
0

We can mark the view for check and call detechchange.

@ViewChild('searchDt') searchTable: DataTable;


self.dealService.getAccounts(self.searchParams).subscribe((response) => {       
   Deal.setAvailableAccount(response.map((current) => {
     return {
        "cifNumber": current['cif'],
        "ucic": current['ucic'],
        "accountNomenclature": current['customerName'],
     }
   }));

   **self.searchTable.changeDetector.markForCheck();
   self.searchTable.changeDetector.detectChanges();**
});
blazehub
  • 1,880
  • 19
  • 25
0

For new people facing the same issue. I solved it by moving the p-table (or p-datatable) to a separate component and making the instanciation of this component dependent to a variable. Example:

<p-dialog [(visible)]="loading">
   <innercomponent *ngIf="loading"> <!-- contains the p-table -->
   </innercomponent>
</p-dialog>

Taking the example of the post, we can move the condition [hidden]="loading" of

<p-dataTable [hidden]="loading" ...> 

to the level of the wrapping component as shown before, the idea is to destroy and recreate the component in order to refresh table data

Youness Marhrani
  • 1,084
  • 16
  • 8
0

I found the solution after spending most of the time and all above solution won't work for me.

Below is the solution which I tackled,

 let newArr = [...this.arrayRecords, yourNewObject];
 this.arrayRecords = structuredClone(newArr);

What I did here is, I used structuredClone to get deep copy of newArr and then p-table will able to detect the changes.

er-sho
  • 9,581
  • 2
  • 13
  • 26