Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TableState for Table #304

Closed
gatapia opened this issue May 3, 2016 · 65 comments
Closed

TableState for Table #304

gatapia opened this issue May 3, 2016 · 65 comments
Assignees
Labels
Type: New Feature Issue contains a new feature or new component request
Milestone

Comments

@gatapia
Copy link
Contributor

gatapia commented May 3, 2016

It would be good to be able to remember the state of the table to be able to give the user continuity as they use an application. This state would include:

  • sorts
  • filters
  • column widths (if resizeable)
  • page number
  • expanded state?
  • toggled columns?

A nice clean API to get and set this would be great:

# template
<p-datatable (stateChanged)="onStateChanged($event)" 
    [initialState]="loadInitialState()"...

# component
onStateChanged(event: any) {
  localStorage.set('table-state', event.data);
}

loadInitialState() {
  return localStorage.get('table-state');
}

Or perhaps even a totally automatic API, like:

<p-datatable persistState="true"...
@GraemeSMiller
Copy link

Would be a great feature. Automatically is a nice to have. Best for me if it exposed functions so can hookin and save to remote storage etc

@gatapia
Copy link
Contributor Author

gatapia commented May 9, 2016

Just looking at source now and it looks like accessing the sort/filter state is simple enough, just attach to onFilter/onSort events. There appears to be now way to set these values back.

Somewhat related, onSort fires the onFilter event even if filter has not changed, i.e. filter data (get filter event), sort data (get filter event then sort event). I think that filter event is redundant. minor issue though.

@vijosoco
Copy link

Is there any update on this issue. I am using the LazyLoad event to store the sort and filter information. I am using local storage (which isn't ideal - but it works). Now when i navigate away from the page with my datatable i can persist those values. Once i return to that page (onInit) i can get those values back from localstorage and use them in very next query (lazy load). HOWEVER, there seems to be no way to visually update the data table UI to show that those filters are being used or that the sort order on a column has changed.

Is there any way to update the datatable header fields on the UI?

lbamburski added a commit to lbamburski/primeng that referenced this issue Sep 23, 2016
…ns style, toogled columns, filters.

Fixes:
[New Feature] DataTable get/set state (for remembering sorts, filters, etc) primefaces#304
@lbamburski
Copy link
Contributor

This pull request solves this. It uses localStorage for lazy load. Everything is updated visually. For integration with MultiSelect for toggle columns, event onPersistentStateApplied should be used similar to this:

onPersistentStateApplied(event): void {
if (!event.state || !event.state.visibleColumnsState) {
return;
}

    let eventColumns = event.state.visibleColumnsState;

    this.columns.splice(0, this.columns.length);

    for (let i = 0;i < eventColumns.length;i++) {
        let eventColumn = eventColumns[i];
        this.columns.push({
            field: eventColumn.field,
            header: eventColumn.header,
            style: eventColumn.style
        });
    }

    let columnsOptionsByLabel = {};
    for(let i = 0; i < this.columnOptions.length; i++) {
        columnsOptionsByLabel[this.columnOptions[i].label] = this.columnOptions[i];
    }

    for(let j = 0; j < this.columns.length; j++) {
        let columnOption = columnsOptionsByLabel[this.columns[j].header];
        columnOption.value = this.columns[j];
    }
}

@vijosoco
Copy link

Any news on when we might see this released?

@cagataycivici
Copy link
Member

This is on roadmap;

https://github.com/primefaces/primeng/wiki/Roadmap

Closing for now and will create a new ticket with details when we pick it up from roadmap after 1.0.
At the moment, we're closing tickets that duplicate the upcoming features in roadmap as a part of issue tracker maintenance.

@pchristou
Copy link

@cagataycivici thanks for the roadmap link - are there any dates pencilled in for when this feature will be released?

@asnp87
Copy link

asnp87 commented Feb 22, 2017

any update on this?

@cagataycivici
Copy link
Member

Planned for 2.1 due late march.

@cagataycivici cagataycivici reopened this Feb 22, 2017
@cagataycivici cagataycivici self-assigned this Feb 22, 2017
@cagataycivici cagataycivici changed the title [New Feature] DataTable get/set state (for remembering sorts, filters, etc) TableState for DataTable Mar 9, 2017
@cagataycivici
Copy link
Member

Similar to PrimeFaces TableState;

https://www.primefaces.org/showcase/ui/data/datatable/tableState.xhtml

@cagataycivici cagataycivici added the Type: New Feature Issue contains a new feature or new component request label Mar 9, 2017
@asnp87
Copy link

asnp87 commented Apr 27, 2017

@cagataycivici do you have a new planning for this feature?

@cagataycivici
Copy link
Member

Yes, but can't give a specific date right now. Rough estimation is in 2 months.

@TimKohler
Copy link

Love ngPrime but this is a major issue with an otherwise great data table. In the meantime, are there any events we could subscribe to and properties we could set that would allow us to implement this ourselves?

@jamesgroat
Copy link

jamesgroat commented May 11, 2017

@TimKohler -- I solved this manually using something like this:

public gridOptions = {
    first: 0,
    rows: 10,
    sortField: 'title',
    sortOrder: 1
  };

onSort(e: { field: string, order: number }) {
    this.gridOptions.sortField = e.field;
    this.gridOptions.sortOrder = e.order;
    this.gridOptions.first = 0;
    this.localStorageService.set('my-grid-options', this.gridOptions);
  }
  onPage(e: { first: number, rows: number }) {
    this.gridOptions.rows = e.rows;
    this.gridOptions.first = e.first;
    this.localStorageService.set('my-grid-options', this.gridOptions);
  }

 ngOnInit() {
  // after loading the table data then read the options and
  // if options exist set them wrapped in setTimeout():

      const opt = this.localStorageService.get('my-grid-options');
      if (opt) {
        setTimeout(() => {
          this.gridOptions = opt;
          this.dataTable.sortField = this.gridOptions.sortField;
          this.dataTable.sortOrder = this.gridOptions.sortOrder;
          this.dataTable.sortSingle();
          this.dataTable.paginate(this.gridOptions);
          this.blocked = false;
        }, 0);

        return;
      }
 }

The table looks like:

<p-dataTable #dt [value]="sets" (onRowClick)="select($event.data)" [paginator]="true" 
paginatorPosition="both" [pageLinks]="10" [rows]="10" [rowsPerPageOptions]="[5,10,20,30,40]"
(onPage)="onPage($event)" (onSort)="onSort($event)">

@TimKohler
Copy link

TimKohler commented May 11, 2017

@jamesgroat Thank you! This is a great workaround. Got it working well.

@cagataycivici
Copy link
Member

Yes, there are events to use until this is built-in.

@latchkostov
Copy link

I cannot get pagination to stick with the newest version of PrimeNG, since paginate() no longer accepts any arguments.

Does anyone have a solution? Here is my current code:

onPage(e: { first: number, rows: number }) {
    console.log(this.dataTable.first);
    this.gridOptions.rows = e.rows;
    this.gridOptions.first = e.first;
    localStorage.setItem(this.optionsKey, JSON.stringify(this.gridOptions));
  }
  
  const opt = localStorage.getItem(this.optionsKey);
    const filters = localStorage.getItem("filters");
    if (opt) {
        let go = JSON.parse(opt);
        setTimeout(() => {
            this.gridOptions = go;
            this.dataTable.sortField = this.gridOptions.sortField;
            this.dataTable.sortOrder = this.gridOptions.sortOrder;
            this.dataTable.sortSingle();
            this.dataTable.first = this.gridOptions.first;
            this.dataTable.rows = this.gridOptions.rows;
            this.dataTable.paginate();
            //this.dataTable.paginate(this.gridOptions);
            
            if (filters) {
                console.log(JSON.parse(filters));
                this.dataTable.filters = JSON.parse(filters);
            }
      }, 0);

@jamesgroat
Copy link

@blgrnboy Try this:

this.gridOptions = opt;
this.dataTable.sortField = this.gridOptions.sortField;
this.dataTable.sortOrder = this.gridOptions.sortOrder;
this.dataTable.sortSingle();
this.dataTable.onPageChange(this.gridOptions);

I don't think you need these:

this.dataTable.first = this.gridOptions.first;
this.dataTable.rows = this.gridOptions.rows;

@latchkostov
Copy link

@jamesgroat I tried that, with no success.

@jamesgroat
Copy link

@blgrnboy make sure you are calling that code after you have loaded your data?

@latchkostov
Copy link

@jamesgroat My sort and filter settings are working and persisting as expected, so I would expect pagination to work as well. I did try moving around the code anyway, but still doesn't seem to work.

@cagataycivici
Copy link
Member

cagataycivici commented Oct 24, 2018

This is happening but should we use sessionstorage, localstorage or a service for this? We can't decide over here :). Any ideas?

@umdstu
Copy link

umdstu commented Oct 24, 2018

When I implemented this with angularjs's ui-grid, I used localStorage. My customers want their states to be there when they come back another day. sessionStorage is simply too short lived for this kind of thing. And technically if you used localStorage, you could also use an expiration date kinda deal, so the developer could choose how long it's stored. But that's not really common i'd guess. That said, the option/ability to store this server-side would also be nice, via a provided callback of some sort.

Just my .02 cents.

@AlejandroFlorin
Copy link

I implemented it using Session Storage as we didn't need it to persist between browser sessions but local storage gives devs the most options as they can clear it on logout if they want. A service would lose state between browser windows so that would be the most restrictive. I vote Local Storage since it is the most flexible IMO.

@bossqone
Copy link
Contributor

What about providing some storage service interface (with default implementation e.g. localstorage) which could be implemented by user when needed, and overridden at DI level (whole app / per module / per component)?

I also suggest to make this feature optional (disabled by default), because from my perspective table should contain minimal (or none) internal state.

@nandeshnair
Copy link

nandeshnair commented Oct 24, 2018 via email

@umdstu
Copy link

umdstu commented Oct 25, 2018

As part of this, would it make sense to add a new supported state to the table, specifically column visibility? Right now it's only possible by ngIf'ing the column. If one were to add a way for users to hide columns (outside of p-table), there would likely not be a way to pass that into whatever state is saved as it stands.

@AizeLeOuf
Copy link

2 years for a mandatory feature.. who use a table with filters without saving it ? :/

@cagataycivici
Copy link
Member

In progress...

@malbarmavi
Copy link

Is this issue include the state of columns resize ? I have manage the state of order and toggle but I can't find any solution for resize

https://stackblitz.com/edit/angular-primeng-table-order-resize-toggle

@cagataycivici
Copy link
Member

Implemented now, usage is simple as defining stateStorage to selected where to keep it e..g sessionStorage or localStorage along with the state key;

<p-table[columns]="cols" [value]="cars" stateStorage="session | local" stateKey="key1">

Currently supported features that can be stateful are;

  • page
  • sort
  • filter
  • selection
  • column resize
  • column reorder
  • row expansion

I'll mark it as resolved after doing the docs tomorrow. Thank you for all the feedback on the initial version.

@cagataycivici
Copy link
Member

Ready for a test drive in upcoming 7.0.0-RC1.

@nandeshnair
Copy link

nandeshnair commented Nov 14, 2018 via email

@nsksaisaravana
Copy link

Thanks, guys for all your hard work.

@AizeLeOuf
Copy link

This feature doesn't work for me =>

<p-table #dt id="jobs-table" class="table-style" [columns]="cols" [value]="jobs" stateStorage="local" stateKey="p-table-jobs-{{ this.user.usernameCanonical }}" [loading]="loading" sortField="startTime" [lazy]="true" (onLazyLoad)="loadLazy($event)" [rows]="25" [totalRecords]="totalFiltered" [responsive]="true" [scrollable]="true" [scrollHeight]="this.style.height" selectionMode="multiple" [(selection)]="selectedJobs" [metaKeySelection]="true" virtualScroll="virtualScroll" [virtualRowHeight]="32" [(contextMenuSelection)]="selectedJobs" [contextMenu]="cm" contextMenuSelectionMode="joint" [resizableColumns]="true" [reorderableColumns]="true">
In my local storage

{"sortField":"startTime","sortOrder":1,"filters":{"username":{"value":["Paul"],"matchMode":"in"},"program":{"value":["NextGenApp"],"matchMode":"in"}},"columnWidths":"157,158,158,158,158,157,157,157","columnOrder":["jobid","username","jobname","program","startTime","duration","accelerability","accelerators"]}

new feature are correctly take into account in p-table but in the page, filters are always empty Oo
Have you plan to load SaveState in another PR ? Release ? or planned to work right now ?

@AlejandroFlorin
Copy link

State retention of filter entry controls does not work. State retention of the currently selected Page DOES work. I haven't tested sorting.

I'm using the recently released 7.0.0 production version

Not sure if it makes a difference but the headings and columns on my table are manually defined (not dynamically built) and the filter controls are bound using model driven form (formcontrolname = ...)

@umdstu
Copy link

umdstu commented Dec 7, 2018

I haven't had a chance to test this yet, but based on comments above, it looks like this doesn't support visibility of columns? aka. you'd have to track them outside of this state management functionality, then merge in as necessary. Kinda lame :( Is this something on the todo list @cagataycivici ?

@wombll
Copy link

wombll commented Dec 10, 2018

State retention of filter entry controls does not work. State retention of the currently selected Page DOES work. I haven't tested sorting.

I'm using the recently released 7.0.0 production version

Not sure if it makes a difference but the headings and columns on my table are manually defined (not dynamically built) and the filter controls are bound using model driven form (formcontrolname = ...)

I had this issue at first but if you check the code snippet in the manual it has col.filterMatchMode and [value]="tt.filters[col.field]?.value" which holds the state of the filters.

    <ng-template pTemplate="header" let-columns>
        <tr>
            <th *ngFor="let col of columns" pResizableColumn [pSortableColumn]="col.field"><p-sortIcon [field]="col.field"></p-sortIcon>{{col.header}}</th>
        </tr>
        <tr>
            <th *ngFor="let col of columns" [ngSwitch]="col.field"><input pInputText type="text" (input)="tt.filter($event.target.value, col.field, col.filterMatchMode)" [value]="tt.filters[col.field]?.value"></th>
        </tr>
    </ng-template>

@AlejandroFlorin
Copy link

AlejandroFlorin commented Dec 10, 2018

@wombll. Thank but I'm using paging, lazyloading and model driven controls and it isn't clear how to use the State feature with those. I can see the state information being stored in sessionStorage for the table's defined session key but the onlazyload event is not using it consistently. I'm going to post a question on the forums to see if I can get a working sample with lazyloading and model driven controls using the new state feature.

@ghost
Copy link

ghost commented Dec 10, 2018

@AlejandroFlorin I'm also using the same PrimeNG approach with Table onLazyLoad nad $event value doesn't take stored values consistently as you've mentioned.

If anyone of you have an example of TableState connected with lazyload, will be very grateful.

@AlejandroFlorin
Copy link

After I posted on the primeng forums, I realized that I was expecting a bit too much from the new functionality. It basically just provides a built in session object with automatic storage during the onDeactivate event. Though that makes sense after thinking about it. I wound up using the same technique as I had before for lazy loaded pages and model driven controls but just using the built in session object. Here is my post with my code in case it helps anyone:

https://forum.primefaces.org/viewtopic.php?f=35&t=57640&p=172030#p172030

@nkm96
Copy link

nkm96 commented Dec 2, 2019

Is there any way we use state for saving toggle columns too?

@umdstu
Copy link

umdstu commented Dec 2, 2019

See my comment above. It can be done but you have to roll it yourself, not what's built into primeng

@nkm96
Copy link

nkm96 commented Dec 3, 2019

thanks, I handle it by saving columns in local storage.

@xylplm
Copy link

xylplm commented Mar 24, 2020

我希望自动保存TableState可以选择保存项。就像我一样,我只需要保存rows,而不需要保存其他。我们可以写一个数组作为可选功能,如['rows']。这样我就只保存rows配置。

atretyak1985 added a commit to Nanitor/primeng that referenced this issue Jul 18, 2020
@h1ghland3r
Copy link

thanks, I handle it by saving columns in local storage.

Do you mind to share your solution?

@ryann3588
Copy link

It would be nice to choose which properties of the table to include in the state save/restore. We are having an issue with the selection state. It is possible to have enough items in the table to where it exceeds the storage capacity of browsers. If the select all toggle is used it causes an error.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: New Feature Issue contains a new feature or new component request
Projects
None yet
Development

No branches or pull requests