Simple and customizable Angular block module for block/busy effects/loading indicators.
Install package:
npm install @efk3/angular-block
Set up in your NgModule:
import { NgModule } from '@angular/core';
import { BlockModule, BLOCK_DEFAULT_GLOBAL_COMPONENT, BLOCK_DEFAULT_SPECIFIED_COMPONENT } from '@efk3/angular-block';
@NgModule({
imports: [BlockModule.forRoot()],
declarations: [YourBlockerComponent, YourTargetSpecificBlockerComponent],
entryComponents: [YourBlockerComponent, YourTargetSpecificBlockerComponent],
providers: [
{ provide: BLOCK_DEFAULT_GLOBAL_COMPONENT, useValue: YourBlockerComponent },
{ provide: BLOCK_DEFAULT_SPECIFIED_COMPONENT, useValue: YourTargetSpecificBlockerComponent },
],
})
export class YourModule {}
The module is configurable by providers.
The component provided in BLOCK_DEFAULT_GLOBAL_COMPONENT
will be used as default blocker component when the target is not specified.
The component provided in BLOCK_DEFAULT_SPECIFIED_COMPONENT
will be used as default blocker component when the target is specified. If you do not provide this then the BLOCK_DEFAULT_GLOBAL_COMPONENT
will be used.
If there is no default component provided and the block
method called without component
property then the service will throw an error.
You can block the application or an element manually, by subscription, by promise and by observable.
If the target is not specified then the ApplicationRef
will be used as the target and the blocker component will be appended to the document.body
.
If you call block
multiple times for the same target then the component will be appended only once but the service will store the number of blocks. The number of blocks is grouped by the target. The appended component will be removed when the block count is zero.
import { BlockService } from '@efk3/angular-block';
@Component({...})
export class MyComponent implements OnInit {
constructor(private blockService: BlockService) {}
public ngOnInit(): void {
this.blockService.block();
setTimeout(() => {
this.blockService.unblock();
}, 3000);
}
}
The block will be initialized instantly and will be removed when the unsubscription happens.
import { BlockService } from '@efk3/angular-block';
@Component({...})
export class MyComponent implements OnInit {
constructor(private blockService: BlockService, private http: HttpClient) {}
public ngOnInit(): void {
const subscription = this.http.get('...').subscribe();
this.blockService.block(subscription);
}
}
The block will be initialized when the first subscription happens to the observable. The block
method will wrap the original observable so you have to use the returned observable to make the service work. The block will be removed automatically when the observable completes or throws an error.
import { BlockService } from '@efk3/angular-block';
@Component({...})
export class MyComponent implements OnInit {
constructor(private blockService: BlockService) {}
public ngOnInit(): void {
const subject = new Subject();
this.blockService.block(subject.asObservable()).subscribe(() => {
// logic
});
}
}
The block will be initialized instantly and will be removed when the promise returns or throws an error.
The block
method returns the original promise so you can chain the call if you want.
import { BlockService } from '@efk3/angular-block';
@Component({...})
export class MyComponent implements OnInit {
constructor(private blockService: BlockService) {}
public ngOnInit(): void {
const promise = new Promise<any>(() => {
// logic
});
this.blockService.block(promise);
promise.then(() => {
// logic
});
}
}
You can set up the block by using the block directive.
<div [k3Block]="trigger" [k3BlockerComponent]="BlockerComponent" [k3BlockData]="blockData">
</div>
The trigger can be a subscription, an observable or a promise. The blocker component can be specified with the k3BlockerComponent
input. The data for blocker component can be specified with the k3BlockData
input.
Important to know that the block will be triggered every time when the k3Block
input changes. If you want to replace e.g. an observable as a trigger then first you have to make the current observable complete or the block will not be removed.
The block
method overloaded with multiple types for easier work.
If you call it only with a trigger then it works like you don't specified anything but the trigger
in the input and returns the given trigger
.
The other call version is for more specified configuration, in this version you can use the following properties:
trigger
- You can use types described above as a trigger.target
- The blocker component will be appended to the given target(s). Simple and multiple targets (as an array) are accepted.ApplicationRef
,ViewContainerRef
andElementRef
types are supported as target.data
- This object will be injected into the blocker component.component
- You can specify the blocker component. If you do not set this property then the globally configured components will be used.
In this method also the given trigger will be returned.
You have to create your own blocker component. There is no requirement for this component but you have to add it to the entryComponents
too in your module because it will be created at runtime.
The blocker component will receive the following custom injections.
import { BLOCK_DATA } from '@efk3/angular-block'
@Inject(BLOCK_DATA) public data: any
import { BLOCK_BLOCKER_COUNT } from '@efk3/angular-block';
@Inject(BLOCK_BLOCKER_COUNT) public blockerCount: Observable<number>
import { BLOCK_TARGET, Target } from '@efk3/angular-block';
@Inject(BLOCK_TARGET) public target: Target
You can get the blocker count for a target with this method:
getBlockerCount(target?: Target): Observable<number>
You can get the blocker component for a target with this method:
getBlockerComponent(target?: Target): ComponentRef<any>
If you have to manage the blocks manually and you don't want to care about how much time you used the block then you can use the clearBlock
method which removes every block from the given target(s).
resetBlock(target?: Target): void
If you call this method then all of the blocks which are started by a trigger will be invalidated. Event if they expire they will not unblock the target(s) if you blocked them again in the meantime.
You can see a full example on stackblitz.