Login

A minimal centered login card with email/password fields, validation errors, and a social-login separator.

Preview

Try submitting with empty fields or an invalid email to see <ng-oat-form-error> in action.

Welcome back!!

Sign in to your account

Forgot password?
or
html
<form [formRoot]="loginForm" (ngSubmit)="onSubmit()" class="card max-w-sm w-100">
  <header>
    <h3 class="mb-0">Welcome back</h3>
    <p class="text-light mb-0">Sign in to your account</p>
  </header>
  <div class="vstack gap-3">
    <div>
      <ng-oat-input label="Email" type="email" [formField]="loginForm.email" />
      <ng-oat-form-error [control]="loginForm.email" />
    </div>
    <div>
      <ng-oat-input label="Password" type="password" [formField]="loginForm.password" />
      <ng-oat-form-error [control]="loginForm.password" />
    </div>
    <div class="hstack justify-between">
      <ng-oat-checkbox label="Remember me" [formField]="loginForm.remember" />
      <a href="#" class="text-small">Forgot password?</a>
    </div>
    <button type="submit" class="btn">Sign In</button>
    <ng-oat-separator label="or" />
    <button class="btn outline" type="button">Continue with Google</button>
  </div>
  <footer class="text-center text-small text-light">
    Don't have an account? <a href="#">Sign up</a>
  </footer>
</form>

SFC Source

typescript
import { Component, signal } from '@angular/core';
import { FormField, FormRoot, form, schema, required, email, minLength } from '@angular/forms/signals';
import { NgOatInput, NgOatCheckbox, NgOatFormError, NgOatSeparator } from '@letsprogram/ng-oat';

@Component({
  selector: 'app-login',
  imports: [FormField, FormRoot, NgOatInput, NgOatCheckbox, NgOatFormError, NgOatSeparator],
  template: `
    <div class="flex justify-center p-8">
      <form [formRoot]="loginForm" (ngSubmit)="onSubmit()" class="card max-w-sm w-100">
        <header>
          <h3 class="mb-0">Welcome back</h3>
          <p class="text-light mb-0">Sign in to your account</p>
        </header>
        <div class="vstack gap-3">
          <div>
            <ng-oat-input label="Email" type="email" placeholder="you@example.com" [formField]="loginForm.email" />
            <ng-oat-form-error [control]="loginForm.email" />
          </div>
          <div>
            <ng-oat-input label="Password" type="password" placeholder="••••••••" [formField]="loginForm.password" />
            <ng-oat-form-error [control]="loginForm.password" />
          </div>
          <div class="hstack justify-between">
            <ng-oat-checkbox label="Remember me" [formField]="loginForm.remember" />
            <a href="#" class="text-small">Forgot password?</a>
          </div>
          <button type="submit" class="btn">Sign In</button>
          <ng-oat-separator label="or" />
          <button class="btn outline" type="button">Continue with Google</button>
        </div>
        <footer class="text-center text-small text-light">
          Don't have an account? <a href="#">Sign up</a>
        </footer>
      </form>
    </div>
  `,
})
export class LoginComponent {
  private model = signal({ email: '', password: '', remember: false });

  loginForm = form(this.model, schema($ => {
    required($.email);
    email($.email);
    required($.password);
    minLength($.password, 6);
  }));

  onSubmit() { console.log('Login:', this.model()); }
}