1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-09-27 04:03:00 +02:00
bitwarden-browser/libs/components/src/async-actions/bit-action.directive.ts
Andreas Coroiu 3ffeb684a7
feat: improve async actions docs and guards (#4215)
* docs: clarify submit handler in standalone example

* docs: clarify protection against re-running actions

* docs: clarify that these directives replace click and ngSubmit

* docs: clarify `void`

* feat: disable action directive on bitsubmit disable

* docs: fix grammar

* docs: change to note

* feat: guard against double running bitAction
2022-12-14 08:47:01 +01:00

68 lines
2.0 KiB
TypeScript

import { Directive, HostListener, Input, OnDestroy, Optional } from "@angular/core";
import { BehaviorSubject, finalize, Subject, takeUntil, tap } from "rxjs";
import { LogService } from "@bitwarden/common/abstractions/log.service";
import { ValidationService } from "@bitwarden/common/abstractions/validation.service";
import { ButtonLikeAbstraction } from "../shared/button-like.abstraction";
import { FunctionReturningAwaitable, functionToObservable } from "../utils/function-to-observable";
/**
* Allow a single button to perform async actions on click and reflect the progress in the UI by automatically
* activating the loading effect while the action is processed.
*/
@Directive({
selector: "[bitAction]",
})
export class BitActionDirective implements OnDestroy {
private destroy$ = new Subject<void>();
private _loading$ = new BehaviorSubject<boolean>(false);
disabled = false;
@Input("bitAction") protected handler: FunctionReturningAwaitable;
readonly loading$ = this._loading$.asObservable();
constructor(
private buttonComponent: ButtonLikeAbstraction,
@Optional() private validationService?: ValidationService,
@Optional() private logService?: LogService
) {}
get loading() {
return this._loading$.value;
}
set loading(value: boolean) {
this._loading$.next(value);
this.buttonComponent.loading = value;
}
@HostListener("click")
protected async onClick() {
if (!this.handler || this.loading || this.disabled || this.buttonComponent.disabled) {
return;
}
this.loading = true;
functionToObservable(this.handler)
.pipe(
tap({
error: (err: unknown) => {
this.logService?.error(`Async action exception: ${err}`);
this.validationService?.showError(err);
},
}),
finalize(() => (this.loading = false)),
takeUntil(this.destroy$)
)
.subscribe();
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
}