Showing posts with label scroll. Show all posts
Showing posts with label scroll. Show all posts

Sunday, 27 June 2021

Angular scroll-to-top implementation

@angular/cli: 15.2.8
Source: GitHub

In this article I would like to talk about an often-used feature on a user-centric application - the scroll-to-top button for allowing a user to quickly scroll to the top of the page, without having to use the mouse. The end result would be something like this:




Let's break up this implementation into 3 steps:

1) Set up the scroll-to-top button (with some nice styling).
2) Determine when the button should be displayed.
3) Scroll to the top of the page on clicking the button.

Setting up the scroll-to-top button:
I have used @angular/material for some easy styling. In order to display the button fixed to the bottom-right corner of the page (and some nice hover effects with opacity), I have used the below properties on the :host -

scroll.component.scss

Determine when the button should be displayed:
This is an interesting section. To begin with, we need to set up an Observable on the scroll event of the DOCUMENT. You may directly access the document object, or you may use an InjectionToken<Document>, provided by angular.

This observable starts emitting values as soon as the user starts to scroll up or down. Now we are not really interested in the actual scroll event, but we would like to check something important every time the scroll event is fired - and that is the scroll position. Angular makes this quite simple with the ViewportScroller service from @angular/common package. This service has few useful methods, and I am going to use two of them - getScrollPosition and scrollToPosition.

list.component.ts

The getScrollPosition method returns an array of 2 numbers - the X and Y coordinate. I check if the Y coordinate is more than 0 (which means the user has scrolled down) and I enable to scroll-to-top button.

Scroll to the top of the page on clicking the button:
For this last part, I make use of the scrollToPosition method, which again takes in the X and Y coordinates, and I scroll to [0, 0].

list.component.ts


That is all. Hope this was helpful :)

UPDATE:

The source code has been updated to angular 15, with standalone components. The main branch contains the updated syntax of angular 15. Refer to the legacy branch to view the old syntax of angular 13.

Cheers!

Sunday, 25 April 2021

Angular infinite scrolling using RxJs and NgRx

@angular/cli: 12.1.0
Source: GitHub

I have seen multiple blog posts on this topic which make use of one method or the other. I just thought of putting everything together under one hood - RxJS, NgRx, Angular Material and in the end, pure document property access. This is what is leads to:

preview


Let's quickly talk about the setup here - I am using an Angular CLI 12.1.0 project. I have NgRx state management set up, with a root state and a feature state. Of course, NgRx is in no way related to the purpose of this application, but I just thought it would not hurt to implement it. We have to admit, as your application grows in size, NgRx does give you some peace of mind :-)

The UI is built using angular material. I have used the mat-list and and the mat-spinner components. I have some hard-coded dummy data, which I have converted into an observable stream, with a delay of 1500 ms, which gives some feeling of having fetched data from a REST service.

The key logic is to calculate when the user has scrolled down to the edge of the page. I chose a simple formula derived from the document object. Of course there are other fancy approaches like using an IntersectionObserver, but that's something I felt was too much for me!

data.service.ts

The above formula works great on a desktop, as well as a mobile (you can trust me, I have tested it myself - but feel free to drop me a note if it doesn't).

I set up the scroll action using RxJS fromEvent.

data.service.ts

Once the scroll event is set up, whenever the above formula is satisfied, the next set of data is called for. For my sample use case, I have used a limit and skip value, which I have used to slice off a chunk from my dummy data set. Every time a new set is emitted by my data observable, I have made use of a reducer function to append it to my previous list of data.

feature.reducer.ts

This is where I store the next limit and skip values for the next data load, and also make a simple calculation of whether my data load is complete, and set it on my state. Once this flag becomes true, subsequent scroll actions do not trigger any more data fetch.

scroll.component.ts

Cheers!