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

[externalPaging]="true" causes rows to disappear? #391

Closed
pchristou opened this issue Dec 22, 2016 · 28 comments
Closed

[externalPaging]="true" causes rows to disappear? #391

pchristou opened this issue Dec 22, 2016 · 28 comments

Comments

@pchristou
Copy link
Contributor

Hi,

I'm trying to use server pagination but the moment I switch the externalPaging flag to true, I'm unable to get any rows to render. I've tried to follow the demo code and apply it to my own situation but still haven't got anywhere. Here's my code at the moment:

// in view...
            <ngx-datatable
                class="material"
                [rows]="rows"
                [columns]="columns"
                [columnMode]="'force'"
                [headerHeight]="50"
                [footerHeight]="50"
                [externalPaging]="true"
                [count]="count"
                [offset]="offset"
                [limit]="limit"
                (page)="onPage($event)"    
            >
            </ngx-datatable>

// further down...
    ngOnInit() {    
        this.taskService.testWithPaging(1).subscribe((tasks) => { 
            tasks.map((task, i) => { 
                    this.rows[i++] = { id: task.id, researcher: task.researcher, details: task.details };
            });
      });
    }

I've verified and the taskService method returns 100 objects with the properties id, researcher and details. This works without issue when I remove the externalPaging flag (or set it to false) but having it switched on, results in nothing being shown. Is the way in which I'm populating the "rows" variable incorrect?

@DanjBethel
Copy link

You need to set the count property to the total number of rows. If you have the count property set to 0 at the top, within your subscribe method you should set it to the length of the results returned.

@DanjBethel
Copy link

this.count = tasks.length;

@pchristou
Copy link
Contributor Author

@DanjBethel aha! Thank you, that's solved it. I think I misunderstood the way server side paging works. I've created a webservice method which allows me to get the next/previous 100 results by feeding it the page number (offset). Unfortunately, it looks as though the server side paging example keeps loading the same company.json file each time instead of a different segment. I'll have to have a play around to see if I can get the result I'm after i.e. loading a fresh batch of 100 objects per page.

@DanjBethel
Copy link

@pchristou I'm actually having the exact same issues with my implementation. Hence why I knew the answer to your question :)

Maybe we can help each other out? My server side APi also gives me my data in pages, I've tried simply sending the offset as the page number to the server, it works, however it breaks the paging in the table somehow.

@pchristou
Copy link
Contributor Author

@DanjBethel yeah, I see what you mean. Whilst count == limit the paging disappears.

Maybe an idea will be to set the count to be a number equivalent to your total results set and then on the new page callback, update the rows to show your next batch of results. I need to try it out as this is all theory but I admit, it seems quite hacky.. :(

@DanjBethel
Copy link

@pchristou Yeah. I'm not even entirely surely what the docs was trying to accomplish with its implementation, as I think most people will be coming from our angle.

After I send the offset to my api call, i retrieve the right batch of result. However, when I click the pagination numbers at the bottom, the data just disappears. It's not displaying the data when there is a change to the "rows" array. Maybe I need to reset it somehow?

@pchristou
Copy link
Contributor Author

@DanjBethel Yeah, I agree! No point implementing server side paging if all you're doing is returning the whole results set each time.

I've got something sort of working but it needs clean-up. Note that the sample below is simply 2 pages each with a 100 results. Takes a couple seconds to load but looks to be working. Thought I'd paste here to hopefully help you (and others) get past this initial block and perhaps we can find a more elegant solution!

So first off on page load I'm triggering the "page" function which loads the relevant page data into the rows variable.

// offset initial value = 0 
// limit initial value = 100
    ngOnInit() { 
        this.page(this.offset, this.limit);
    }

In the page function:

    page(offset, limit) {
      this.taskService.testWithPaging(offset).subscribe((tasks) => { 
          
            this.count = 200; // setting a hardcoded value of 200 for the moment to test paging between 2 pages
            this.rows = [...this.rows]; // this is necessary as we store all paging results in the row array before adding additional results from other pages. Removing this means you have to click page 1>2>1>2 before seeing the results on page 2

            tasks.map((task, i) => { 
// we simply add to the rows array where we left off before so page 2 will start from 100+index
            this.rows[((offset * limit)+ i++)] = { id: task.id, researcher: task.researcherName, details: task.details };
            });
      });
    }

Of course, I guess the "bad thing" with this is as the user clicks through pages, the row array is storing more and more results plus callbacks are happening when users click back to previously visited pages. Like I said, this is still a work in progress but hopefully helps folk.

@DanjBethel
Copy link

@pchristou

Yeah. I get what your doing but there must be an easier way. I have over 100,000 records for my table that I'm using this component for, so this solution will kill me for sure.

@amcdnl
Copy link
Contributor

amcdnl commented Dec 25, 2016

Can you make a demo?

@DanjBethel
Copy link

@pchristou Mind making a demo on our behalf? I can't atm. Family trip!

@pchristou
Copy link
Contributor Author

@DanjBethel @amcdnl sorry, I haven't the time to put a demo together at the moment. However, the crux of the problem looks to be the way the rows variable is used. For instance, if your table has 50 rows per page and you're viewing page 1, you need to make sure the rows[index] has values for [0-49] in order for the rows to render. If you then jump from page 1 to page 3, your rows variable must have values for indexes [100-149] as well as some sort of values for indexes [0-49] and [50-99].

With that said, what I've done for now is convert all indexes before the requested "page index" to NULL. So in short, if user requests page 3, I set indexes [0-49][50-99] to NULL values and populate [100-149] so page 3 results render. It works ok but it feels hacky and I shudder to think what will happen performance wise if a user requests a much higher page number. I suspect it works this way because the table was originally built with client-side paging in mind and I'm guessing storing the results like this in a rows var makes the paging quick and snappy?

I guess the preferred solution for server side paging would be to allow the user to feed in an 'ad-hoc' rows variable with indexes of [0-limit]. Of course this means that whenever you request a new page, a new request is sent so no caching is done by default, but that happens now anyway I think?

@DanjBethel
Copy link

@amcdnl If the last comment by @pchristou wasn't sufficient, give me some time to put together a demo this weekend.

Let me know

@smorcuend
Copy link

I have the same issue with the first load of first page.

Here an example: http://embed.plnkr.co/9d5uHNToxYJjMnQXe0Wg/

@radarsu
Copy link

radarsu commented Feb 23, 2017

@smorcuend, did you manage to resolve the issue somehow?

@Koshmaar
Copy link

Koshmaar commented Mar 10, 2017

@pchristou @DanjBethel Have you made any demo since then or can share some working code?

Also, how to display the paging controls on the bottom of table? I see them in online demo, but can't figure out where they are added in code.

EDIT: ok, I found how to enable paging controls. All ngx-datatable properties have to be specified in html, including those that looked to me like purely visual: [columnMode]="'force'" [rowHeight]="'auto'" [headerHeight]="50" [footerHeight]="50" And the count parameter is very important, have to be set to rows.length after adding has been finished.

@szanella
Copy link

I am having this issue too, and it kind of makes external paging pointless as you're still forced to provide the component a data structure bigger than a single page.
Are you accepting pull requests on this?

@amcdnl
Copy link
Contributor

amcdnl commented Apr 15, 2017

@szanella - Always.

I'm not sure I follow the issue. Have you seen the server pagination issue?

@leolorenzoluis
Copy link

leolorenzoluis commented Apr 18, 2017

@amcdnl I get this issue too. It loads the first page fine, but when I paginate to a different page then the rows disappear. However, I notice something that it tries to load the correct results if I navigate back to page 1.

I'm using version 7.3.0.

I have the following html:

<ngx-datatable #taskTable class='material' [columnMode]="'force'" [headerHeight]="50" [footerHeight]="50" [rowHeight]="'auto'" [rows]='rows' [externalPaging]="true" [count]="totalItemsCount" [limit]="limit" (page)="onPage($event)" [offset]="offset"
    [scrollbarV]="false" [scrollbarH]="false">

I have the following code:

page(offset, limit) {
        if (this.getHistorySubscription) {
            this.getHistorySubscription.unsubscribe();
        }
        this.getAllTasksSubscription = this.taskServiceApi.getHistoryWithHttpInfo(offset + 1, limit).subscribe(response => this.setData(response));
    }

    setData(response) {
        this.totalItemsCount = response.headers.get('x-total-count');

        const start = this.offset * this.limit;
        const end = start + this.limit;
        const rows = [...this.rows];
        const results = response.json();

      for (let i = start; i < end; i++) {
        rows[i] = results[i];
      }

      this.rows = rows;
      console.log('Page Results', start, end, rows);
    }

Originally, I thought the table just implements such row template, where it just reuses/destroys the rows component and populate it with the next batch of results. But it looks like I may be missing something internally that the component uses.

Also I have a question. Technically, if all rows are paginated from 0..n, the rows are just lazy loaded from the server side. So instead of loading them in one big list, it'll be chunked and by the time everything is paginated then the browser will have to store everything in memory?

Update
It also gets broken the moment a user sorts, and navigates to a different page.

@surfjedi
Copy link

Having same problem? needing to get pages in different sets of ten items from server on pagination.
Any progress?

@naku-i386
Copy link

@amcdnl
Just allow us to overwrite the offset.
For me setting the [offset]="alwaysZero" doesn't work.

I don't want to append data, i want to replace it, show pages depending on count returned from the database.

@szanella
Copy link

szanella commented Apr 27, 2017

When paginating externally, the behaviour I'd expect is that the table component should simply output any changes of the current page and be fed with the current page rows that are retrieved externally, usually through an Http request.

From what I've seen, this does not happen; what happens instead is that the table component always requires the full data set and moves its offset based on the current page.

As an example, suppose I have 10 items per page, and I move to the third page: what I'd expect to happen is I get an event that signals me I need to get data for the third page (this already works); then I get the 10 items for the third page and feed them as rows to the table.
What should happen now is that the table receives them and, knowing we're using externalPaging, simply displays them; what actually happens is that the table treats them as the full data set and tries to offset them to the third page (as if we weren't using externalPaging), resulting in an empty output.

This implies that pagination is still handled by the datatable component, and not externally, and makes the externalPaging function somewhat pointless.

Update: @amcdnl I reckon this is related to #138 , which should've been fixed by the pull request #714
I'm trying it out

@leolorenzoluis
Copy link

leolorenzoluis commented Apr 29, 2017

@szanella If you'd do that you'd face problem when the "select" feature is on. If I paginate from page 1 and I select all checkboxes, and paginate to page 3 using your design, then when I navigate back to page 1, the table no longer know if the page 1 rows are selected or not. Obviously this is because of the table just displaying the next 10 items for the third page.

I understand everyone have different use cases and might not need that functionality, and would really just want to display the next 10 items, so just modifying the [rows] may be good enough. However, I don't know how the component handles the rows internally.

@amcdnl
Copy link
Contributor

amcdnl commented Apr 30, 2017

Please retest in 9.0 :)

@leolorenzoluis
Copy link

@amcdnl The changelog breaking is not clear to me. "BREAKING: Fixes for external pagination fixes. Index calculation change being last page = 0 + pageSize (#714, #138, #391)"

What is breaking here and what do we do to upgrade to 9.0 with existing previous version?

@MeganAlexia
Copy link

Same problem here with the following :
<ngx-datatable class="material" [rows]="rows" [columns]="columns" [columnMode]="'force'" [headerHeight]="50" [footerHeight]="50" [rowHeight]="'auto'" [reorderable]="false" [externalPaging]="true" [count]="count" [offset]="query.offset" [limit]="query.limit" (page)='setPage($event)' (sort)='onSort($event)'> </ngx-datatable>

In .ts side :

ngOnInit(): void {
        this.query.setPagination(2, 0);
        this.count = 200;
        this.route.params.subscribe(
            (params) => {
                this.module = this.getModuleInstance(params['module']);
                this.init();
            }
        );
    }

    public init(): void {
        this.setPage();
        this.columns = columnCollection.getSettings().columns;
    }

    public setPage(page) {
        if (page) {
            this.query.offset = page.offset;
        }
        this.module.getTableData(this.query).subscribe(
            (response: any) => {
                this.rows = response;
            }
        );
    }

Rows disappear and I get the following error :

zone.js:518 TypeError: Cannot read property 'forEach' of undefined
at OrderableDirective../src/directives/orderable.directive.ts.OrderableDirective.ngOnDestroy

Is there something I am doing wrong ?

@IRCraziestTaxi
Copy link

IRCraziestTaxi commented Jul 6, 2017

Why does the count need to manually be set to the number of rows in the table? Here's my problem. I am using the table with an always-present "new item" row that can be filled out to add a new row to the table. The problem is that I must include that row in the count in order for all rows to be displayed, and I get "X total" in the table footer that includes the new item row, which is not correct since it is not yet an existing entity.

Why can't the [count] input simply show the number passed in and not worry about it when displaying rows?

Edit: I discovered I can get around this by using a footer template, so disregard my complaint. I still think it's strange that the table depends on the count input to render rows rather than simply display the number in the footer.

@katestearns-suplari
Copy link

I am getting a similar issue. I am using server-side pagination. I have the count set to the total number of rows in the db: Around 89k. I am showing 20 rows at a time. The page count at the bottom only shows pages 1 - 5.

If I click the arrow to go to the last available page the whole table and pagination disappears.

@Hypercubed
Copy link
Contributor

Please reopen if this is still an issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests