Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | 1x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 3x 3x 1x 2x 2x 2x 2x 1x 1x 1x 1x | import {
ChangeDetectionStrategy,
Component,
inject,
input,
signal,
} from '@angular/core';
import type { InputSignal, Signal, WritableSignal } from '@angular/core';
import { Auth, signInWithEmailAndPassword } from '@angular/fire/auth';
import { FormGroup, ReactiveFormsModule } from '@angular/forms';
import type { FormControl, ValidationErrors } from '@angular/forms';
import { Router, RouterLink } from '@angular/router';
import { SpinnerComponent } from '~/app/shared/spinner/spinner.component';
import { AuthErrorMessagesComponent } from '../auth-error-messages/auth-error-messages.component';
import { getErrorCode } from '../error-code';
import { createEmailControl, createPasswordControl, PASSWORDS } from '../identity-forms';
/** Email & password credentials for Authentication */
type LoginFormGroup = FormGroup<{
email: FormControl<string | null>;
password: FormControl<string | null>;
}>;
/**
* Email and password login form.
*/
@Component({
selector: 'app-login',
imports: [
AuthErrorMessagesComponent,
ReactiveFormsModule,
RouterLink,
SpinnerComponent,
],
templateUrl: './login.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class LoginComponent {
/** Errors specific to the email field. */
public readonly $emailCntrlErrors: Signal<ValidationErrors | undefined>;
/** Aria-invalid attribute for email field. */
public readonly $emailCntrlInvalid: Signal<boolean>;
/** Login form error response code. */
public readonly $errorCode: WritableSignal<string>;
/** Errors specific to the password field. */
public readonly $passwordCntrlErrors: Signal<ValidationErrors | undefined>;
/** Aria-invalid attribute for password field. */
public readonly $passwordCntrlInvalid: Signal<boolean>;
/** Toggle Login form and spinner. */
public readonly $showForm: WritableSignal<boolean>;
public readonly emailCntrl: FormControl<string | null>;
public readonly loginForm: LoginFormGroup;
/** Used in error message for password maximum length. */
public readonly maxPasswordLength: number = PASSWORDS.maxLength;
/** Used in error message for password minimum length. */
public readonly minPasswordLength: number = PASSWORDS.minLength;
/**
* Navigate to root to allow default redirectTo Route to decide initial destination unless the
* `next` query parameter is set.
*/
public readonly next: InputSignal<string> = input<string>('/');
public readonly passwordCntrl: FormControl<string | null>;
private readonly _auth: Auth;
private readonly _router: Router = inject(Router);
constructor() {
this._auth = inject(Auth);
({ $errors: this.$emailCntrlErrors, $invalid: this.$emailCntrlInvalid, control: this.emailCntrl } = createEmailControl());
({ $errors: this.$passwordCntrlErrors, $invalid: this.$passwordCntrlInvalid, control: this.passwordCntrl } = createPasswordControl());
this.$showForm = signal<boolean>(true);
this.loginForm = new FormGroup({
email: this.emailCntrl,
password: this.passwordCntrl,
});
this.$errorCode = signal<string>('');
}
/**
* Login using credentials and then redirect to next view.
*/
public async onSubmit(): Promise<void> {
const { email, password } = this.loginForm.value;
// Validators prevent email or password being falsy, but TypeScript doesn't know that.
if (this.loginForm.invalid || !email || !password) {
throw new Error('Invalid form submitted');
}
this.$errorCode.set(''); // Clear out any existing errors
this.$showForm.set(false);
try {
await signInWithEmailAndPassword(this._auth, email, password);
await this._router.navigateByUrl(this.next());
} catch (err: unknown) {
const code = getErrorCode(err);
this.$errorCode.set(code);
this.$showForm.set(true);
}
}
}
|