Pagination

The last topic that we’ll cover in this tutorial is pagination. You’ll implement a simple pagination approach so that users can view the links in smaller chunks rather than having an incredibly long list of Link elements.

Preparing the Angular Components

Once more, you first need to prepare the Angular components for this new functionality. In fact, we’ll slightly adjust the current routing setup. Here’s the idea: The LinkListComponent will be used for two different use cases (and routes). The first one is to display the 10 top voted links. Its second use case is to present new links in a list separated into multiple pages that the user can navigate through.

You added two new routes: /top and /new/:page. The second one reads the value for page from the URL so that this information is available inside the component that’s rendered, here that’s LinkListComponent.

The root route '' now redirects to the first page of the route where new posts are displayed.

You need to update the HeaderComponent to show the new /top route:

We need to add quite a bit of logic to the LinkListComponent to account for the two different responsibilities that it now has.

The query now accepts arguments that we’ll use to implement pagination and ordering. skip defines the offset where the query will start. If you passed a value of, e.g. 10 to this argument, it means that the first 10 items of the list will not be included in the response. first then defines the limit, or how many elements; you want to load from that list. Say, you’re passing the 10 for skip and 5 for first, you’ll receive items 10 to 15 from the list.

You need to update the references to this query in the CreateLink component.

Let’s retake a close look to understand what’s going on:

  1. You define the pageParams$ observable based on the this.route.paramMap where we retrieve all the params and map it to get only the page param, then we parse in number
  2. We perform kinds of the same thing for the path$ observable that we create from this.route.url, a SegmentUrl converted in string
  3. Now, we begin to create the first stream, that will be used as variables in the watchQuery function, first$. first$ used to calculate the chunk of links that you retrieve.
  4. Then, we declare skip$, the second variable that will enable us to perform the chunk of links that you retrieve
  5. Afterwards, we provide orderBy$ that will include the ordering attribute createdAt_DESC for the new page to make sure the newest links are displayed first. The ordering for the /top route will be calculated manually based on the number of votes for each link.
  6. We create the getQuery function that will receive the variables (the values of first$, skip$ and orderBy$ ) in parameter, set it in the options and returns the Observable of valueChanges. Note, that we also perform the subscribeToMore.
  7. Now, we combine all observables of first$, skip$ and orderBy$, get their values and create an object having the property first, skip, orderBy
  8. Then we retrieve the object created by the combination of first$, skip$ and orderBy$ to provide it to the getQuery function. Due the fact that getQuery returns an Observable<ApolloQueryResult<AllLinkQueryResponse>>, we will get an Observable<Observable<ApolloQueryResult<AllLinkQueryResponse>>> if we use the .map operator. Therefore, we use switchMap to “flatten” the Observable<Observable<ApolloQueryResult<AllLinkQueryResponse>>> to an Observable<ApolloQueryResult<AllLinkQueryResponse>>
  9. In the end, We execute the query and save the allLinks and the count

You also need to define the LINKS_PER_PAGE constant and then import it into the LinkListComponent as well as the LinkItemComponent.

You also need to map linksPerPage to LINKS_PER_PAGE in src/app/link-list/link-list.component.ts.

Implementing Navigation

Next, you need functionality for the user to switch between the pages. First, add two button elements to the bottom of the LinkListComponent that can be used to navigate back and forth.

Since you added pageNumber as one of the Input on hn-link-item, you now need to add it to the LinkItemComponent.

Since the setup is slightly more complicated now, you are going to calculate the list of links to be rendered in a separate method.

For the isNewPage, you’ll simply return all the links returned by the query. That’s logical since here you don’t have to make any manual modifications to the list that is to be rendered. If the user loaded the component from the /top route, you’ll sort the list according to the number of votes and return the top 10 links. This is accomplished through an orderedLinks computed property which you will implement next.

You will make use of the lodash library within the orderedLinks function.

You also need to add a count property to the LinkListComponent.

Next, you’ll implement the functionality for the Previous- and Next-buttons.

The implementation of these is straightforward. You’re retrieving the current page from the URL and implementing a sanity check to make sure that it makes sense to paginate back or forth. Then you merely calculate the next page and tell the router where to navigate next. The router will then reload the component with a new page in the URL that will be used to calculate the right chunk of links to load. Hop on over to the running app and use the new buttons to paginate through your list of links!

Final Adjustments

Through the changes that we made to the ALL_LINKS_QUERY, you’ll notice that the update functions of your mutations don’t work anymore. That’s because readQuery now also expects to get passed the same variables that we defined before.

Note: readQuery essentially works in the same way as the query method on the ApolloClient that you used to implement the search. However, instead of making a call to the server, it will simply resolve the query against the local store! If a query was fetched from the server with variables, readQuery also needs to know the variables to make sure it can deliver the right information from the cache.

All that’s happening here is the computation of the variables depending on whether the user currently is on the /top or /new route.

Finally, you also need to adjust the implementation of update when new links are created.

You have now added a simple pagination system to the app, allowing users to load links in small chunks instead of loading them all up front.

Unlock the next chapter
How do you access information about the current page URL?
A GraphQL query
Import & use the router-info package
By injecting the ActivatedRoute service and using the params method
You can not gain access to this information