Preview:
//html
<div class="register-wrapper d-flex justify-content-center align-items-center">
  <div class="card box">
    <div class="card-header text-center">
      <h4 class="card-title">Sign-up</h4>
    </div>
    <form class="form" [formGroup]="registerFormGroup" (ngSubmit)="RegisterUser()">
      <div class="card-body">
        <div class="row">
          <div class="col-md-6">
            <div class="form-group">
              <label for="name" class="form-label">Name</label>
              <input type="text" class="form-control" id="name" placeholder="Enter your Name" formControlName="name">
              <div *ngIf="registerFormGroup.controls['name'].invalid && (registerFormGroup.controls['name'].dirty || registerFormGroup.controls['name'].touched)" class="text-danger">
                <div *ngIf="registerFormGroup.controls['name'].errors?.['required']">Name is required.</div>
                <div *ngIf="registerFormGroup.controls['name'].errors?.['minlength']">Name must be at least 2 characters long.</div>
                <div *ngIf="registerFormGroup.controls['name'].errors?.['maxlength']">Name must be at most 20 characters long.</div>
              </div>
            </div>
            <div class="form-group">
              <label for="surname" class="form-label">Surname</label>
              <input type="text" class="form-control" id="surname" placeholder="Enter your Surname" formControlName="surname">
              <div *ngIf="registerFormGroup.controls['surname'].invalid && (registerFormGroup.controls['surname'].dirty || registerFormGroup.controls['surname'].touched)" class="text-danger">
                <div *ngIf="registerFormGroup.controls['surname'].errors?.['required']">Surname is required.</div>
                <div *ngIf="registerFormGroup.controls['surname'].errors?.['minlength']">Surname must be at least 2 characters long.</div>
                <div *ngIf="registerFormGroup.controls['surname'].errors?.['maxlength']">Surname must be at most 20 characters long.</div>
              </div>
            </div>
            <div class="form-group">
              <label for="email" class="form-label">Email Address</label>
              <input type="email" class="form-control" id="email" placeholder="Enter a valid Email address" formControlName="email">
              <div *ngIf="registerFormGroup.controls['email'].invalid && (registerFormGroup.controls['email'].dirty || registerFormGroup.controls['email'].touched)" class="text-danger">
                <div *ngIf="registerFormGroup.controls['email'].errors?.['required']">Email is required.</div>
                <div *ngIf="registerFormGroup.controls['email'].errors?.['email']">Email is invalid.</div>
              </div>
            </div>
            <div class="form-group">
              <label for="password" class="form-label">Password</label>
              <input type="password" class="form-control" id="password" placeholder="Enter between 8 to 15 characters" formControlName="password">
              <div *ngIf="registerFormGroup.controls['password'].invalid && (registerFormGroup.controls['password'].dirty || registerFormGroup.controls['password'].touched)" class="text-danger">
                <div *ngIf="registerFormGroup.controls['password'].errors?.['required']">Password is required.</div>
                <div *ngIf="registerFormGroup.controls['password'].errors?.['minlength']">Password must be at least 8 characters.</div>
                <div *ngIf="registerFormGroup.controls['password'].errors?.['maxlength']">Password cannot be more than 15 characters.</div>
              </div>
            </div>
            <div class="form-group">
              <label for="PhoneNumber" class="form-label">Phone Number</label>
              <input type="text" class="form-control" id="PhoneNumber" placeholder="Enter your Phone Number" formControlName="PhoneNumber">
              <div *ngIf="registerFormGroup.controls['PhoneNumber'].invalid && (registerFormGroup.controls['PhoneNumber'].dirty || registerFormGroup.controls['PhoneNumber'].touched)" class="text-danger">
                <div *ngIf="registerFormGroup.controls['PhoneNumber'].errors?.['required']">Phone Number is required.</div>
                <div *ngIf="registerFormGroup.controls['PhoneNumber'].errors?.['minlength']">Phone Number must be 10 digits.</div>
                <div *ngIf="registerFormGroup.controls['PhoneNumber'].errors?.['maxlength']">Phone Number must be 10 digits.</div>
              </div>
            </div>
          </div>

          <div class="col-md-6">
            <div class="form-group">
              <label for="Date_of_Birth" class="form-label">Date of Birth</label>
              <input type="date" class="form-control" id="Date_of_Birth" placeholder="Enter your Date of Birth" formControlName="Date_of_Birth">
              <div *ngIf="registerFormGroup.controls['Date_of_Birth'].invalid && (registerFormGroup.controls['Date_of_Birth'].dirty || registerFormGroup.controls['Date_of_Birth'].touched)" class="text-danger">
                <div *ngIf="registerFormGroup.controls['Date_of_Birth'].errors?.['required']">Date of Birth is required.</div>
                <div *ngIf="registerFormGroup.controls['Date_of_Birth'].errors?.['futureDate']">Date of birth cannot be a future date.</div>
                <div *ngIf="registerFormGroup.controls['Date_of_Birth'].errors?.['olderThan100']">Date of birth cannot be more than 100 years ago.</div>
                <div *ngIf="registerFormGroup.controls['Date_of_Birth'].errors?.['futureYear']">User cannot be younger than 16.</div>
              </div>
            </div>
            <div class="form-group">
              <label for="Id_Number" class="form-label">ID Number</label>
              <input type="text" class="form-control" id="Id_Number" placeholder="Enter your ID Number" formControlName="Id_Number">
              <div *ngIf="registerFormGroup.controls['Id_Number'].invalid && (registerFormGroup.controls['Id_Number'].dirty || registerFormGroup.controls['Id_Number'].touched)" class="text-danger">
                <div *ngIf="registerFormGroup.controls['Id_Number'].errors?.['required']">ID Number is required.</div>
                <div *ngIf="registerFormGroup.controls['Id_Number'].errors?.['minlength']">ID Number must be 13 digits.</div>
                <div *ngIf="registerFormGroup.controls['Id_Number'].errors?.['maxlength']">ID Number must be 13 digits.</div>
              </div>
            </div>
            <div class="form-group">
              <label for="Physical_Address" class="form-label">Physical Address</label>
              <input type="text" class="form-control" id="Physical_Address" placeholder="Enter your Physical Address" formControlName="Physical_Address">
              <div *ngIf="registerFormGroup.controls['Physical_Address'].invalid && (registerFormGroup.controls['Physical_Address'].dirty || registerFormGroup.controls['Physical_Address'].touched)" class="text-danger">
                <div *ngIf="registerFormGroup.controls['Physical_Address'].errors?.['required']">Physical Address is required.</div>
                <div *ngIf="registerFormGroup.controls['Physical_Address'].errors?.['minlength']">Physical Address must be at least 7 characters long.</div>
                <div *ngIf="registerFormGroup.controls['Physical_Address'].errors?.['maxlength']">Physical Address must be at most 100 characters long.</div>
              </div>
            </div>
            <div class="form-group">
              <label for="Photo" class="form-label">Photo</label>
              <input type="file" class="form-control" id="Photo" (change)="onFileSelected($event)">
              <div *ngIf="registerFormGroup.controls['Photo'].invalid && (registerFormGroup.controls['Photo'].dirty || registerFormGroup.controls['Photo'].touched)" class="text-danger">
                <div *ngIf="registerFormGroup.controls['Photo'].errors?.['required']">Photo is required.</div>
              </div>
            </div>
            <div class="form-group">
              <label for="User_Type_ID" class="form-label">User Type</label>
              <select class="form-control" id="User_Type_ID" formControlName="User_Type_ID">
                <option value="1">Owner</option>
                <option value="2">Employee</option>
                <option value="3">Member</option>
              </select>
              <div *ngIf="registerFormGroup.controls['User_Type_ID'].invalid && (registerFormGroup.controls['User_Type_ID'].dirty || registerFormGroup.controls['User_Type_ID'].touched)" class="text-danger">
                <div *ngIf="registerFormGroup.controls['User_Type_ID'].errors?.['required']">User Type is required.</div>
              </div>
            </div>
          </div>
        </div>
      </div>
      <div class="card-footer d-flex justify-content-between">
        <button type="submit" class="btn btn-primary">Sign-up</button>
        <button type="button" class="btn btn-secondary" (click)="resetForm()">Cancel</button>
      </div>
    </form>
  </div>
</div>

//ts
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { MatSnackBar } from '@angular/material/snack-bar';
import { UserService } from '../../Services/userprofile.service';
import { FlexLayoutModule } from '@angular/flex-layout';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-register',
  standalone: true,
  imports: [ReactiveFormsModule, CommonModule, FlexLayoutModule],
  templateUrl: './register.component.html',
  styleUrls: ['./register.component.css']
})
export class RegisterComponent {
  submitted = false;
  registerFormGroup: FormGroup;
  selectedFile: File | null = null;

  constructor(
    private router: Router,
    private userService: UserService,
    private fb: FormBuilder,
    private snackBar: MatSnackBar
  ) {
    this.registerFormGroup = this.fb.group({
      // email: ['', [Validators.required, Validators.email, Validators.pattern(/^[a-z0-9._%+-]+@gmail\.com$/), Validators.minLength(3), Validators.maxLength(50)]],
      email: ['', [Validators.required, Validators.email]],
      password: ['', [Validators.required, Validators.minLength(8), Validators.maxLength(15)]],
      name: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(20)]],
      surname: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(20)]],
      // PhoneNumber: ['', [Validators.required, Validators.pattern(/^0\d{9}$/)]],
      PhoneNumber: ['', [Validators.required, Validators.minLength(10), Validators.maxLength(10)]],
      Date_of_Birth: ['', [Validators.required, this.validateDateOfBirth]],
      // Id_Number: ['', [Validators.required, Validators.pattern(/^\d{2}(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])\d{7}$/)]],
      Id_Number: ['', [Validators.required, Validators.minLength(13), Validators.maxLength(13)]],
      Physical_Address: ['', [Validators.required, Validators.minLength(7), Validators.maxLength(100)]],
      Photo: ['', Validators.required],
      // User_Status_ID: [1, [Validators.required, Validators.min(1), Validators.max(2)]],
      // User_Type_ID: ['', [Validators.required, Validators.min(1), Validators.max(3)]]
      User_Status_ID: [1, Validators.required],
      User_Type_ID: ['', Validators.required]
    });
  }

  ngOnInit() { }

  validateDateOfBirth(control: any): { [key: string]: any } | null {
    const selectedDate = new Date(control.value);
    const currentDate = new Date();
    const minDate = new Date(currentDate.getFullYear() - 100, currentDate.getMonth(), currentDate.getDate());
    const maxDate = new Date(currentDate.getFullYear() - 16, currentDate.getMonth(), currentDate.getDate());

    if (selectedDate > currentDate) {
      return { 'futureDate': true };
    }

    if (selectedDate < minDate) {
      return { 'olderThan100': true };
    }

    if (selectedDate > maxDate) {
      return { 'futureYear': true };
    }

    return null;
  }

  onFileSelected(event: any) {
    const file = event.target.files[0];
    if (file) {
      this.selectedFile = file;
      this.registerFormGroup.patchValue({ Photo: file });
    } else {
      this.selectedFile = null;
    }
  }

  RegisterUser() {
    this.submitted = true;
  
    if (this.registerFormGroup.valid && this.selectedFile) {
      console.log('Form is valid');
      console.log('Selected file:', this.selectedFile);

      const emailLowerCase = this.registerFormGroup.value.email.toLowerCase();
      this.registerFormGroup.patchValue({ email: emailLowerCase });
  
      const formData: FormData = new FormData();
      formData.append('name', this.registerFormGroup.value.name);
      formData.append('surname', this.registerFormGroup.value.surname);
      formData.append('email', this.registerFormGroup.value.email);
      formData.append('password', this.registerFormGroup.value.password);
      formData.append('PhoneNumber', this.registerFormGroup.value.PhoneNumber);
      formData.append('Date_of_Birth', this.registerFormGroup.value.Date_of_Birth);
      formData.append('Id_Number', this.registerFormGroup.value.Id_Number);
      formData.append('Physical_Address', this.registerFormGroup.value.Physical_Address);
      formData.append('Photo', this.selectedFile, this.selectedFile.name);
      formData.append('User_Status_ID', this.registerFormGroup.value.User_Status_ID);
      formData.append('User_Type_ID', this.registerFormGroup.value.User_Type_ID);
  
      this.userService.RegisterUser(formData).subscribe({
        next: (response) => {
          console.log('Registration Success:', response);
          this.registerFormGroup.reset();
          this.openSnackbar('Your user profile has been created', 'success');
          this.router.navigate(['/login']);
        },
        error: (error) => {
          console.log('Registration Error:', error);
          let errorMessage = 'Registration failed: ';
  
          if (error.error === 'DuplicateEmail') {
            errorMessage += 'This email address is already registered.';
          } else if (error.error === 'DuplicatePhoneNumber') {
            errorMessage += 'This phone number is already registered.';
          } else if (error.error === 'DuplicateDateOfBirth') {
            errorMessage += 'This date of birth is already registered.';
          } else if (error.error === 'DuplicateIdNumber') {
            errorMessage += 'This ID number is already registered.';
          } else if (error.error === 'DuplicatePhoto') {
            errorMessage += 'This photo is already associated with another user.';
          } else {
            errorMessage += 'Unknown error. Please try again later.';
          }
  
          this.openSnackbar(errorMessage, 'danger');
        }
      });
    } else {
      console.log('Form is not valid or file is not selected');
      console.log(this.registerFormGroup.errors);
      console.log(this.registerFormGroup.controls);

      this.openSnackbar('Form is not filled in correctly', 'danger');
    }
  }
  

  openSnackbar(message: string, type: 'success' | 'danger') {
    this.snackBar.open(message, 'Close', {
      duration: 5000,
      panelClass: type === 'success' ? 'snackbar-success' : 'snackbar-danger'
    });
  }

  get f() {
    return this.registerFormGroup.controls;
  }

  resetForm() {
    this.registerFormGroup.reset({
      user_status_id: 1  // Reset user_status_id to default value of 1 (Active)
    });
    this.router.navigate(['/login']);
  }
}

//css
.register-wrapper {
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  background: #f0f4f7; /* Light blue background */
}

.box {
  padding: 60px 50px 40px;
  width: 100%;
  max-width: 800px;
  background: #fff;
  border-radius: 10px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

.card-header {
  text-align: center;
  font-weight: 700;
  color: #000; /* Blue color for header text */
}

.card-title {
  font-size: 30px;
  margin: 0;
}

.form-group label {
  color: #000; /* Blue color for form labels */
}

.form-control {
  border: 1px solid #ccc;
  padding: 10px;
  border-radius: 5px;
  transition: border-color 0.3s ease-in-out;
}

.form-control:focus {
  border-color: #000; /* Blue border on focus */
  box-shadow: 0 0 5px rgba(63, 81, 181, 0.2); /* Light blue shadow on focus */
}

.text-danger {
  color: #f44336; /* Red color for error messages */
}

.btn-primary {
  background-color: #4caf50; /* Green background for primary button */
  border-color: #4caf50; /* Green border for primary button */
  color: #fff; /* White text for primary button */
}

.btn-secondary {
  background-color: #f44336; /* Red background for secondary button */
  border-color: #f44336; /* Red border for secondary button */
  color: #fff; /* White text for secondary button */
}

.card-footer {
  display: flex;
  justify-content: space-between;
  padding: 20px;
}

.card-footer .btn {
  width: 48%;
}

/* Snackbar Styles */
.snackbar {
  visibility: visible;
  min-width: 250px;
  margin-left: -125px;
  background-color: #333;
  color: #fff;
  text-align: center;
  border-radius: 2px;
  padding: 16px;
  position: fixed;
  z-index: 1;
  left: 50%;
  bottom: 30px;
  font-size: 17px;
}

.snackbar-success {
  background-color: #4caf50; /* Green for success */
}

.snackbar-danger {
  background-color: #f44336; /* Red for error */
}

.close-snackbar {
  color: white;
  font-weight: bold;
  float: right;
  font-size: 22px;
  line-height: 20px;
  cursor: pointer;
  background: none;
  border: none;
}
downloadDownload PNG downloadDownload JPEG downloadDownload SVG

Tip: You can change the style, width & colours of the snippet with the inspect tool before clicking Download!

Click to optimize width for Twitter