Material 2 Pagination With Http And DataSource
Asked Answered
P

2

15

I am having trouble with Material Pagination working with DataSource. I need to get back from the Service the total items from the collation, which the REST service provides (I can't change it). So far the only way I could do it was to do the same request twice, and that's just not right.

If you look at the "servers-collection.component.ts" I call a "getServiceData" just to get the total items for the pagination ("total_items" in the request response). What I'm looking for is to get the data straight from the main request.

This is the raw response I have (GET):

{
    "_links": {
        "self": {
            "href": "https://{URL}/servers?page=1"
        },
        "first": {
            "href": "http://{URL}/servers"
        },
        "last": {
            "href": "http://{URL}/servers?page=100"
        },
        "next": {
            "href": "http://{URL}/servers?page=2"
        }
    },
    "_embedded": {
        "servers": [
            {
                "id": 1,
                "name": "Server Name",
                "ipAddress": "111.222.333.444",
                "hostName": "server.hostname.com",
                "_links": {
                    "self": {
                        "href": "http://{URL}/servers/1"
                    }
                }
            }
        ]
    },
    "page_count": 100,
    "page_size": 5,
    "total_items": 498,
    "page": 1
}

... and this is what I did to deal with it:

servers-collection.component.html

<div class="example-container mat-elevation-z8">
    <mat-table #table [dataSource]="dataSource">
        <!-- ID Column -->
        <ng-container matColumnDef="id">
            <mat-header-cell *matHeaderCellDef> No. </mat-header-cell>
            <mat-cell *matCellDef="let row"> {{row.id}} </mat-cell>
        </ng-container>

        <!-- Name Column -->
        <ng-container matColumnDef="name">
            <mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
            <mat-cell *matCellDef="let row"> {{row.name}} </mat-cell>
        </ng-container>

        <!-- IP Address Column -->
        <ng-container matColumnDef="ipAddress">
            <mat-header-cell *matHeaderCellDef> IP Address </mat-header-cell>
            <mat-cell *matCellDef="let row"> {{row.ipAddress}} </mat-cell>
        </ng-container>

        <!-- Host Name Column -->
        <ng-container matColumnDef="hostName">
            <mat-header-cell *matHeaderCellDef> Hostname </mat-header-cell>
            <mat-cell *matCellDef="let row"> {{row.hostName}} </mat-cell>
        </ng-container>

        <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
        <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
    </mat-table>

    <mat-paginator #pagination
                   [length]="length"
                   [pageSize]="pageSize"
                   [pageSizeOptions]="pageSizeOptions"
                   (page)="pageEvent = loadData($event)">
    </mat-paginator>
</div>

servers-collection.component.ts

import { Component, OnInit, ViewChild } from '@angular/core';
import { MatPaginator, PageEvent } from "@angular/material";

import { ServersService } from "../../../shared/services/servers/servers.service";
import { ServersDataSource } from "../../../shared/data-source/server.datasource";

@Component({
  selector: 'app-servers-collection',
  templateUrl: './servers-collection.component.html',
  styleUrls: ['./servers-collection.component.css']
})
export class ServersCollectionComponent implements OnInit {
    // Display Data
    displayedColumns = ['id', 'name', 'ipAddress', 'hostName'];
    dataSource:ServersDataSource|null;
    pageEvent:PageEvent;

    //Pagination
    length:number;
    pageSize:number = 10;
    pageIndex:number = 1;
    pageSizeOptions:number[] = [5, 10, 25, 50, 100];

    @ViewChild(MatPaginator) pagination: MatPaginator;

    constructor(public serversService:ServersService) {
    }

    ngOnInit() {
        this.loadData();
    }

    loadData() {
        this.serversService.getServiceData(this.pageIndex, this.pageSize).subscribe(serviceData => {
                this.length = serviceData['total_items'];
            }
        );

        this.dataSource = new ServersDataSource(this.serversService, this.pagination);
    }
}

server.datasource.ts

import { DataSource } from '@angular/cdk/collections';
import { Observable } from 'rxjs/Observable';
import { MatPaginator } from "@angular/material";

import { Server } from "../interfaces/Server";
import { ServersService } from "../services/servers/servers.service";

export class ServersDataSource extends DataSource<any> {
    constructor(private _serverService: ServersService, private _pagination : MatPaginator) {
        super();
    }

    connect(): Observable<Server[]> {
        return this._serverService.getServers(this._pagination.pageIndex + 1, this._pagination.pageSize);
    }

    disconnect() {}
}

servers.service.ts

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from "rxjs/Observable";
import { Server } from "../../interfaces/Server";
import 'rxjs/add/operator/map';

@Injectable()
export class ServersService {
    url:string = 'http://{URL}/servers';
    servers:Server[];

    constructor(private http: Http) {
    }

    getServiceData(startIndex, pageSize) {
        return this.http.get(this.url + '?page=' + startIndex + '&limit=' + pageSize)
            .map(response => response.json());
    }

    getServers(startIndex, pageSize): Observable<Server[]> {
        return this.http.get(this.url + '?page=' + startIndex + '&limit=' + pageSize)
            .map(this.extractData);
    }

    extractData(result: Response): Server[] {
        return result.json()['_embedded']['servers'];
    }
}

Thank you all in advance!

Pinnace answered 22/10, 2017 at 22:5 Comment(4)
This problem is very similar to the one I am having and the help is phenomenal. Thank you very much.Putrid
Great you found a solution! Instead of editing your question, consider adding your solution as an answer and mark it as solved. You've put so much effort into it, it'd be a shame if it wasn't found when needed.Repast
Additionally, giving the answer and accepting it will prevent it from showing up as "unanswered"Retarder
Just asking, have you seen this event in documentation in angular material?Weintraub
P
9

Found the answer myself. It turns out it was scope (that I wasn't understanding properly).

I can't set a variable directly inside of the Observer to the class. What I can do is to call a method and pass what I need to it. Might look basic but took me a week to arrive here.

So this is how the coded ended up being:

servers-collection.component.ts:

import { Component, OnInit } from '@angular/core';
import { ServersService } from "../../../shared/services/servers/servers.service";
import { ServersDataSource } from "../../../shared/data-source/server.datasource";

@Component({
    selector: 'app-servers-collection',
    templateUrl: './servers-collection.component.html',
    styleUrls: ['./servers-collection.component.css']
})
export class ServersCollectionComponent implements OnInit {
    // Data
    displayedColumns = ['id', 'name', 'ipAddress', 'hostName'];
    dataSource:ServersDataSource|null;
    data:any;

    // Pagination
    length:number;
    pageIndex:number = 1;
    pageSize:number = 10;
    pageSizeOptions:number[] = [5, 10, 25, 50, 100];

    constructor(private _serversService:ServersService) {
    }

    ngOnInit() {
        this.loadData();
    }

    loadData() {
        this.data = this._serversService.getData(this.pageIndex, this.pageSize);
        this.data.subscribe(data => {
            this.setPagination(data['total_items'], data['page'], data['page_size']);
            this.dataSource = new ServersDataSource(data['_embedded']['servers']);
        });
    }

    setPagination(length, startIndex, pageSize) {
        this.length = length;
        this.pageIndex = startIndex;
        this.pageSize = pageSize;
    }

    onPaginateChange(event) {
        this.pageIndex = event.pageIndex;
        this.pageSize = event.pageSize;
        this.loadData();
    }
}

server.datasource.ts:

import { DataSource } from '@angular/cdk/collections';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import { Server } from "../interfaces/Server";

export class ServersDataSource extends DataSource<any> {
    constructor(private _servers:Server[]) {
        super();
    }

    connect(): Observable<Server[]> {
        return Observable.of(this._servers);
    }

    disconnect() {}
}

servers.service.ts:

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from "rxjs/Observable";
import 'rxjs/add/operator/map';

@Injectable()
export class ServersService {
    url:string = 'http://{URL}/servers';

    constructor(private http: Http) {
    }

    getData(startIndex, pageSize): Observable<any> {
        return this.http.get(this.url + '?page=' + startIndex + '&limit=' + pageSize)
            .map(this.extractData);
    }

    extractData(result: Response) {
        return result.json();
    }
}
Pinnace answered 15/6, 2018 at 6:26 Comment(1)
Thanks a lot! You saved me.Tortuga
C
2

Thanks a lot for this piece of code. I've used paginator like this:

<mat-paginator #pagination [length]="length" 
[pageSize]="pageSize 
[pageSizeOptions]="pageSizeOptions" 
(page)="pageEvent = onPaginationChange($event)"> 
// instead of loadData($event) 
</mat-paginator>

I also modified this since first page is indexed as 0 and on my data as 1

 onPaginateChange(event) {
    this.pageIndex = event.pageIndex + 1;
    this.pageSize = event.pageSize;
    this.loadData();
}
Confess answered 25/12, 2018 at 20:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.