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)