Angular Async Render (Async Resolvers)
Are you sick of the navigation blocking Angular resolver. No problem... just use the async-render component for the different parts of you app that need to be rendered
and create directive resolvers that you can attach to the inidvidual async render components. You can also provide your cool loader to be visualized while loading
the data or you can use the exported async render component/directive (using asyncRender) with a template variable to be able to get access to the isLoading variable. You can also trigger a refresh on all of the resolvers by using the refresh$.next() subject method or trigger a individual resolve by querying the directive with ViewChild and calling the resolve() method. You can also configure the resolvers to resolve automatically stream emissions. For more info check out the app or look bellow. Happy coding!
Installation
yarn add hg-async-render || npm i hg-async-render
Usage
You can check the src folder of this repo for a more detailed explanation but the usage is as follows:
1. Configiration (import the AsyncRenderModule to your module)
import { AsyncRenderModule } from 'hg-async-render';
@NgModule({
declarations: [
],
imports: [
AsyncRenderModule,
],
providers: [
],
bootstrap: [AppComponent]
})
export class AppModule { }
2. Create your first action based resolver (alternatively you can create a regular resolver with a function call that returns an observable - view here)
import { Directive } from '@angular/core';
import { AsyncRenderResolver, HG_ASYNC_RENDER_RESOLVER } from 'hg-async-render';
@Directive({
selector: '[appUserListAsyncResolver]',
providers: [
{
provide: HG_ASYNC_RENDER_RESOLVER,
useExisting: UserListAsyncResolverDirective,
multi: true
}
]
})
export class UserListAsyncResolverDirective extends AsyncRenderResolver {
constructor(service: YourService) {
super({
loadAction: service.loadUsers,
cancelAction: service.cancelLoadUsers,
success$: service.userLoadSuccess$,
failure$: service.userLoadFailure$
});
}
}
3. Use the async render component and the resolver from step.2 (you can also use [hgAsyncRender] directive - look here)
<ng-template #loader let-isLoading>
<app-loader loaderSize="small" [visible]="isLoading" [localLoader]="true"></app-loader>
</ng-template>
<div>
<div>UserListIsLoading: {{userListAsyncRender.isLoading}}</div>
<button (click)="userListAsyncRender.refresh$.next()">Reload Users</button>
<hg-async-render [loaderTemplateRef]="loader" #userListAsyncRender="asyncRender" appUserListAsyncResolver>
<h1>User List</h1>
<ul>
<li *ngFor="let user of users$ | async"> {{ user.email }}</li>
</ul>
</hg-async-render>
</div>
Async Render Component Inputs:
@Input() loaderTemplateRef: TemplateRef<any>;
@Input() errorTemplateRef: TemplateRef<any>;
@Input() autoHideLoader = false;
@Input() autoShowError = false;
More Congifirations
1. Use the async render directive to skip the additional element added and use a custom loader
<div>UserPostDepIsLoading (Directive): {{asyncRender.isLoading}}</div>
<button (click)="asyncRender.refresh$.next()">Reload User Post</button>
<ng-template hgAsyncRender #asyncRender="asyncRender" appUserPostDepAsyncResolver>
<h1>Post</h1>
{{ post$ | async | json }}
</ng-template>
⚠️Since the directive is rendering the template there is no loaderTemplateRef binding. But it's not actually needed since you can just put the loader itself info the template and use the template varialbe to access the state
2. You can also additionally configure the resolvers
import { Directive } from '@angular/core';
import { AsyncRenderResolver, HG_ASYNC_RENDER_RESOLVER, ResolverConfig } from 'hg-async-render';
@Directive({
selector: '[appUserListAsyncResolver]',
providers: [
{
provide: HG_ASYNC_RENDER_RESOLVER,
useExisting: UserListAsyncResolverDirective,
multi: true
}
]
})
export class UserListAsyncResolverDirective extends AsyncRenderResolver {
@Input('appUserPostDepAsyncResolver') shouldSkip: boolean;
config: ResolverConfig = ResolverConfig.AutoResolve;
constructor(service: YourService) {
super(
{
loadAction: ([dependecyStreamData1, dependecyStreamData2, dependecyStreamData3]) => service.loadSomething(dependecyStreamData1, dependecyStreamData2, dependecyStreamData3),
cancelAction: service.cancelLoadUsers,
success$: service.userLoadSuccess$,
failure$: service.userLoadFailure$
},
[dependecyStream1$, dependecyStream2$, dependecyStream3$]
);
}
}
3. Regular Resolver
import { Directive, Input } from '@angular/core';
import { AsyncRenderResolver, HG_ASYNC_RENDER_RESOLVER } from 'hg-async-render';
import { HttpClient } from '@angular/common/http';
@Directive({
selector: '[appSimpleUserListAsyncResolver]',
providers: [
{
provide: HG_ASYNC_RENDER_RESOLVER,
useExisting: SimpleUserListAsyncResolverDirective,
multi: true
}
],
exportAs: 'appSimpleUserListAsyncResolver'
})
export class SimpleUserListAsyncResolverDirective extends AsyncRenderResolver {
@Input('appSimpleUserListAsyncResolver') shouldSkip;
constructor(http: HttpClient) {
super(() => http.get('https://jsonplaceholder.typicode.com/users'));
}
}
The result can be accessed via the data$ property on the resolver directive. You can either use ViewChild to get the directive and access the data$ property or by using a template variable like:
<ng-template hgAsyncRender #simpleUserListAsyncRenderDirective="asyncRender"
[appSimpleUserListAsyncResolver]="false" #resolver="appSimpleUserListAsyncResolver">
<h1>User List</h1>
<ul>
<li *ngFor="let user of (resolver.data$ | async)"> {{ user.email }}</li>
</ul>
</ng-template>
4. Error Handling
Each resolver has an error property that will contain the error if one exists. You can use this property inside your templates via template reference variable containing the resolver instance (just like the one with name resolver from the code above section 4) to present the error message/code to the user.
Check our website;