Exploring the process of linking MatPaginator to a server-sourced datasource within an Angular Material table

In my Angular 5 project, I am utilizing Angular Material table to create a grid. The data is fetched from an HTTP request and stored in a variable named dataSourceNew within the view.ts file. Since dataSourceNew has dynamic content and structure, no interface is exported.

      this.http.get('http://example.org?,{headers: this.headers})
          .subscribe(
          res => { 

           this.displayedColumnsNew = res.record;
           this.dataSourceNew = res.data;
           this.matdatasource = new MatTableDataSource(this.dataSourceNew);
           this.dataSourceNew.paginator = this.paginator;
           console.log("got matdatasource object",this.matdatasource);
      
         });

I have successfully created a data-table using the provided syntax in the HTML file.

     <div class="example-container mat-elevation-z8">
           <mat-table #table [dataSource]="dataSourceNew">


       <ng-container  *ngFor="let head of tableData.record; let i= index; " matColumnDef="{{head}}" (click)="setClickedRow(ddata) "  [class.active]="ddata[primaryid] == selectedRow">
               <mat-header-cell *matHeaderCellDef> {{tableData.gridhead[i]}} 
                </mat-header-cell>
              <mat-cell *matCellDef="let element"> {{element[head]}} </mat-cell>
         </ng-container>


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

To add pagination to the table, I declared this:

 @ViewChild(MatPaginator) paginator: MatPaginator;

However, when I tried attaching the pagination to the table in the HTML file as shown below:

  <mat-paginator #paginator
                 [pageSize]="10"
                 [pageSizeOptions]="[5, 10, 20]"
                 [showFirstLastButtons]="true">
  </mat-paginator>

An error occurred in the console that broke my application. Despite importing MatPaginator and MatTableDataSource in the respective .ts file:

Uncaught Error: Template parse errors:
Can't bind to 'pageSize' since it isn't a known property of 'mat-paginator'.
1. If 'mat-paginator' is an Angular component and it has 'pageSize' input, then verify that it is part of this module.
2. If 'mat-paginator' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.
3. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.

The datasource log revealed that the paginator element of this object is empty. How can I utilize the Angular Material paginator without creating an interface?

Update :

Following David's suggestion, I added MatPaginator to the imports of the main app module. Now, the pagination block displays perfectly.

However, the Current Records indicate "0 of 0," even though there are 14 records, and features such as changing the number of records, next, previous, etc., do not work. What could be missing?

Update 2: By adding the Length property to the paginator tags, the pagination functions properly. The issue now lies with the datatable not adjusting the current number of rows.

Update 3: Upon implementing Pierre Mallet's suggestion, total records are inferred from the API response without explicitly defining the [length]. The paginator is correctly bound with my

this.matdatasource = new MatTableDataSource([]);
. However, I wish to initially fetch a few records (let's say 20) and subsequently call the database for more requests, avoiding hitting the DB at once for all records. How can this be achieved?

Answer №1

To properly connect your MatTableDataSource to the paginator, you need to ensure that the paginator is instantiated first. This can be achieved by listening to the AfterViewInit hooks and accessing the paginator with

@ViewChild(MatPaginator) paginator: MatPaginator;

Since your data is asynchronous, follow these steps:

  1. Create an empty DataSource initially.
  2. Populate the dataSource once your data is fetched.
  3. Link the paginator after the view has been fully rendered.

Give it a try and see if this approach works for you!

export class YourComponent implements AfterViewInit {

  private matdatasource;

  // Access the paginator element
  @ViewChild(MatPaginator) paginator: MatPaginator;

  constructor (private http: HttpClient) {
    // Step 1: Initialize DataSource
    this.matdatasource = new MatTableDataSource([]);
    this.http.get('http://example.org',{headers: this.headers})
      .subscribe(res => { 
        // Step 2: Populate Data
        this.matdatasource.data = res.data;
      });
  }
   
  // Step 3: Link the paginator when the empty dataSource is ready and paginator is rendered
  ngAfterViewInit() {
    this.dataSource.paginator = this.paginator;
  }
  
}

Answer №2

To start, create an instance of the paginator

@ViewChild(MatPaginator) paginator: MatPaginator;

Next, fill in the dataSource with your data

this.matdatasource = new MatTableDataSource([])

Lastly, connect the paginator to the dataSource for functionality

this.dataSource.paginator = this.paginator;

For more information on this topic, refer to this link

Answer №3

If you're looking for a simple solution to set up your database and data source, consider using @matheo/datasource. It streamlines the process and eliminates any headaches along the way.

Having worked with various APIs that include different pagination responses (including those without pagination like Firebase), I have successfully managed total counts for all scenarios without any issues.

For more information, visit:
https://medium.com/@matheo/reactive-datasource-for-angular-1d869b0155f6

If you decide to use the library and need assistance with your specific use case, feel free to reach out. Happy coding!

Answer №4

Presented here is a simplified version of the concept proposed by @mateo-tibaquira. The key points are as follows:

  • Utilizing DataSource in combination with Observable to capture the transient loading state and any errors that may occur during the service call.
  • Adjusting the paginator's length based on the total count obtained from the backend service call.
  • Introducing a generic class Page<T> to encapsulate the response array of type T, along with essential pagination details: pageSize, pageIndex, and totalCount.
  • Assumption that the backend calls support pagination parameters compatible with <mat-table> and <mat-paginator>.

This code draws inspiration from, and heavily relies on, the well-crafted Angular Material blog post titled Angular Material Data Table: A Complete Example (Server Pagination, Filtering, Sorting).

custom-data-source.ts

import { map, catchError, finalize,
         debounceTime, distinctUntilChanged, startWith, tap, delay 
       } from "rxjs/operators";

import { CollectionViewer, DataSource } from "@angular/cdk/collections";
import { MatPaginator }                 from '@angular/material';

import { Page } from '../model/page';

export class CustomDataSource<T> implements DataSource<T>, Observer<Page<T>> 
{
    private items = new BehaviorSubject<Page<T>>( new Page<T>() );
    private loading = new BehaviorSubject<boolean>(false);
    private err = new BehaviorSubject<any>( {} );

    protected paginator: MatPaginator;

    items$ = this.items.asObservable();
    loading$ = this.loading.asObservable();
    error$ = this.err.asObservable();

    closed = false;

    constructor( protected itemType: string ) {
    }

    next( results: Page<T> ) : void {
        console.debug( "Found items: ", this.itemType, results );
        if ( this.paginator ) {
            this.paginator.length = results.totalCount;
        }
        this.items.next( results );
    }
    error( errr: any ) : void {
        console.error( "Error loading items: ", this.itemType, errr );
        this.err.next( errr );
    }
    complete() : void {
        console.debug( "Done loading items.", this.itemType );
        this.loading.next( false );
    }

    connect( collectionViewer: CollectionViewer ) : Observable<Page<T>> {
        this.closed = false;
        return this.items$;
    }

    disconnect( collectionViewer: CollectionViewer ): void {
        this.closed = true;
        this.items.complete();
        this.loading.complete();
        this.err.complete();
    }

    protected preLoad() : void {
        this.err.next( {} );
        this.loading.next( true );
    }

    setPaginator( pgntr: MatPaginator ) : void {
        this.paginator = pgntr;
    }
}

model/page.ts

export class Page<T> extends Array<T> {
    public static readonly DEFAULT_PAGE_SIZE: number = 10;

    pageIndex: number;
    pageSize: number;
    totalCount: number;

    constructor( pgIx?: number, pgSize?: number, tot?: number, items?: T[] )
    {
        super();
        this.pageIndex = pgIx ? pgIx : 0;
        this.pageSize = pgSize ? pgSize : 0;
        this.totalCount = tot ? tot : 0;
        if ( items && items.length > 0 ) {
            this.push( ...items );
        }
    }
}

xyz-data-source.ts

import { Xyz } from '../model/xyz';
import { CustomDataSource } from './custom-data-source';
import { XyzService } from '../service/xyz-service';

export class XyzDataSource extends CustomDataSource<Xyz>
{
    constructor( private xyzService: XyzService ) {
        super( 'Xyz' );
    }

    loadXyz( offset: number = 0, limit: number = 10, predicates?: any ) : void
    {
        this.preLoad();

        this.xyzService
            .searchXyz( offset, limit, predicates )
            .subscribe( this );
    }    
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is it possible in RxJS to configure a ReplaySubject (or equivalent) that replays the latest messages with a distinctive identifier?

Is it possible to create a generic code that behaves like a ReplaySubject but emits each most recent message with a unique name upon subscribing? In the script below, the current output is... I want this to be logged once ... received the latest bbb 5 I c ...

What is the best way to access automatically generated JavaScript variables in TypeScript?

I am currently facing an issue with integrating a Google login API in my React project and I need some help. The problem arises when the user already has an active session, rendering the API unnecessary. The Javascript solution provided is as follows: co ...

Displaying varying values of an object using TypeScript's console log trick

While using Angular version 8 with RJXS library, I encountered a peculiar issue when attempting to log an object variable: Object { carName: "Sorbonne", age: "20", type: "C1" } ​ carName: "BB" ​ age: "23&quo ...

The image is malfunctioning in the production environment, but functions perfectly on the localhost server

Currently, I am in the process of creating a website utilizing Next.js and Mantine. In order to incorporate my logo into the Header section, I utilized the Image component from next/image. Unfortunately, upon deployment, the image does not display as inten ...

Implementing atob in Angular's interface

Looking for a solution to a token storage issue, my initial thought is that interfaces might be the way to go. Presently, my login code looks like this: import { Component } from '@angular/core'; import { FormBuilder } from '@angular/forms&a ...

Issue with validating the date picker when updating user information

Trying to manage user accounts through a dialog form for adding and updating operations, where the type of operation is determined by a variable injected from the main component. Encountered an issue while launching the update process - the date picker tri ...

Learn the process of transferring a complete JSON object into another object using Angular

I'm having trouble inserting one object into another. My JSON object is multidimensional, like this: { "item1":{ "key":"Value", "key":"Value" }, "item2":{ "key ...

Learn how to access nested arrays within an array in React using TypeScript without having to manually specify the type of ID

interface UserInformation { id:number; question: string; updated_at: string; deleted_at: string; old_question_id: string; horizontal: number; type_id: number; solving_explanation:string; ...

Enhance your Angular experience by adding two new columns to your JSONArray table

I am faced with a JSON-Array dilemma. Array(8) 0: (3) ["Test", "1", "222"] 1: (3) ["Test", "2", "333"] 2: (3) ["Test", "3", "444"] 3: (3) ["Test", "4", "555"] 4: (3) ["Test", "5", "666"] 5: (3) ["Test", "6", "777"] 6: (3) ["Test", "7", "888"] I want to t ...

Issue with Loosing Focus in React TextInput when typing the letter "S"

My TextInput is acting strangely - it loses focus only when I type the letter s. All other letters work fine. <FormControl key={"1"} sx={{ m: 1 }} variant="outlined" onChange={handleFilterValueChange}> <InputLabel htmlFor=& ...

Troubleshooting: Issue with Button Layout in Ionic's ItemSliding Component

How can I create a list item that allows swiping to reveal an archive button? The requirement is for the icon to appear to the left of the text. I'm referring to the documentation on Button Layout: https://ionicframework.com/docs/api/components/item ...

How can you annotate and inherit a class method that returns an array of itself?

In the following example, I present a simplistic representation of code that may not align with standard HTML or front-end conventions. Please excuse any confusion this may cause. TL, DR I am facing challenges in specifying a return type for a method tha ...

What is the proper place for DOM manipulation within Angular 2?

When it comes to Angular 2, the rules around DOM manipulation have evolved from Angular 1. In Angular 1, all DOM manipulation was supposed to be handled in directives for proper testability. However, with Angular 2, there seems to be a lack of clear inform ...

Troubleshooting issues with unit testing a component that utilizes a mocked service

I recently delved into testing Angular components and services. After watching a course on Pluralsight, I tried implementing some ideas from the tutorial available at . Unfortunately, I encountered an issue with testing the component method. Despite my eff ...

Navigating a SwipeableDrawer in React with scrolling functionality

I'm currently using a swipeable drawer in React from MUI to display lengthy content. My goal is to keep the title visible even when the drawer is closed, and I was able to achieve this through the following method: MUI SwipeableDrawer I've provi ...

Discover the contents of an Object's key in TypeScript

I currently have a variable of type object. let ref_schema_data: object The value of ref_schema_data: { '$schema': 'http://json-schema.org/draft-07/schema', '$id': 'tc_io_schema_top.json', allOf: [ { type: &a ...

Adjust the Angular menu-bar directly from the content-script of a Chrome Extension

The project I've been working on involves creating an extension specifically for Google Chrome to enhance my school's online learning platform. This website, which is not managed by the school itself, utilizes Angular for its front-end design. W ...

Place a hook following the storage of a variable in the device's memory

Within a component, I am facing the following situation: const [home, setHome]=useState(false) if(home){ return(<Redirect push={true} to="/" />); } setItem("isRegistered", resquest[0].user) setHome(true) The issue here is that ...

Trouble with two dropdown selections in Angular and preset values

I am encountering an issue with a select input in angular 7. The scenario involves having 2 selects where selecting an option in the second select populates the corresponding values in the first select. The first select should display a default placeholder ...

Retrieve the structure from a React application

When it comes to documenting architecture, the process can be incredibly beneficial but also quite time-consuming and prone to becoming outdated quickly. I have come across tools like Doxygen that are able to extract architectural details such as dependen ...