DEV Community

Joaquin Cid
Joaquin Cid

Posted on

NGXS loading spinners and actions executing

In our web-apps, we deal with async communication to our servers every day, and in order to give our users a better UX we usually display spinners or disable certain pieces of UI while an operation is being executed.

Although this is a very common feature to build, it is never as trivial as one may think so.

If you are using NGXS as your state management library, now you can use a new plugin to know when an action is being executed and control UI elements accordingly.

Async actions

One of the nice things about NGXS is that supports async actions (observables or promises) out of the box, and you can easily implement them like this:

@Action(MyAction)
myAction(ctx: StateContext<StateModel>){
  // ... asyncMethod
  return this.service.get();
}

Now, if we want to display a loading spinner, the preferred way to implement this is to use a loading property in the state, and set it true when the action is executed, and set it false once the async method returns the response.

@Action(MyAction)
myAction(ctx: StateContext<StateModel>){
  // ... asyncMethod
  ctx.patchState({loading: true});
  return this.service.get().pipe(
    tap(response => {
      //...
      ctx.patchState({loading: false});
    })
  );
}

New @ngxs-labs/actions-executing plugin

actions-executing is a new plugin that provides the state of an action being executed. It works by leveraging the actions lifecycle from the Actions stream class, and provides a very easy way to know when an action is being executed.

So, instead of manually setting loading to true or false, we just select the action we can to listen to via @Select

@Select(actionsExecuting([MyAction])) myActionIsExecuting$;

and we can use it on our templates like this:

<span *ngIf="myActionIsExecuting$ | async">
  Loading...
</span>

How to use it

First we need to install the package:

npm install --save @ngxs-labs/actions-executing

Then, we import it in our module:

//...
import { NgxsModule } from '@ngxs/store';
import { NgxsActionsExecutingModule } from '@ngxs-labs/actions-executing';

@NgModule({
    //...
    imports: [
        //...
        NgxsModule.forRoot([
          //... your states
        ]),
        NgxsActionsExecutingModule.forRoot(),
    ],
    //...
})
export class AppModule { }

Finally, as shown before, we just call @Select on the actions we want to listen, and bind it to our template, i.e.:

//...
import { actionsExecuting, ActionsExecuting } from '@ngxs-labs/actions-executing';

//...
export class SingleComponent {

  @Select(actionsExecuting([MyAction])) myActionIsExecuting$: Observable<ActionsExecuting>;

}
<button [disabled]="myActionIsExecuting$ | async"
        (click)="doSomething()">
  My Action
</button>

<span *ngIf="myActionIsExecuting$ | async">
  Loading...
</span>

If you want to see @ngxs-labs/actions-executing you can check out a demo here


Hope you enjoyed this post, and if you did, please show some ❤️. I often write about Angular, Firebase and Google Cloud platform services. If you’re interested on more content, you can follow me on dev.to, medium and twitter.

Top comments (0)