src/lib/login/login.component.ts
| changeDetection | ChangeDetectionStrategy.OnPush |
| providers |
LoginFormProviders
{
provide: ErrorStateMatcher, useClass: ShowOnDirtyErrorStateMatcher,
}
|
| selector | rxap-login |
| imports |
ReactiveFormsModule
FormDirective
MatIconModule
MatFormFieldModule
MatInputModule
NgIf
MatButtonModule
MatCheckboxModule
MatTooltipModule
MatProgressSpinnerModule
FormControlMarkDirtyDirective
FormSubmitFailedDirective
FormSubmittingDirective
MatProgressBarModule
AsyncPipe
MatSnackBarModule
ControlErrorDirective
|
| styleUrls | ./login.component.scss |
| templateUrl | ./login.component.html |
Properties |
|
Methods |
|
constructor(authentication: RxapAuthenticationService, snackBar: MatSnackBar)
|
|||||||||
|
Defined in src/lib/login/login.component.ts:80
|
|||||||||
|
Parameters :
|
| Public Async requestPasswordReset | ||||||
requestPasswordReset(email: string)
|
||||||
|
Defined in src/lib/login/login.component.ts:90
|
||||||
|
Parameters :
Returns :
any
|
| Public Readonly requestingPasswordReset$ |
Default value : new ToggleSubject()
|
|
Defined in src/lib/login/login.component.ts:78
|
import {
AsyncPipe,
NgIf,
} from '@angular/common';
import {
ChangeDetectionStrategy,
Component,
inject,
Inject,
} from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import {
ErrorStateMatcher,
ShowOnDirtyErrorStateMatcher,
} from '@angular/material/core';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import {
MatSnackBar,
MatSnackBarModule,
} from '@angular/material/snack-bar';
import { MatTooltipModule } from '@angular/material/tooltip';
import { RxapAuthenticationService } from '@rxap/authentication';
import {
FormControlMarkDirtyDirective,
FormDirective,
FormSubmitFailedDirective,
FormSubmittingDirective,
} from '@rxap/forms';
import { ControlErrorDirective } from '@rxap/material-form-system';
import { ToggleSubject } from '@rxap/rxjs';
import { fadeAnimation } from '../fade-animation';
import { LoginFormProviders } from './login.form';
import { RequestResetPasswordMethod } from './request-reset-password.method';
@Component({
selector: 'rxap-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
LoginFormProviders,
{
provide: ErrorStateMatcher,
useClass: ShowOnDirtyErrorStateMatcher,
},
],
animations: [
fadeAnimation,
],
imports: [
ReactiveFormsModule,
FormDirective,
MatIconModule,
MatFormFieldModule,
MatInputModule,
NgIf,
MatButtonModule,
MatCheckboxModule,
MatTooltipModule,
MatProgressSpinnerModule,
FormControlMarkDirtyDirective,
FormSubmitFailedDirective,
FormSubmittingDirective,
MatProgressBarModule,
AsyncPipe,
MatSnackBarModule,
ControlErrorDirective,
]
})
export class LoginComponent {
public readonly requestingPasswordReset$ = new ToggleSubject();
private readonly requestPasswordResetMethod = inject(RequestResetPasswordMethod);
constructor(
@Inject(RxapAuthenticationService)
private readonly authentication: RxapAuthenticationService,
@Inject(MatSnackBar)
private readonly snackBar: MatSnackBar,
) {
}
public async requestPasswordReset(email: string) {
this.requestingPasswordReset$.enable();
try {
const result = await this.requestPasswordResetMethod.call({ email });
if (!result) {
this.snackBar.open(
'The password reset request result is falsy',
undefined,
{
duration: 5000,
panelClass: 'error',
},
);
} else {
this.snackBar.open(
'The password reset request was successful',
undefined,
{
duration: 3000,
},
);
}
} catch (e: any) {
this.snackBar.open(
e.message,
undefined,
{
duration: 3000,
panelClass: 'error',
},
);
} finally {
this.requestingPasswordReset$.disable();
}
}
}
<form #form="rxapForm" [@fadeAnimation]="'in'"
class="inner mat-elevation-z5 flex flex-col justify-center items-center gap-4"
novalidate
rxapForm>
<div class="header flex flex-col items-center justify-center grow-0">
<mat-icon class="avatar-icon grow-0" color="primary">account_circle</mat-icon>
</div>
<div class="content flex flex-col gap-8">
<div class="flex flex-col">
<mat-form-field class="email-input">
<mat-icon inline matPrefix>email</mat-icon>
<mat-label i18n>Email</mat-label>
<input formControlName="email"
i18n-placeholder
matInput
name="email"
placeholder="Enter your email address"
type="email">
<mat-error *rxapControlError="let error key 'required'">
Email is <strong>required</strong>
</mat-error>
<mat-error *rxapControlError="let error key 'email'">
Please enter a valid email address
</mat-error>
</mat-form-field>
<mat-form-field class="password-input">
<mat-icon inline matPrefix>vpn_key</mat-icon>
<mat-label i18n>Password</mat-label>
<input #passwordInput
formControlName="password"
i18n-placeholder
matInput
name="password"
placeholder="Enter your password"
type="password">
<mat-error *rxapControlError="let error key 'required'">
Password is <strong>required</strong>
</mat-error>
<button (click)="passwordInput.type = 'test'" *ngIf="passwordInput.type === 'password'" mat-icon-button
matSuffix tabIndex="-1"
type="button">
<mat-icon>visibility</mat-icon>
</button>
<button (click)="passwordInput.type = 'password'" *ngIf="passwordInput.type === 'text'" mat-icon-button
matSuffix tabIndex="-1"
type="button">
<mat-icon>visibility_off</mat-icon>
</button>
</mat-form-field>
</div>
<div class="sub-controls flex flex-row flex-wrap justify-between items-center gap-4">
<mat-checkbox class="remember-input grow-0" formControlName="remember">Remember me</mat-checkbox>
<div [matTooltipDisabled]="form.control.controls['email'].valid"
class="grow-0"
matTooltip="Please enter a valid email address"
matTooltipPosition="above">
<button (click)="requestPasswordReset(form.value.email)"
[disabled]="!form.value.email || form.control.controls['email'].hasError('email')"
color="accent"
mat-button
type="button">
<span class="flex flex-row justify-between items-center gap-2">
<mat-progress-spinner *ngIf="requestingPasswordReset$ | async" [diameter]="15"
mode="indeterminate"></mat-progress-spinner>
<span class="grow-0">Password forgotten</span>
</span>
</button>
</div>
</div>
<div class="grow-0 flex flex-row justify-center items-center">
<div class="grow-0">
<button color="primary" mat-raised-button rxapFormControlMarkDirty type="submit">Login</button>
</div>
</div>
</div>
<div *rxapFormSubmitFailed="let error" class="flex flex-row justify-center items-center">
<mat-error>{{ error.message }}</mat-error>
</div>
<mat-progress-bar *rxapFormSubmitting class="loading-bar" mode="indeterminate"></mat-progress-bar>
</form>
./login.component.scss
$padding: 48px;
::ng-deep .mat-mdc-snack-bar-container.error {
background-color: var(--warn-500);
color: var(--foreground-color);
}
.inner {
padding: $padding;
position: relative;
margin: 16px 0;
background-color: var(--background-color);
::ng-deep .mat-mdc-form-field-icon-prefix, ::ng-deep .mat-mdc-form-field-icon-suffix {
color: var(--hint-text-color, gray);
}
.header {
.avatar-icon {
width: 100px;
height: 100px;
font-size: 100px;
}
}
.content {
width: 256px + $padding;
max-width: calc(100vw - #{$padding * 2} - 16px);
.email-input, .password-input {
width: 100%;
.prefix-icon {
width: 24px;
height: 24px;
display: flex;
justify-content: center;
align-items: center;
}
/* TODO(mdc-migration): The following rule targets internal classes of form-field that may no longer apply for the MDC version. */
::ng-deep .mat-form-field-prefix {
align-self: flex-end;
margin: 4px;
}
}
[type="submit"] {
width: 128px;
text-transform: uppercase;
}
}
.sub-controls > * {
margin-bottom: 8px;
}
.loading-bar {
position: absolute;
bottom: 0;
left: 0;
right: 0;
top: auto;
}
}