Showing posts with label rxjs. Show all posts
Showing posts with label rxjs. 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!

Thursday, 12 November 2020

Angular timeout and error handling with RxJS

@angular/cli: 8.3.23
Source: GitHub

This is a short article which demonstrates how we can leverage RxJS to effectively set a time-out on long running server requests, and then display a meaningful error message when that happens.

For demonstration purpose, I have made use of Angular in-memory-web-api for REST services (Refer to in-memory-web-api).

I have set up text-box and a button, and I would enter a specific item-id to retrieve the corresponding item. For the in-memory-web-api, I have set a delay of 500ms.



If I now add a timeout of 250ms (timeout imported from rxjs/operators), the next button click to call the web-api would time out. Note that, since the button click is tied to an action stream, any error thrown would effectively complete the observable.



For handling this error, my error-handler method checks for the error instance, and it comes as an instance of TimeoutError (also part of rxjs).





Please note that effective handling really depends on the structure of your REST API.

Now comment out the timeout and try fetching with a blank id. The request is again errored out, and effectively handled in my error-handler method.


Try changing the URL from 'api/items' to 'api/item'. The request errors out with a different error, but handled through the same logic.


Cheers!

Saturday, 5 September 2020

Autocomplete with RxJS and Angular Material

@angular/cli - 15.2.8
@angular/material - 15.2.9

Source: GitHub

This article talks about an efficient way to implement auto-complete feature using Angular concepts. The key components used in this demonstration are as follows:

1. Angular Material (v15) Autocomplete module.
2. RxJS for action and data binding.

I have used Dummy-JSON for getting fake JSON data.






Let us now see how we go about it.


The UI:

The front-end consists of an angular autocomplete module. The input event taps the user's keystrokes and emits a new value into an action subject.


Each key-stroke emits a new value into an action subject declared in my ProductService class.




The Service:

I selected a Subject, since I want it emit a value only when the user starts to type. The action$ is an observable built out of this subject.



Every time a new value is emitted into the action stream, it returns an observable from the http GET request. Since this is a higher-order observable (i.e., an observable returning an observable), we make use of one of the higher order functions like switchMap.


So why switchMap (and not other higher order functions like concatMap or mergeMap)? The reason is: switchMap simply unsubscribes from the previous observable once a new value is emitted by the parent observable.

What this means is - as soon as the user types in another letter or removes a letter, there is simply no need to subscribe and execute the rest call for the previous values. The user is only interested to see the results of the search according to his/her current input. And switchMap does this automatically, and thus gets rid of unwanted rest calls.

We can also get additional control on when to fire the rest service depending on the length of the user's input. For example, in this demo, I fire the rest call only when the user has entered at least 2 characters. If not, then I return an observable of empty array (using of([])).

Use distinctUntilChanged operator to exclude user inputs which have not changed during the debounce period. So the http call will only fire for new input values, and not if the user quickly changed the input and reverted to the old value.


Back to the Component:

We tap onto the above observable and use async pipe to subscribe to it. Since we are completely using observables, we can also use the OnPush change-detection-strategy.


And that is all. You can download the source code from GitHub and try it on.

Cheers!

Wednesday, 11 September 2019

Angular - custom lookup form component

@angular/cli - 12.0.0

Most of our application requirements are fulfilled using standard HTML elements. However, we may sometimes need to stretch ourselves when we are working for large enterprise applications. In such cases, reusability becomes a strong factor which determines ease of development. Today's use case is one such custom requirement.

This article speaks about creating an Angular component which acts as a lookup - an input field with a search button. The button opens up a modal window, which shows a list of values from which the user may select one. Additionally, this new component should also angular-form-ready, which means, it should be capable enough to be registered as a template-driven or reactive form element.

Let's first talk about the component itself.

The GUI

app.component.html


The app-lookup component takes in a couple of inputs - formConfig and lookupConfig. A sample implementation is shown below:

app.component.ts - sample config

When user clicks on the search button, a modal window opens with data fetched from server (in my case a simple promise). Use selects a record and clicks select. The data gets populated on the input form.

lookup modal

lookup form

Submitted form

If allowUnlistedValue is set as true, the user will be able to enter a value which does not exist in the list, and the value will be accepted. If not, then the form element will return empty value.

So this is all about the component.

Now let's see how to set it up!

We need to extend this component so that Angular recognizes this as a valid form element. Since this element is of input type, I have implemented the interface ControlValueAccessor. This interface needs to implement 3 methods - writeValue, registerOnChange and registerOnTouched.

The writeValue method is the one which is responsible to writing your value to the view from your model.
The registerOnChange is used to propagate changes from your model to the view. This is how the form knows that one of its members has changed its value.
The registerOnTouched is used to let the wrapper form element know that one of its elements has been marked as touched.

ControlValueAccessor interface implementation


Error Handling

Question: Now that we are done with setting up the value read/write operations, how about error handling? How do we let the wrapping angular form know if our custom component has an error and needs to be marked as invalid?

Answer: To do this, we inject an instance of NgControl into the component, which gives us the FormControl instance. It is this form-control object which sets the errors in the component conditionally. Remember, it is a custom component, so everything needs to be set by us :-)




Note: The ErrorStateMatcher has nothing to do with this implementation, but it is more for the material input component. ErrorStateMatcher marks the component in red when it has an error.

As a result, if allowUnlistedValue is set as false, the user will not be able to enter a value which does not exist in the list, and the component (and subsequently the form) will be marked as invalid. This is demonstrated by disabling the Submit button when form is invalid!

Error handling


Do note: A much more in-depth set of instructions for creating custom form elements can be found in a blog by Pascal Precht.

And there you go. You have your own angular component ready to be used in a form! You can download the source code from GitHub and play around with the configurations.

Cheers!
Follow me on Twitter or connect with me on LinkedIn.