Handle server-side sorting in an Angular application

featured_image

When our frontend gets a paginated result from an API, we have to handle not only paging, but also sorting of the outcome. In this post, I show how to create a custom sorting component that works well with a standard Page object returned by a Spring Boot API.

What we are going to build

The goal is to build a custom sorting component that accepts a paginated response from a Spring Boot API and displays it in a data table. We will be able to control the sorting direction and a column that will be used to arrange records.

Our example app will be displaying a list of projects fetched from the "/api/projects" endpoint exposed by the backend API.

The finished project is available in the GitHub repository – little-pinecone/scrum_ally.

You can see the datatable from that project, with sorting, on the following screenshot:

sorting-asc screenshot

Architecture overview

You can see the final directory tree for the sorting part of the frontend app on the image below:

sorting-project-structure screenshot

Requirements

Handle sorting

Consume a page received from a Spring Boot app

Make sure, that the frontend is able to consume a page received from an API. We are doing it by mirroring the Page instance (code from the Handle server-side pagination in an Angular application post):

Adjust the default pageSize to your needs.

Create a sortable-column class

We need a class that will carry the internal name, displayed title and sorting direction of a column:

Let’s add a function for toggling the direction of the sorting, it will be called every time a user clicks a column title:

The first click results in setting the ascending order, the second click gives the descending order and the third click clears sorting (the arrow won’t be shown and the records will be sorted by id, ascending, by default).

Create a sortable-header component

Generate the component with the following command:

Copy the following code to make the component accept an array filled with sortable-column objects and emit events when users trigger sorting:

An html template displays columns titles in <th> elements. On the right of every title there is a place for a sort icon – it reflects the chosen sorting direction. The icon is displayed only for a column for which the sorting was activated:

A user triggers sort() function by clicking the column title.

Add a service to handle base actions

Use the following command to generate a service:

In this service we are going to add two public methods. One for getting the column that has a non-null sort direction set – that’s the column chosen by a user:

The other, for setting direction in all other columns to null – it will remove a sorting icon displayed in a datatable from a previously chosen column:

The service and our component are ready to be used in all components that serves datatables.

Include our sorting feature in a datatable

In the template with the datatable (e.g. projects-table.component.html), replace the old <th> elements (e.g <th scope="col">Project name</th> ) with the sortable-header component:

Now, we need to provide the actual sortableColumns array. Declare it in the component that serves the view (e.g. projects-table.component.ts):

If you provide column names, titles (displayed in the view) and sort order (for the one you choose as a default sorting column), the records will be sorted when your app loads the view.

When adding more columns, remember to leave the third parameter, direction, set to null. Only one column should have the direction set to 'asc' or 'desc' value. The direction in the rest of them has to be set to null.

Let’s provide the implementation of the sort() method, called when a user clicks a column title:

When the sort() method is called, the column for which it was triggered is filtered out from the list of all columns so we can set the direction field in the rest of them to null.

Then, we call the getData() function, so let’s take a look at the implementation:

The getData() function is called every time the page is loaded, refreshed, the pagination has been changed, so it has to be able to retrieve the sorting column.  It does so by using the sortingSercive we created previously. Next, it can pass the chosen column to the projectDataService. Now, we can move on to the next step – adjusting the service so it can pass sorting parameters to the backend.

Update a service that handles requests

The getPage() method accepts two arguments: pageable and sortableColumn. If the sortableColumn is null, the response will be sorted by the id of the records by default. However, if it is set, the column name and the sort direction will be passed to the backend, as you can see below:

Thanks to that, you are able to pass the sort parameters alongside the paging parameters to the API and receive a nice, paginated and already sorted response.

Test the sorting feature

Add those tests to our custom-sorting.service.spec.ts file :

You also need to updated tests for all components that uses the SortableHeaderComponents (eg. projects-table.component.spec.ts) :

 

Photo by Ivan Cujic on StockSnap

3 thoughts on “Handle server-side sorting in an Angular application

  1. Server side, the WebApi controller s Get method. Further, there must be still some issues in my code: when I change the page, the server gets the correct params and returns the next N items; if I set a breakpoint in the JS success handler I can browse these new items; but at the end the view is not refreshed, and I keep seeing the first page instead of the second. Naftis Jul 22 ’13 at 18:16

    1. Hi,

      If I read the description correctly, the issue occurs when you use the pagination component – clicking the next page retrieves a new page from the backend, but the frontend does not refresh the table content. There isn’t much info here to help you with debugging, but the following few things come to my mind:

      1. Errors in the developer console

      Do you get any errors when you click the Next page icon and have the developer console open? If any errors are displayed in the console, you can use them to debug/google the problem more effectively.

      2. Are all tests passing?

      Make sure that all frontend tests are passing ($ ng test).

      If you work on your personal project, check out the example tests from the repo:

      for a service retrieving data from the API

      for a custom pagination service

      and try to implement them. It may help.

      3. Is the new page retrieved from the API really passed to the component with datatable?

      To clarify, in the component that serves the list of requested items, you have a function for requesting a new page:

      // src/app/projects/components/data-table/projects-table/projects-table.component.ts

        public getNextPage(): void {

          this.page.pageable = this.paginationService.getNextPage(this.page);

          this.getData();

        }

      and the getData implementation subscribes the result when it is retrieved from the API (.subscribe(page => this.page = page)):

      // src/app/projects/components/data-table/projects-table/projects-table.component.ts

         private getData(): void {

          let column = this.sortingService.getSortableColumn(this.sortableColumns);

          this.projectDataService.getPage(this.page.pageable, column)

          .subscribe(page => this.page = page);

        }

      and in the template of this component, you pass this page to the table:

      <!–src/app/projects/components/data-table/projects-table/projects-table.component.html–>

      <tbody *ngIf=”page”>

      <tr *ngFor=”let project of page.content”>

      and in the same template, you pass this page to the custom pagination component ([page]=”page”):

      <!–src/app/projects/components/data-table/projects-table/projects-table.component.html–>

        <app-custom-pagination

          [page]=”page”

          (nextPageEvent)=”getNextPage()”

          (previousPageEvent)=”getPreviousPage()”

          (pageSizeEvent)=”getPageInNewSize($event)”>

        </app-custom-pagination>

       

      I hope you will solve the issue soon. In case you are working on a direct clone from the scrum_ally repo (master branch) , without custom changes, please create an issue in the repo, providing enough info to replicate this problem (e.g. a failing test).

      Regards, little_pinecone

  2. Hi,

    I’ve been visiting your website a few times and decided to give you some positive feedback because I find it very useful. Well done.

    I was wondering if you as someone with experience of creating a useful website could help me out with my new site by giving some feedback about what I could improve?

    You can find my site by searching for “casino gorilla” in Google (it’s the gorilla themed online casino comparison).

    I would appreciate if you could check it out quickly and tell me what you think.

    casinogorilla.com

    Thank you for help and I wish you a great week!

Leave a Reply

Your email address will not be published. Required fields are marked *