File

src/lib/login/login.component.ts

Metadata

Index

Properties
Methods

Constructor

constructor(authentication: RxapAuthenticationService, snackBar: MatSnackBar)
Parameters :
Name Type Optional
authentication RxapAuthenticationService No
snackBar MatSnackBar No

Methods

Public Async requestPasswordReset
requestPasswordReset(email: string)
Parameters :
Name Type Optional
email string No
Returns : any

Properties

Public Readonly requestingPasswordReset$
Default value : new ToggleSubject()
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;
  }

}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""