ng2-smart-table with paging from back-end (Spring)
Asked Answered
M

6

12

I am using a back-end server (Java Spring) that has Pager enabled. I am loading 100 records per page on a HTTP call.

On angular2 service, it is consuming the API call with "?page=1&size=100" as the initial call whereas on the client size pager, it shows 10 and moves up to 10 page which is fine. But I am unable to load the next chunk of data from server. I have checked ServerDataSource and used .setPaging(1,100).

How can I load the next chunk of data (2-200) and how can I achieve this. Any hints will be helpful.

@Injectable()
export class AmazonService extends ServerDataSource {

constructor(protected http: Http) {
    super(http);
}

public getAmazonInformation(page, size): Observable<Amazon[]> {

    let url = 'http://localhost:8080/plg-amazon?page=1&size=100';
    this.setPaging(1, 100, true);
    if (this.pagingConf && this.pagingConf['page'] && 
       this.pagingConf['perPage']) {
          url += 
       `page=${this.pagingConf['page']}&size=${this.pagingConf['perPage']}`;
}

return this.http.get(url).map(this.extractData).catch(this.handleError);
}

Thank you!

Mispickel answered 21/6, 2017 at 7:51 Comment(0)
S
8

I solved this problem with LocalDataSource.

HTML:

<ng2-smart-table [settings]="settings" [source]="source"></ng2-smart-table>

TS:

source: LocalDataSource = new LocalDataSource();
pageSize = 25;

ngOnInit() {
  this.source.onChanged().subscribe((change) => {
    if (change.action === 'page') {
      this.pageChange(change.paging.page);
    }
  });
}

pageChange(pageIndex) {
  const loadedRecordCount = this.source.count();
  const lastRequestedRecordIndex = pageIndex * this.pageSize;

  if (loadedRecordCount <= lastRequestedRecordIndex) {    
    let myFilter; //This is your filter.
    myFilter.startIndex = loadedRecordCount + 1;
    myFilter.recordCount = this.pageSize + 100; //extra 100 records improves UX.

    this.myService.getData(myFilter) //.toPromise()
      .then(data => {
        if (this.source.count() > 0){
          data.forEach(d => this.source.add(d));
          this.source.getAll()
          .then(d => this.source.load(d))
      }
        else
          this.source.load(data);
      })
  }
}
Stigmasterol answered 10/4, 2019 at 7:27 Comment(3)
Thanks for the example. How do you accurately load the pagination values when the localDataSource is not complete. I.e. say I have 10 pages of 25 data items (aka 250 records in total), I load the first 50 on page load, and want to show the user 10 pages in the pagination, so when the user click another page, it dynamically loads the data if it's not there.Elmerelmina
hey i use ur solution but i got this property 'then' does not exist on type 'object 'Studhorse
I think you should uncomment //.toPromise part. Error is about your "getData" function.Stigmasterol
L
1

Try to set the settings to the smart-table like this

<ng2-smart-table #grid [settings]="settings" ... >

And at your component, define the settings, something like this:

  public settings: TableSettings = new TableSettings();

  ngOnInit(): void {
      ...
    this.settings.pager.display = true;
    this.settings.pager.perPage = 100;
    ...
  }
Labyrinthodont answered 21/6, 2017 at 13:45 Comment(2)
Thank you, but this is for client side page setting which will show 100 records in a page which is in my response from first request, but my database has more than 100 records that I want to load in chunks of 100. My question was how and where to update the http request url with (page & size) when we reach the 100th element.Mispickel
smart-table will add the parameters to the request for you :) take a look on the ServerDataSource implementation, github.com/akveo/ng2-smart-table/blob/master/src/…, method addPagerRequestOptions( )Labyrinthodont
O
1

Also if you need to customize request to back-end there is configuration parameters for ServerDataSource that is used for data retrieving/pagination/sorting.

enter image description here

Ohalloran answered 16/8, 2018 at 7:48 Comment(0)
A
0

When you have your endpoint for some entity on {endPointBase}/ng2-smart-table, where you are processing key_like request params (i.e: @Requestparam Map to spring data Example) , you can use on client side:

export class SpringDataSource extends ServerDataSource {
    constructor(http: HttpClient, endPointBase: string) {
        const serverSourceConf = new ServerSourceConf();
        serverSourceConf.dataKey = 'content';
        serverSourceConf.endPoint = endPointBase + `/ng2-smart-table`;
        serverSourceConf.pagerLimitKey = 'size';
        serverSourceConf.pagerPageKey = 'page';
        serverSourceConf.sortFieldKey = 'sort';
        serverSourceConf.totalKey = 'totalElements';

        super(http, serverSourceConf);
    }

    protected addPagerRequestParams(httpParams: HttpParams): HttpParams {
        const paging = this.getPaging();
        return httpParams
            .set(this.conf.pagerPageKey, (paging.page - 1).toString())
            .set(this.conf.pagerLimitKey, paging.perPage.toString());
    }

    protected addSortRequestParams(httpParams: HttpParams): HttpParams {
        const sort: {field: string, direction: string}[] = this.getSort();

        sort.forEach((column) => {
            httpParams = httpParams.append(this.conf.sortFieldKey, `${column.field},${column.direction}`);
        });
        return httpParams;
    }
}
Aileen answered 9/4, 2020 at 12:0 Comment(1)
This example is without caching. But caching is problem, when we can use sorting and filtering based on DB queries on big table. 10 items is retrieved from server quickly. And our UX was improved by small pages retrieved from server.Aileen
S
0

This is my way of solving all these problems.

1: Make sure that the Backend (API) has cors enabled to show the client the "X-Pagination" header like this:

app.UseCors(builder => builder.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin().WithExposedHeaders(new string[] { "X-Pagination"}));

2: HTML:

<ng2-smart-table [settings]="settings" [source]="source"></ng2-smart-table>

3: SERVICE:

getClients(pageNumber: number, pageSize: number): Observable<any> {
            const headers = new HttpHeaders({'Content-Type': 'application/json','Accept': 'application/json' });
            let params = new HttpParams();
            params = params.append('PageNumber',pageNumber.toString());
            params = params.append('PageSize',pageSize.toString());
            return this.http.get<ClientDto[]>(API_URL, {headers: headers, observe: "response" as "body",  responseType: "json", params} );
          }

4: TS:

export class ListClientComponent implements OnInit {

  source: LocalDataSource;
  pageSize = 30;
  currentPage = 1;
  showPerPage = 10;
  totalCount;
  constructor(private service: ClientService,private router: Router) {
    this.initData();
  }

  public settings = {
    mode: 'external', 
    pager:{
      display: true,
      perPage: this.showPerPage,
    },
    actions: {
      add:false,
      edit:true,
      delete:false
    },
        columns: {
      clientId: {
        title: 'CI',
      },
      name: {
        title: 'Name'
      },
      instagramName: {
        title: 'Instagram'
      },
      type: {
        title: 'Type'
      },
      phone: {
        title: 'Phone'
      },
      gender: {
        title: 'Gender'
      },
      age: {
        title: 'Age'
      },
      city: {
        title: 'City'
      },
      email: {
        title: 'Email'
      }
    },
  };

  ngOnInit() {
    this.initOnChagedData();
  }

  onEdit(event){
    console.log(event.data.clientId);
    this.router.navigate(['/clients/update-client/'+ event.data.clientId]) ;  
  }

  initData(){
    this.source = new LocalDataSource();
    this.service.getClients(this.currentPage, this.pageSize).subscribe( (result: HttpResponse<any>) => {
      if(!result){
        return;
      }
      this.source.load(result.body);
      this.totalCount = JSON.parse(result.headers.get("X-Pagination"));
      this.totalCount = this.totalCount["totalCount"];
      console.log(this.source.count());
    }
    )
  }

  initOnChagedData(){
    this.source.onChanged().subscribe((change) => {
      if (change.action === 'page') {
        this.pageChange(change.paging.page);
      }
    });
  }

  pageChange(pageIndex) {
    var getNew = pageIndex * this.showPerPage;
    if( getNew >= this.source.count() && getNew < this.totalCount){
      this.currentPage = this.currentPage + 1;
      this.service.getClients(this.currentPage, this.pageSize).subscribe( result => {
        if(!result){
          return;
        }
        result.body.forEach(element => {
          this.source.add(element);
        });
      })
    }
  }
}

You should notice that pageSize = total data that we extract from the backend and show per page = total data that we show in the table.

The algorithm is designed to extract from the API only when necessary, that is, when the client reaches a page where the data ends in memory, then it asks for 30 more data to have another 3 pages, and also does not forget the other 3 that already had extracted before.

Selves answered 3/6, 2021 at 1:18 Comment(1)
Where are you setting totalCount to the table, to show page numbers below the table?Reconstitute
A
0

I had the same issue as pointed by @sovan while working with ng2-smart-table. After searching for a quick fix i landed on this thread which was really helpful expecially @mohammed answer. However i later discovered from this thread that the best way to achieve this is by using "ServerDataSource" @Oleksandr Yefymov. Below is the complete sample code to solve the problem. full source project available on stackblitz here https://stackblitz.com/edit/angular-ivy-jlq4bf?file=src/app/app.component.ts.

//Html Page

//Compnent Class

import { LocalDataSource, ServerDataSource } from 'ng2-smart-table';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  name = 'Angular ' + VERSION.major;
  constructor(private _http: HttpClient) {
 this.initData();
}

  source: LocalDataSource;
  pageSize = 30;
  currentPage = 1;
  showPerPage = 30;
  totalCount;

  public settings = {
    mode: 'external',
    pager: {
      display: true,
      perPage: this.showPerPage,
    },
    actions: {
      add: false,
      edit: true,
      delete: false,
    },
    columns: {
      name: {
        title: 'Passenger Name',
      },
      trips: {
        title: 'Trips',
      },
      airline_name: {
        title: 'Airline Name',
        valuePrepareFunction: (idx, air) => {
          return `${air.airline[0].name}`;
        },
      },
      airline_country: {
        title: 'Airline Country',
        valuePrepareFunction: (idx, air) => {
          return `${air.airline[0].country}`;
        },
      },
    },
  };

  initData() {
    this.source = new ServerDataSource(this._http, {
      dataKey: 'data',
      endPoint: 'https://api.instantwebtools.net/v1/passenger',
      pagerPageKey: 'page', // this should be page number param name in endpoint (request not response) for example 'page'
      pagerLimitKey: 'size', // this should page size param name in endpoint (request not response)
      totalKey: 'totalPassengers', // this is total records count in response, this will handle pager
    });
  }
}

Note: The "pagerPageKey" and "pagerLimitKey" is the actual name of the parameter in your endpoint. "totalKey" is the total record of all the records.

e.g: https://api.instantwebtools.net/v1/passenger?page=1&size=30

pagerPageKey:"page" pagerLimitKey:"size"

It is important to mention that the page indexing starts from 1 by default and there is no configuration by default to set the startindex. if your pagination starts from 0 as in the case of the api endpoint used in the above sample then there is a quick fix as suggested on this link https://github.com/akveo/ng2-smart-table/issues/858#issuecomment-439617491. All you have to do is create a new class CustomServerDataSource, extending the ServerDataSource class.

import { HttpParams } from "@angular/common/http";
import { ServerDataSource } from "ng2-smart-table";

export class CustomServerDataSource extends ServerDataSource {
    addPagerRequestParams(httpParams: HttpParams): HttpParams {
        if (this.pagingConf && this.pagingConf['page'] && this.pagingConf['perPage']) {
            httpParams = httpParams.set(this.conf.pagerPageKey, this.pagingConf['page'] + (-1));
            httpParams = httpParams.set(this.conf.pagerLimitKey, this.pagingConf['perPage']);
        }
        return httpParams;
    }
}

This is also implemented in the source code. just uncomment the line in the intData method to see it work.

Antevert answered 19/3, 2022 at 9:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.