1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-17 08:43:33 +00:00
Files
browser/libs/components/src/async-actions/bit-submit.directive.ts
Will Martin 827c4c0301 [PM-15847] libs/components strict migration (#15738)
This PR migrates `libs/components` to use strict TypeScript.

- Remove `@ts-strict-ignore` from each file in `libs/components` and resolved any new compilation errors
- Converted ViewChild and ContentChild decorators to use the new signal-based queries using the [Angular signal queries migration](https://angular.dev/reference/migrations/signal-queries)
  - Made view/content children `required` where appropriate, eliminating the need for additional null checking. This helped simplify the strict migration.

---

Co-authored-by: Vicki League <vleague@bitwarden.com>
2025-08-18 15:36:45 -04:00

91 lines
2.7 KiB
TypeScript

import { DestroyRef, Directive, OnInit, Optional, inject, input } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { FormGroupDirective } from "@angular/forms";
import { BehaviorSubject, catchError, filter, of, switchMap } from "rxjs";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { FunctionReturningAwaitable, functionToObservable } from "../utils/function-to-observable";
/**
* Allow a form to perform async actions on submit, disabling the form while the action is processing.
*/
@Directive({
selector: "[formGroup][bitSubmit]",
})
export class BitSubmitDirective implements OnInit {
private readonly destroyRef = inject(DestroyRef);
private _loading$ = new BehaviorSubject<boolean>(false);
private _disabled$ = new BehaviorSubject<boolean>(false);
readonly handler = input.required<FunctionReturningAwaitable>({ alias: "bitSubmit" });
readonly allowDisabledFormSubmit = input<boolean>(false);
readonly loading$ = this._loading$.asObservable();
readonly disabled$ = this._disabled$.asObservable();
constructor(
private formGroupDirective: FormGroupDirective,
@Optional() validationService?: ValidationService,
@Optional() logService?: LogService,
) {
formGroupDirective.ngSubmit
.pipe(
filter(() => !this.disabled),
switchMap(() => {
// Calling functionToObservable executes the sync part of the handler
// allowing the function to check form validity before it gets disabled.
const awaitable = functionToObservable(this.handler());
// Disable form
this.loading = true;
return awaitable.pipe(
catchError((err: unknown) => {
logService?.error(`Async submit exception: ${err}`);
validationService?.showError(err);
return of(undefined);
}),
);
}),
takeUntilDestroyed(),
)
.subscribe({
next: () => (this.loading = false),
complete: () => (this.loading = false),
});
}
ngOnInit(): void {
this.formGroupDirective.statusChanges
?.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((c) => {
if (this.allowDisabledFormSubmit()) {
this._disabled$.next(false);
} else {
this._disabled$.next(c === "DISABLED");
}
});
}
get disabled() {
return this._disabled$.value;
}
set disabled(value: boolean) {
this._disabled$.next(value);
}
get loading() {
return this._loading$.value;
}
set loading(value: boolean) {
this.disabled = value;
this._loading$.next(value);
}
}