DEV Community

Cover image for Implementing a page with Angular Pipe Async + RxJs Observable + Skeleton Loading
Renan Ferro
Renan Ferro

Posted on

Implementing a page with Angular Pipe Async + RxJs Observable + Skeleton Loading

Have you ever implemented a page with RxJs and the content is displayed in a non-smooth way to the user!? Or when you're browsing some website and the content loads and the is jumping!?

Yes!? Perfect, because today I want to show you how we can to implement a page with the powerful Angular Pipe Async, RxJs and the beautiful power of Skeleton Loading!

So, let's start it!


Creating our Product interface

First, we gonna create a simple Product interface to type our Observable and our API response,
by typing our elements we get a lot of good things, like the powerful auto-complete.

The command is: ng generate interface models/product

export interface Product {
  name: string;
}
Enter fullscreen mode Exit fullscreen mode

Creating our service to get our Product list

Now, let's create our Angular Service to implement the get method to communicate with our API and get our API Products Data. (I'm using a free mock Rest API, it is: mockapi.io)

The command is: ng generate service service/product

constructor(
      private _httpClient: HttpClient,
  ) { }

  getProductsList(): Observable<Product[]> {

    return this._httpClient.get<Product[]>(
        `${ environment.urlApi }/products`,
    );
  }
Enter fullscreen mode Exit fullscreen mode

Now we have our service with our getProductsList() method! This method are communicating with our API and returning for us a product array Observable.


Implementing the communication with our API getProucts() method!

First we need to create a Product Observable. So, in your component.ts file we create it, like below:

export class MarketListComponent implements OnInit {

  productsList$: Observable<Product[]>; 

  constructor() { }

  ngOnInit(): void {
  }
}
Enter fullscreen mode Exit fullscreen mode

The $ is a convention to identify an Observable variable, so if you to see a variable and the last character is a $ probably that's an Observable.

I like to isolate my typescript code in some methods, I mean, I don't like to write my code directly in my ngOnInit() Lifecycle hooks. So, I'll create a simple method to isolate and call it in ngOnInit()! But you can write the code directly inside of ngOnInit().

export class MarketListComponent implements OnInit {

  productsList$: Observable<Product[]>; 

  constructor(
      private _moviesService: ProductsService
  ) { }

  ngOnInit(): void {
    this.getProducts();
  }

  getProducts(): void {

    this.productsList$ = this._moviesService.getProucts();
  }
}
Enter fullscreen mode Exit fullscreen mode

What have we done!? In the constructor we declare our Products Service. And now our observable productsList$ is receiving the getMovies() and getting the data from API. So, now we need to show it in our html!


Implementing the template with Angular Pipe Async

When we have an Observable, we need to do the subscribe and we need to do the unsubscribe of our observable too! So, the powerful async pipe does it for us! Perfect, right!?

In few words, the async pipe subscribes to an Observable and when the component is destroyed, it automatically unsubscribes!

Now let's implement the async pipe, in our html file we are do like below:

<main>
  <ng-container *ngIf="(productsList$ | async) as productsList; else statusAPITemplate">
    <ng-container *ngFor="let product of productsList">
      <div class="card-product">
        <h1>
         {{ product.name }}
        </h1> 
      </div>
    </ng-container>
  </ng-container>
  <ng-template #statusAPITemplate>
    <span>
     Loading data...
    </span>
  </ng-template>
</main>
Enter fullscreen mode Exit fullscreen mode

Understanding the structure!

First, we have a ng-container with our productList$ and your async pipe and we declare the productList as our variable, that's all we need to do! Because the Angular
async pipe takes care of to do subscribe and do the unsubscribe. And when the last value isn't emitted, the angular show us your <ng-template #statusAPITemplate> with our Loading data span!
The second <ng-container *ngFor="let product of productsList"> is because we have a list of products, so we need to iterate it!

And that's the result!

We have our loading template and after a seconds we have our product list content, but if you repair, the content is abruptly displayed to the user, causing a bad experience and if we have it on mobile our screen would probably jump, which is even worse.

And Voilà, we can resolve this and providing a better user experience and adding value to our page and our code, let's do it with the mighty skeleton loading!


What is skeleton loading!?

In a nutshell, skeleton loading it's an interface representation of your component is getting the data, so you simulate the space with the animated element and avoid screen-jumping, lighthouse content shift problems and get a good user experience. So, let's start!

In my Angular projects I like to use the NGX Skeleton loader library, it works very well for me!

So, execute the command npm i ngx-skeleton-loader and import it in our component module.

Now, we need to change our Loading data span to the our new component, the ngx-skeleton-loader. So our code will look like below:

<main>
  <ng-container *ngIf="(productsList$ | async) as productsList; else statusAPITemplate">
    <ng-container *ngFor="let product of productsList">
      <div class="card-product">
        <h1>
         {{ product.name }}
        </h1> 
      </div>
    </ng-container>
  </ng-container>
  <ng-template #statusAPITemplate>
    <!--<span>
     Loading data...
    </span>-->
      <ngx-skeleton-loader [theme]="{width: '100%', height: '95px', marginBottom: '14px', borderRadius: '1.5rem'}"
                           [count]="7">
      </ngx-skeleton-loader>
  </ng-template>
</main>
Enter fullscreen mode Exit fullscreen mode

Understanding the structure!

In the theme input we can pass CSS properties to customize our ngx-skeleton-loader. Then we passed some properties to look like our card-product. And in the count input we pass the amount of ngx-skeleton-loader elements we want to have.

And now, finally, we have a better user experience, we prevent a some problems with lighthouse and improve our code and our page! Take a look!

I really like using skeleton loading in my projects, it's powerful!

I hope you enjoyed the article and if it helped you, take a comment! and if you have any questions, leave a comment too, it will be a pleasure to talk!

See you soon guys!

Top comments (0)