PnP React Controls Part 5 – PnP Paging Control

Introduction

Hi friends, this is part 5 where I will explain the usage of the PnP Paging control. We will see how we can leverage this control on our SPFx projects and make use of the feature provided by this control. Thanks to the community.

About the control

This control allows us to render the contents with paging. This control can be used with other controls like Listview to render the contents in paging. There are few properties available with this control to display the paging based on our requirement.

Focus on the Code

You can refer Part 1 post on the creation of the SPFx project and installing all the dependencies. In addition to the dependencies mentioned in that post you have to install @pnp/sp – v3.5.1. You should also do some changes to the project files to make the pnpjs version 3 to work on SPFx 1.14.0. Please follow this link to do the changes.

Note: I have used the ListView webpart and added the PnP Paging control to the same web part. Please refer to the PnP React Controls Part 4.1 – Extending ListView Control to start and it has multiple linked post for extending the Listview control.

Created a file named ExtendListViewPnPPaging.tsx which is a copy of ExtendListViewDemo.tsx

import * as React from 'react';
import { useState, useEffect } from 'react';
import styles from './ListViewDemo.module.scss';
import { IListViewDemoProps } from './IListViewDemoProps';
import { escape } from '@microsoft/sp-lodash-subset';
import { ListView, IViewField, SelectionMode, GroupOrder, IGrouping } from "@pnp/spfx-controls-react/lib/ListView";
import { useBoolean } from '@uifabric/react-hooks';
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/items";
import { Persona, PersonaSize } from 'office-ui-fabric-react/lib/Persona';
import { IColumn } from 'office-ui-fabric-react/lib/DetailsList';
import * as moment from 'moment';
import ListViewContextMenu from './ListViewContextMenu';
import { Pagination } from "@pnp/spfx-controls-react/lib/Pagination";

const slice: any = require('lodash/slice');

const ExtendListViewPnPPaging: React.FC<IListViewDemoProps> = (props) => {
    const {
        description,
        isDarkTheme,
        environmentMessage,
        hasTeamsContext,
        userDisplayName
    } = props;
    const [loading, { setTrue: displayLoading, setFalse: hideLoading }] = useBoolean(true);
    const [listItems, setListItems] = useState<any[]>(undefined);

    const [pagedItems, setPagedItems] = useState<any[]>([]);
    const [pageSize, setPageSize] = useState<number>(10);
    const [currentPage, setCurrentPage] = useState<number>(1);

    const viewFields: IViewField[] = [
        {
            name: 'CaseID',
            displayName: 'Case ID',
            minWidth: 100,
            maxWidth: 150,
            sorting: true
        },
        {
            name: '',
            displayName: '',
            sorting: false,
            maxWidth: 40,
            render: (rowItem: any) => {
                return (
                    <ListViewContextMenu item={rowItem} />
                );
            }
        },
        {
            name: 'CaseStatus',
            displayName: 'Case Status',
            minWidth: 100,
            maxWidth: 150
        },
        {
            name: 'BizSegment',
            displayName: 'Business Segment',
            minWidth: 100,
            maxWidth: 150
        },
        {
            name: 'Author.Title',
            displayName: 'Created By',
            minWidth: 200,
            maxWidth: 250,
            sorting: true,
            render: (item?: any, index?: number, column?: IColumn) => {
                return (
                    <Persona size={PersonaSize.size24} showInitialsUntilImageLoads imageShouldStartVisible
                        text={item['Author.Title']} imageUrl={`/_layouts/15/userphoto.aspx?username=${item['Author.EMail']}&size=M`} />
                );
            }
        },
        {
            name: 'Created',
            displayName: 'Created',
            minWidth: 200,
            maxWidth: 250,
            sorting: true,
            render: (item?: any, index?: number, column?: IColumn) => {
                return (
                    <div>{moment(item.Created).format("DD-MMM-YYYY")}</div>
                );
            }
        }
    ];
    const groupFields: IGrouping[] = [
        {
            name: 'Author.Title',
            order: GroupOrder.ascending
        }
    ];

    const _getListItems = async (): Promise<void> => {
        const items: any[] = await props.sp.web.lists.getByTitle('Case Master').items
            .select('CaseID', 'CaseStatus', 'BizSegment', 'Author/Title', 'Author/EMail', 'Created')
            .expand('Author')
            ();
        setListItems(items);
        hideLoading();
    };

    const _getSelectedItem = (items: any[]) => {
        console.log('Selected Item(s): ', items);
    };

    const _onPageUpdate = async (pageno?: number) => {
        var currentPge = (pageno) ? pageno : currentPage;
        var startItem = ((currentPge - 1) * pageSize);
        var endItem = currentPge * pageSize;
        let filItems = slice(listItems, startItem, endItem);
        setCurrentPage(currentPge);
        setPagedItems(filItems);
    };

    useEffect(() => {
        if (listItems) _onPageUpdate();
    }, [listItems]);

    useEffect(() => { _getListItems() }, []);

    return (
        <section className={`${styles.listViewDemo} ${hasTeamsContext ? styles.teams : ''}`}>
            <div className={styles.welcome}>
                <img alt="" src={isDarkTheme ? require('../assets/welcome-dark.png') : require('../assets/welcome-light.png')} className={styles.welcomeImage} />
                <h2>Well done, {escape(userDisplayName)}!</h2>
            </div>
            <div>
                {loading ? (
                    <div>Loading, Please wait...</div>
                ) : (
                    <>
                        <ListView
                            items={pagedItems}
                            viewFields={viewFields}
                            compact={true}
                            selectionMode={SelectionMode.multiple}
                            selection={_getSelectedItem}
                            showFilter={true}
                            filterPlaceHolder="Search..."
                            stickyHeader={true}
                            groupByFields={groupFields}
                        />
                        <div style={{ width: '100%', display: 'inline-block' }}>
                            <Pagination
                                currentPage={currentPage}
                                totalPages={Math.round(listItems.length/pageSize)}
                                onChange={(page) => _onPageUpdate(page)}
                                limiter={3} // Optional - default value 3
                            />
                        </div>
                    </>
                )}
            </div>
        </section>
    );
};

export default ExtendListViewPnPPaging;
  • Added relevant imports to the PnP paging control
  • Added the Pagination control and configured the below required properties
    • currentPage – The current page number displayed to the users.
    • totalPages – Total pages based on the items. Used the formula Math.round(<Total Items Count>/<Items displayed per page>).
    • onChange – Callback method to invoke when the the page numbers or arrows are clicked
    • limiter – Optional property to display the number of pages to be displayed minimally.

Following are the additional properties apart from the properties used above

  • hideFirstPageJump – To hide the button to navigate to the first page
  • hideLastPageJump – To hide the button to naviagate to the last page
  • limiterIcon – Fluent UI icon to be displayed as a limiter button to show additional pages if any apart from the pages displayed currently.

Reference URL

ListView – @pnp/spfx-controls-react

Sample Implementation

Conclussion

I hope you had learned something about one of the pnp control. There are lot to come in future.

Please free to post your comments and let me know if you want me to post article on any particular feature or plugins that relates to SharePoint. Thanks for your support.

sudharsank/pnpcontrols-demo: Demo project on different PnP React Controls. (github.com)

Happy Coding…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s