add client side validation errors
This commit is contained in:
parent
77532aaac2
commit
78bf326622
@ -1,4 +1,6 @@
|
||||
<section class="my-10 md:my-30 flex flex-col md:flex-row space-x-20 space-y-10 justify-center items-center">
|
||||
<section
|
||||
class="my-10 md:my-30 flex flex-col md:flex-row space-x-20 space-y-10 justify-center items-center"
|
||||
>
|
||||
<article class="space-y-6">
|
||||
<h1 class="text-3xl text-gray-800 font-space font-bold">Register</h1>
|
||||
<h2 class="text-xl text-gray-600">Sign up with your<br />email address to get started</h2>
|
||||
@ -9,41 +11,62 @@
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<form [formGroup]="registerForm" (ngSubmit)="registerUser()"
|
||||
class="card max-w-11/12 sm:max-w-8/12 grid md:grid-cols-2 gap-4">
|
||||
<article class="space-y-4">
|
||||
<form
|
||||
[formGroup]="registerForm"
|
||||
(ngSubmit)="registerUser()"
|
||||
class="card max-w-11/12 sm:max-w-8/12 grid md:grid-cols-2 gap-4"
|
||||
>
|
||||
<fieldset class="fieldset">
|
||||
<legend class="fieldset-legend">Name</legend>
|
||||
<legend class="fieldset-legend">Name*</legend>
|
||||
<input type="text" class="input" formControlName="name" placeholder="Jhon Doe" />
|
||||
<app-error fieldName="name" [control]="registerForm.get('name')" />
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="fieldset">
|
||||
<legend class="fieldset-legend">Mobile Number</legend>
|
||||
<input type="text" class="input" formControlName="mobile_number" placeholder="+X1 XXXXXXXXXX"/>
|
||||
<legend class="fieldset-legend">Mobile Number*</legend>
|
||||
<input
|
||||
type="text"
|
||||
class="input"
|
||||
formControlName="mobile_number"
|
||||
placeholder="+X1 XXXXXXXXXX"
|
||||
/>
|
||||
<app-error fieldName="mobile number" [control]="registerForm.get('mobile_number')" />
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="fieldset">
|
||||
<legend class="fieldset-legend">Email</legend>
|
||||
<legend class="fieldset-legend">Email*</legend>
|
||||
<input type="text" class="input" formControlName="email" placeholder="Enter email here" />
|
||||
<p class="label">your-email-address@email.com</p>
|
||||
</fieldset>
|
||||
</article>
|
||||
<article class="space-y-4">
|
||||
<fieldset class="fieldset">
|
||||
<legend class="fieldset-legend">Password</legend>
|
||||
<input type="password" class="input" formControlName="password" placeholder="Type here"/>
|
||||
<app-error fieldName="email" [control]="registerForm.get('email')" />
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="fieldset">
|
||||
<legend class="fieldset-legend">Confirm Password</legend>
|
||||
<input type="text" class="input" formControlName="password_confirmation" placeholder="Type here"/>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="fieldset">
|
||||
<legend class="fieldset-legend">City</legend>
|
||||
<legend class="fieldset-legend">City*</legend>
|
||||
<input type="text" class="input" formControlName="city" placeholder="Your city name" />
|
||||
<app-error fieldName="city" [control]="registerForm.get('city')" />
|
||||
</fieldset>
|
||||
</article>
|
||||
|
||||
<fieldset class="fieldset">
|
||||
<legend class="fieldset-legend">Password*</legend>
|
||||
<input type="password" class="input" formControlName="password" placeholder="Type here" />
|
||||
<app-error fieldName="password" [control]="registerForm.get('password')" />
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="fieldset">
|
||||
<legend class="fieldset-legend">Confirm Password*</legend>
|
||||
<input
|
||||
type="text"
|
||||
class="input"
|
||||
formControlName="password_confirmation"
|
||||
placeholder="Type here"
|
||||
/>
|
||||
<app-error [control]="registerForm.get('password_confirmation')" />
|
||||
</fieldset>
|
||||
|
||||
<div class="flex flex-col col-span-2 gap-y-2">
|
||||
<button type="submit" class="btn btn-black py-2 w-full">Register</button>
|
||||
<button type="submit" [disabled]="registerForm.invalid" class="btn btn-black py-2 w-full">
|
||||
Register
|
||||
</button>
|
||||
<a href="" class="text-xs text-gray-800 text-center w-full block hover:text-teal-600"
|
||||
>Already have an account ? Login</a
|
||||
>
|
||||
|
||||
@ -1,39 +1,42 @@
|
||||
import { Component, inject, signal } from "@angular/core";
|
||||
import {FormControl, FormGroup, ReactiveFormsModule} from "@angular/forms";
|
||||
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from "@angular/forms";
|
||||
import { AuthService } from "../../services/auth-service";
|
||||
import { RegisterUserRequest } from "../../../../core/models/user.model";
|
||||
import { Error } from "../../../../shared/components/error/error";
|
||||
|
||||
@Component({
|
||||
selector: "app-register",
|
||||
imports: [ReactiveFormsModule],
|
||||
imports: [ReactiveFormsModule, Error],
|
||||
templateUrl: "./register.html",
|
||||
styleUrl: "./register.css",
|
||||
})
|
||||
export class Register {
|
||||
|
||||
authService = inject(AuthService);
|
||||
errors = signal<string[]>([]);
|
||||
|
||||
registerForm = new FormGroup({
|
||||
name: new FormControl(''),
|
||||
email: new FormControl(''),
|
||||
mobile_number: new FormControl(''),
|
||||
password: new FormControl(''),
|
||||
password_confirmation: new FormControl(''),
|
||||
city: new FormControl(''),
|
||||
name: new FormControl("", { validators: [Validators.required] }),
|
||||
email: new FormControl("", { validators: [Validators.required, Validators.email] }),
|
||||
mobile_number: new FormControl("", { validators: [Validators.required] }),
|
||||
password: new FormControl("", { validators: [Validators.required] }),
|
||||
password_confirmation: new FormControl("", { validators: [Validators.required] }),
|
||||
city: new FormControl("", { validators: [Validators.required] }),
|
||||
});
|
||||
|
||||
registerUser() {
|
||||
this.authService.register(this.registerForm.value as RegisterUserRequest)
|
||||
.subscribe({
|
||||
next: () => console.log('success'),
|
||||
// validation errors if user submit early
|
||||
if (this.registerForm.invalid) {
|
||||
this.registerForm.markAllAsTouched();
|
||||
return;
|
||||
}
|
||||
|
||||
this.authService.register(this.registerForm.value as RegisterUserRequest).subscribe({
|
||||
next: () => console.log("success"),
|
||||
error: (error) => {
|
||||
const errors: Record<number, string[]> = error?.error?.errors || {};
|
||||
const errorMessages: string[] = Object.values(errors).flat();
|
||||
this.errors.set(errorMessages);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
47
src/app/shared/components/error/error.ts
Normal file
47
src/app/shared/components/error/error.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { Component, Input } from "@angular/core";
|
||||
import { AbstractControl } from "@angular/forms";
|
||||
import { UpperCaseFirstPipe } from "../../pipes/upper-case-first-pipe";
|
||||
|
||||
@Component({
|
||||
selector: "app-error",
|
||||
imports: [UpperCaseFirstPipe],
|
||||
template: `
|
||||
@if (this.control && this.control.touched) {
|
||||
@for (error of getErrorMessages(); track error) {
|
||||
<p class="ml-2 text-xs text-red-400">{{ error | upperCaseFirst }}</p>
|
||||
}
|
||||
}
|
||||
`,
|
||||
})
|
||||
export class Error {
|
||||
@Input() control!: AbstractControl | null;
|
||||
@Input() fieldName = "This field";
|
||||
|
||||
getErrorMessages() {
|
||||
const messages: string[] = [];
|
||||
|
||||
if (this.control && this.control.errors) {
|
||||
const errors = this.control.errors;
|
||||
|
||||
if (errors["required"]) {
|
||||
messages.push(`${this.fieldName} is required.`);
|
||||
}
|
||||
if (errors["email"]) {
|
||||
messages.push(`Please enter a valid email address.`);
|
||||
}
|
||||
if (errors["minlength"]) {
|
||||
messages.push(
|
||||
`${this.fieldName} must be at least ${errors["minlength"].requiredLength} characters.`,
|
||||
);
|
||||
}
|
||||
if (errors["pattern"]) {
|
||||
messages.push(`${this.fieldName} is formatted incorrectly.`);
|
||||
}
|
||||
if (errors["serverError"]) {
|
||||
messages.push(errors["serverError"]);
|
||||
}
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
}
|
||||
8
src/app/shared/pipes/upper-case-first-pipe.spec.ts
Normal file
8
src/app/shared/pipes/upper-case-first-pipe.spec.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { UpperCaseFirstPipe } from "./upper-case-first-pipe";
|
||||
|
||||
describe("UpperCaseFirstPipe", () => {
|
||||
it("create an instance", () => {
|
||||
const pipe = new UpperCaseFirstPipe();
|
||||
expect(pipe).toBeTruthy();
|
||||
});
|
||||
});
|
||||
10
src/app/shared/pipes/upper-case-first-pipe.ts
Normal file
10
src/app/shared/pipes/upper-case-first-pipe.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { Pipe, PipeTransform } from "@angular/core";
|
||||
|
||||
@Pipe({
|
||||
name: "upperCaseFirst",
|
||||
})
|
||||
export class UpperCaseFirstPipe implements PipeTransform {
|
||||
transform(value: string): unknown {
|
||||
return value.charAt(0).toUpperCase() + value.slice(1);
|
||||
}
|
||||
}
|
||||
@ -21,7 +21,7 @@ body {
|
||||
}
|
||||
|
||||
.btn {
|
||||
@apply rounded-full transition-all duration-200 font-medium ease-out flex justify-center active:translate-y-[1px];
|
||||
@apply rounded-full transition-all duration-200 font-medium ease-out flex justify-center active:translate-y-px disabled:opacity-50 disabled:cursor-not-allowed;
|
||||
}
|
||||
|
||||
.btn-ghost {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user