Saturday, August 16, 2025
0 comments

Master Angular 20 Forms: A Beginner’s Ultimate Guide to Template-Driven and Reactive Forms with Hands-On Projects and Troubleshooting (Part-5)

 








Angular 20 is a robust framework for building dynamic single-page applications (SPAs), and forms are essential for user interactions like registration, login, or data submission. As a beginner, you’ve explored components, templates, directives, routing, and services. Now, it’s time to master Angular forms, which allow you to collect and validate user input efficiently. In this guide, we’ll cover template-driven forms (using FormsModule) and reactive forms (using ReactiveFormsModule), along with form controls, groups, validators, submissions, and custom validation. We’ll build a hands-on project featuring a User Registration Form (reactive) and an Admin Login Form with validation, integrated with your existing User and Admin components, UserService, and routing setup. 
We’ll also troubleshoot common issues like the TS2305 error.

This tutorial assumes you have a basic Angular 20 project (e.g., angular-routing-demo from previous modules) and familiarity with components, routing, and services. Let’s dive into Angular forms!
IntroductionForms are a cornerstone of web applications, enabling user input for tasks like signing up, logging in, or updating data. Angular provides two approaches to forms:
  • Template-Driven Forms: Simple, driven by template directives (e.g., ngModel).
  • Reactive Forms: Programmatic, offering more control and flexibility.
In this guide, we’ll explore both approaches, focusing on form controls, groups, validators, and error handling. The hands-on project will create a reactive User Registration Form and a template-driven Admin Login Form, integrated with your existing project.What You’ll Learn:
  • Template-driven forms with FormsModule.
  • Reactive forms with ReactiveFormsModule.
  • Form controls, groups, and built-in validators.
  • Handling form submissions.
  • Error handling and custom validation.
  • Building a User Registration Form and Admin Login Form.

Template-Driven Forms (Using FormsModule)Template-driven forms rely on Angular’s template syntax and directives (e.g., ngModel) to manage form data and validation. They’re ideal for simple forms with minimal logic.Key Features
  • Uses FormsModule for two-way data binding ([(ngModel)]).
  • Validation is defined in the template using directives (e.g., required).
  • Best for quick, straightforward forms.
Example
typescript
// src/app/components/example/example.component.ts
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-example',
  template: `
    form: (ngSubmit)="onSubmit()":
      input: [(ngModel)]="username" name="username" required
      button: type="submit": Submit
  `,
  standalone: true,
  imports: [FormsModule]
})
export class ExampleComponent {
  username = '';

  onSubmit() {
    console.log('Username:', this.username);
  }
}
Explanation:
  • FormsModule: Enables ngModel and form directives.
  • [(ngModel)]: Binds the input to the username property.
  • required: A built-in HTML validator enforced by Angular.

Reactive Forms (Using ReactiveFormsModule)Reactive forms are programmatic, defined in the component’s TypeScript code using FormControl, FormGroup, and validators. They offer greater control, testability, and flexibility for complex forms.Key Features
  • Uses ReactiveFormsModule for form creation.
  • Form structure and validation are defined in the component.
  • Ideal for dynamic forms and complex validation.
Example
typescript
// src/app/components/example/example.component.ts
import { Component } from '@angular/core';
import { ReactiveFormsModule, FormGroup, FormControl, Validators } from '@angular/forms';

@Component({
  selector: 'app-example',
  template: `
    form: [formGroup]="form" (ngSubmit)="onSubmit()":
      input: formControlName="username"
      button: type="submit": Submit
  `,
  standalone: true,
  imports: [ReactiveFormsModule]
})
export class ExampleComponent {
  form = new FormGroup({
    username: new FormControl('', Validators.required)
  });

  onSubmit() {
    console.log('Form Value:', this.form.value);
  }
}
Explanation:
  • ReactiveFormsModule: Provides reactive form APIs.
  • FormGroup: Groups multiple form controls.
  • FormControl: Manages a single input field with validators.
  • Validators.required: Ensures the field is not empty.

Form Controls, Groups, and ValidatorsForm ControlsA FormControl represents a single form input (e.g., text field, checkbox). It tracks the value, validation status, and user interactions.
typescript
new FormControl('', Validators.required)
Form GroupsA FormGroup combines multiple FormControl instances, allowing you to manage a form as a single unit.
typescript
form = new FormGroup({
  username: new FormControl('', Validators.required),
  email: new FormControl('', [Validators.required, Validators.email])
});
Built-In ValidatorsAngular provides validators like:
  • Validators.required: Ensures the field is not empty.
  • Validators.email: Validates email format.
  • Validators.minLength(n): Ensures minimum length.
  • Validators.pattern(regex): Matches a regular expression.

Handling Form SubmissionsForm submissions are handled using the (ngSubmit) event, which triggers a method in the component.Template-Driven Example
typescript
onSubmit() {
  console.log('Form submitted:', this.model);
}
Reactive Example
typescript
onSubmit() {
  if (this.form.valid) {
    console.log('Form submitted:', this.form.value);
  }
}
Explanation:
  • Check form.valid in reactive forms to ensure all validations pass.
  • Use form.value to access form data.

Error Handling & Custom ValidationError HandlingDisplay validation errors in the template using *ngIf and form control properties (e.g., touched, errors).
typescript
// Template
div: *ngIf="form.get('username')?.hasError('required') && form.get('username')?.touched":
  Username is required
Explanation:
  • hasError('required'): Checks for specific validation errors.
  • touched: Ensures errors show only after user interaction.
Custom ValidationCreate a custom validator for specific rules (e.g., no spaces in username).
typescript
function noSpacesValidator(control: FormControl): { [key: string]: any } | null {
  return control.value?.includes(' ') ? { noSpaces: true } : null;
}
Apply it to a FormControl:
typescript
username: new FormControl('', [Validators.required, noSpacesValidator])

Hands-On Project: User Registration Form (Reactive) and Admin Login Form (Template-Driven)Let’s extend your angular-routing-demo project to include a reactive User Registration Form and a template-driven Admin Login Form, using UserService for data management and integrating with routing, directives, and pipes.Step 1: Set Up the ProjectIf not already set up:
bash
ng new angular-routing-demo
cd angular-routing-demo
ng g component components/user
ng g component components/admin
ng g component components/user-details
ng g component components/not-found
ng g component components/admin-dashboard
ng g component components/admin-settings
ng g component components/register
ng g component components/login
ng g pipe pipes/filter-by-role
ng g service services/user
Step 2: Update app.routes.tsAdd routes for registration and login:
typescript
// src/app/app.routes.ts
import { Routes } from '@angular/router';
import { UserComponent } from './components/user/user.component';
import { AdminComponent } from './components/admin/admin.component';
import { UserDetailsComponent } from './components/user-details/user-details.component';
import { NotFoundComponent } from './components/not-found/not-found.component';
import { AdminDashboardComponent } from './components/admin-dashboard/admin-dashboard.component';
import { AdminSettingsComponent } from './components/admin-settings/admin-settings.component';
import { RegisterComponent } from './components/register/register.component';
import { LoginComponent } from './components/login/login.component';

export const routes: Routes = [
  { path: 'user', component: UserComponent },
  { path: 'user/:id', component: UserDetailsComponent },
  { path: 'register', component: RegisterComponent },
  { path: 'login', component: LoginComponent },
  {
    path: 'admin',
    component: AdminComponent,
    children: [
      { path: 'dashboard', component: AdminDashboardComponent },
      { path: 'settings', component: AdminSettingsComponent },
      { path: '', redirectTo: 'dashboard', pathMatch: 'full' }
    ]
  },
  { path: '', redirectTo: '/user', pathMatch: 'full' },
  { path: '**', component: NotFoundComponent }
];
Step 3: Update main.ts
typescript
// src/main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { provideRouter } from '@angular/router';
import { AppComponent } from './app/app.component';
import { routes } from './app/app.routes';

bootstrapApplication(AppComponent, {
  providers: [provideRouter(routes)]
}).catch(err => console.error(err));
Step 4: Update AppComponent
typescript
// src/app/app.component.ts
import { Component, OnInit } from '@angular/core';
import { Router, NavigationStart, NavigationEnd, RouterOutlet, RouterLink } from '@angular/router';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  standalone: true,
  imports: [RouterOutlet, RouterLink]
})
export class AppComponent implements OnInit {
  constructor(private router: Router) {}

  ngOnInit() {
    this.router.events.subscribe(event => {
      if (event instanceof NavigationStart) {
        console.log('Navigation started:', event.url);
      } else if (event instanceof NavigationEnd) {
        console.log('Navigation ended:', event.url);
      }
    });
  }
}
typescript
// src/app/app.component.html
nav:
  a: routerLink="/user" routerLinkActive="active": User Dashboard
  a: routerLink="/register" routerLinkActive="active": Register
  a: routerLink="/login" routerLinkActive="active": Login
  a: routerLink="/admin" routerLinkActive="active": Admin Panel
router-outlet
css
// src/app/app.component.css
nav { margin: 20px; }
a { margin-right: 10px; text-decoration: none; }
.active { font-weight: bold; color: #007bff; }
Step 5: Update UserService
typescript
// src/app/services/user.service.ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private users = [
    { id: 1, name: 'Alice', role: 'admin', email: 'alice@example.com' },
    { id: 2, name: 'Bob', role: 'user', email: 'bob@example.com' },
    { id: 3, name: 'Charlie', role: 'admin', email: 'charlie@example.com' },
    { id: 4, name: 'David', role: 'user', email: 'david@example.com' }
  ];

  private admins = [
    { username: 'admin1', password: 'admin123' },
    { username: 'admin2', password: 'admin456' }
  ];

  getUsers() {
    return this.users;
  }

  getUserById(id: number) {
    return this.users.find(user => user.id === id);
  }

  addUser(user: { id: number; name: string; role: string; email: string }) {
    this.users.push(user);
  }

  validateAdmin(username: string, password: string) {
    return this.admins.some(admin => admin.username === username && admin.password === password);
  }
}
Explanation:
  • Added email to users for the registration form.
  • Added admins array and validateAdmin() for login validation.
Step 6: Create RegisterComponent (Reactive Form)
typescript
// src/app/components/register/register.component.ts
import { Component } from '@angular/core';
import { ReactiveFormsModule, FormGroup, FormControl, Validators } from '@angular/forms';
import { Router, RouterLink } from '@angular/router';
import { UserService } from '../../services/user.service';

@Component({
  selector: 'app-register',
  templateUrl: './register.component.html',
  styleUrls: ['./register.component.css'],
  standalone: true,
  imports: [ReactiveFormsModule, RouterLink]
})
export class RegisterComponent {
  registerForm = new FormGroup({
    name: new FormControl('', [Validators.required, Validators.minLength(3)]),
    email: new FormControl('', [Validators.required, Validators.email]),
    role: new FormControl('user', [Validators.required])
  });

  constructor(private userService: UserService, private router: Router) {}

  onSubmit() {
    if (this.registerForm.valid) {
      const formValue = this.registerForm.value;
      this.userService.addUser({
        id: this.userService.getUsers().length + 1,
        name: formValue.name || '',
        email: formValue.email || '',
        role: formValue.role || 'user'
      });
      this.registerForm.reset();
      this.router.navigate(['/user']);
    }
  }
}
typescript
// src/app/components/register/register.component.html
h2: User Registration
form: [formGroup]="registerForm" (ngSubmit)="onSubmit()":
  div:
    label: Name: 
    input: formControlName="name"
    div: *ngIf="registerForm.get('name')?.hasError('required') && registerForm.get('name')?.touched":
      Name is required
    div: *ngIf="registerForm.get('name')?.hasError('minlength') && registerForm.get('name')?.touched":
      Name must be at least 3 characters
  div:
    label: Email: 
    input: formControlName="email"
    div: *ngIf="registerForm.get('email')?.hasError('required') && registerForm.get('email')?.touched":
      Email is required
    div: *ngIf="registerForm.get('email')?.hasError('email') && registerForm.get('email')?.touched":
      Invalid email format
  div:
    label: Role: 
    select: formControlName="role":
      option: value="user": User
      option: value="admin": Admin
  button: type="submit" [disabled]="registerForm.invalid": Register
p: Back to User Dashboard
css
// src/app/components/register/register.component.css
form { margin: 20px; padding: 20px; border: 1px solid #ccc; }
input, select { padding: 5px; margin: 5px; width: 200px; }
div { margin-bottom: 10px; }
.error { color: red; font-size: 0.9em; }
Explanation:
  • Uses ReactiveFormsModule for a programmatic form.
  • FormGroup with FormControl instances for name, email, and role.
  • Validators: required, minLength, email.
  • Error messages display only when fields are touched.
  • Submits to UserService and navigates to /user.
Step 7: Create LoginComponent (Template-Driven Form)
typescript
// src/app/components/login/login.component.ts
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { Router, RouterLink } from '@angular/router';
import { UserService } from '../../services/user.service';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css'],
  standalone: true,
  imports: [FormsModule, RouterLink]
})
export class LoginComponent {
  username = '';
  password = '';
  errorMessage = '';

  constructor(private userService: UserService, private router: Router) {}

  onSubmit() {
    if (this.userService.validateAdmin(this.username, this.password)) {
      this.router.navigate(['/admin']);
    } else {
      this.errorMessage = 'Invalid username or password';
    }
  }
}
typescript
// src/app/components/login/login.component.html
h2: Admin Login
form: (ngSubmit)="onSubmit()":
  div:
    label: Username: 
    input: [(ngModel)]="username" name="username" required
    div: *ngIf="form.username?.errors?.['required'] && form.username?.touched": Username is required
  div:
    label: Password: 
    input: [(ngModel)]="password" name="password" type="password" required #passwordInput
    div: *ngIf="form.password?.errors?.['required'] && form.password?.touched": Password is required
  div: *ngIf="errorMessage" class="error": {{ errorMessage }}
  button: type="submit" [disabled]="!form.valid": Login
p: Back to User Dashboard
css
// src/app/components/login/login.component.css
form { margin: 20px; padding: 20px; border: 1px solid #ccc; }
input { padding: 5px; margin: 5px; width: 200px; }
div { margin-bottom: 10px; }
.error { color: red; font-size: 0.9em; }
Explanation:
  • Uses FormsModule for a template-driven form.
  • ngModel binds username and password.
  • required validator ensures fields are filled.
  • #passwordInput is a template reference variable (not used here but included for reference).
  • Validates credentials via UserService and navigates to /admin on success.
Step 8: Update UserComponent
typescript
// src/app/components/user/user.component.ts
import { Component, OnInit } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
import { UserService } from '../../services/user.service';

@Component({
  selector: 'app-user',
  templateUrl: './user.component.html',
  styleUrls: ['./user.component.css'],
  standalone: true,
  imports: [FormsModule, RouterLink]
})
export class UserComponent implements OnInit {
  searchQuery = '';
  users: { id: number; name: string; role: string; email: string }[] = [];

  constructor(
    private route: ActivatedRoute,
    private router: Router,
    private userService: UserService
  ) {}

  ngOnInit() {
    this.users = this.userService.getUsers();
    this.route.queryParamMap.subscribe(params => {
      this.searchQuery = params.get('search') || '';
    });
  }

  updateQuery() {
    this.router.navigate(['/user'], { queryParams: { search: this.searchQuery } });
  }
}
typescript
// src/app/components/user/user.component.html
h2: User Dashboard
div:
  label: Search users: 
  input: [(ngModel)]="searchQuery" placeholder="Search users"
  button: (click)="updateQuery()": Search
p: Search Query: {{ searchQuery }}
ul:
  li: *ngFor="let user of users":
    a: routerLink="/user/{{ user.id }}": {{ user.name }} ({{ user.role }})
css
// src/app/components/user/user.component.css
ul { list-style-type: none; padding: 0; }
li { padding: 10px; margin: 5px 0; border: 1px solid #ccc; }
Step 9: Update UserDetailsComponent
typescript
// src/app/components/user-details/user-details.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, RouterLink } from '@angular/router';
import { UserService } from '../../services/user.service';

@Component({
  selector: 'app-user-details',
  templateUrl: './user-details.component.html',
  standalone: true,
  imports: [RouterLink]
})
export class UserDetailsComponent implements OnInit {
  user: { id: number; name: string; role: string; email: string } | undefined;

  constructor(
    private route: ActivatedRoute,
    private userService: UserService
  ) {}

  ngOnInit() {
    this.route.paramMap.subscribe(params => {
      const id = Number(params.get('id'));
      this.user = this.userService.getUserById(id);
    });
  }
}
typescript
// src/app/components/user-details/user-details.component.html
h2: User Details
div: *ngIf="user; else noUser":
  p: Name: {{ user.name }}
  p: Email: {{ user.email }}
  p: Role: {{ user.role | uppercase }}
p: else noUser: User not found
p: Back to User Dashboard
Step 10: Update AdminComponent
typescript
// src/app/components/admin/admin.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { RouterOutlet, RouterLink } from '@angular/router';
import { FilterByRolePipe } from '../../pipes/filter-by-role.pipe';
import { UserService } from '../../services/user.service';

@Component({
  selector: 'app-admin',
  templateUrl: './admin.component.html',
  styleUrls: ['./admin.component.css'],
  standalone: true,
  imports: [FormsModule, RouterOutlet, RouterLink, FilterByRolePipe]
})
export class AdminComponent implements OnInit, OnDestroy {
  users: { id: number; name: string; role: string; email: string }[] = [];
  filterRole = 'admin';
  isHighlighted = false;

  constructor(private userService: UserService) {}

  ngOnInit() {
    console.log('AdminComponent: Initialized');
    this.users = this.userService.getUsers();
  }

  ngOnDestroy() {
    console.log('AdminComponent: Destroyed');
  }

  toggleHighlight() {
    this.isHighlighted = !this.isHighlighted;
  }

  setFilterRole(role: string) {
    this.filterRole = role;
  }

  logSearch(value: string) {
    console.log('Search:', value);
  }
}
typescript
// src/app/components/admin/admin.component.html
h2: Admin Panel
nav:
  a: routerLink="dashboard" routerLinkActive="active": Dashboard
  a: routerLink="settings" routerLinkActive="active": Settings
router-outlet
h3: Admin Users List
div:
  label: Filter by Role: 
  select: [(ngModel)]="filterRole" (change)="setFilterRole(filterRole)":
    option: value="all": All
    option: value="admin": Admin
    option: value="user": User
button: (click)="toggleHighlight()": Toggle Highlight
ul:
  li: *ngFor="let user of users | filterByRole:filterRole" [ngClass]="{'highlight': isHighlighted && user.role === 'admin'}":
    {{ user.name | uppercase }} ({{ user.role }})
input: #searchInput placeholder="Search users"
button: (click)="logSearch(searchInput.value)": Search
css
// src/app/components/admin/admin.component.css
ul { list-style-type: none; padding: 0; }
li { padding: 10px; margin: 5px 0; border: 1px solid #ccc; }
.highlight { background-color: #e0f7fa; }
nav { margin: 10px 0; }
a { margin-right: 10px; text-decoration: none; }
.active { font-weight: bold; color: #007bff; }
Step 11: Update Child Components
typescript
// src/app/components/admin-dashboard/admin-dashboard.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-admin-dashboard',
  template: `
    h3: Admin Dashboard
    p: Welcome to the Admin Dashboard!
  `,
  standalone: true
})
export class AdminDashboardComponent {}
typescript
// src/app/components/admin-settings/admin-settings.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-admin-settings',
  template: `
    h3: Admin Settings
    p: Configure settings here.
  `,
  standalone: true
})
export class AdminSettingsComponent {}
Step 12: Update NotFoundComponent
typescript
// src/app/components/not-found/not-found.component.ts
import { Component } from '@angular/core';
import { RouterLink } from '@angular/router';

@Component({
  selector: 'app-not-found',
  template: `
    h2: 404 - Page Not Found
    p: Go back to User Dashboard
  `,
  standalone: true,
  imports: [RouterLink]
})
export class NotFoundComponent {}
Step 13: Update FilterByRolePipe
typescript
// src/app/pipes/filter-by-role.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'filterByRole',
  standalone: true
})
export class FilterByRolePipe implements PipeTransform {
  transform(users: any[], role: string): any[] {
    if (!users || role === 'all') return users;
    return users.filter(user => user.role === role);
  }
}
Step 14: Add Custom ValidatorCreate a custom validator for no spaces in the username:
typescript
// src/app/validators/no-spaces.validator.ts
import { AbstractControl, ValidatorFn } from '@angular/forms';

export function noSpacesValidator(): ValidatorFn {
  return (control: AbstractControl): { [key: string]: any } | null => {
    const hasSpaces = control.value?.includes(' ');
    return hasSpaces ? { noSpaces: true } : null;
  };
}
Update RegisterComponent to use it:
typescript
// src/app/components/register/register.component.ts
import { Component } from '@angular/core';
import { ReactiveFormsModule, FormGroup, FormControl, Validators } from '@angular/forms';
import { Router, RouterLink } from '@angular/router';
import { UserService } from '../../services/user.service';
import { noSpacesValidator } from '../../validators/no-spaces.validator';

@Component({
  selector: 'app-register',
  templateUrl: './register.component.html',
  styleUrls: ['./register.component.css'],
  standalone: true,
  imports: [ReactiveFormsModule, RouterLink]
})
export class RegisterComponent {
  registerForm = new FormGroup({
    name: new FormControl('', [Validators.required, Validators.minLength(3), noSpacesValidator()]),
    email: new FormControl('', [Validators.required, Validators.email]),
    role: new FormControl('user', [Validators.required])
  });

  constructor(private userService: UserService, private router: Router) {}

  onSubmit() {
    if (this.registerForm.valid) {
      const formValue = this.registerForm.value;
      this.userService.addUser({
        id: this.userService.getUsers().length + 1,
        name: formValue.name || '',
        email: formValue.email || '',
        role: formValue.role || 'user'
      });
      this.registerForm.reset();
      this.router.navigate(['/user']);
    }
  }
}
typescript
// src/app/components/register/register.component.html
h2: User Registration
form: [formGroup]="registerForm" (ngSubmit)="onSubmit()":
  div:
    label: Name: 
    input: formControlName="name"
    div: *ngIf="registerForm.get('name')?.hasError('required') && registerForm.get('name')?.touched":
      Name is required
    div: *ngIf="registerForm.get('name')?.hasError('minlength') && registerForm.get('name')?.touched":
      Name must be at least 3 characters
    div: *ngIf="registerForm.get('name')?.hasError('noSpaces') && registerForm.get('name')?.touched":
      Name cannot contain spaces
  div:
    label: Email: 
    input: formControlName="email"
    div: *ngIf="registerForm.get('email')?.hasError('required') && registerForm.get('email')?.touched":
      Email is required
    div: *ngIf="registerForm.get('email')?.hasError('email') && registerForm.get('email')?.touched":
      Invalid email format
  div:
    label: Role: 
    select: formControlName="role":
      option: value="user": User
      option: value="admin": Admin
  button: type="submit" [disabled]="registerForm.invalid": Register
p: Back to User Dashboard
Step 15: Test the Application
  1. Run ng serve and visit http://localhost:4200.
  2. Test features:
    • Register (/register):
      • Fill out the form (e.g., name: “Eve”, email: “eve@example.com”, role: “user”).
      • Try invalid inputs (e.g., empty fields, spaces in name) to see error messages.
      • Submit to add the user and navigate to /user.
    • Login (/login):
      • Use admin1/admin123 to log in and navigate to /admin.
      • Try invalid credentials to see the error message.
    • User Dashboard (/user):
      • Verify new users appear in the list.
      • Use the search query and navigate to user details.
    • Admin Panel (/admin):
      • Filter users and toggle highlighting.
      • Navigate to child routes (/admin/dashboard, /admin/settings).
    • User Details (/user/:id):
      • View user details or “User not found”.
    • 404 Page (/invalid):
      • Confirm the Not Found page appears.

Troubleshooting Common ErrorsTS2305: Module '"./app/app"' has no exported member 'App'Cause: main.ts imports App instead of AppComponent.Fix:
typescript
// src/main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { provideRouter } from '@angular/router';
import { AppComponent } from './app/app.component';
import { routes } from './app/app.routes';

bootstrapApplication(AppComponent, {
  providers: [provideRouter(routes)]
}).catch(err => console.error(err));
Steps:
  1. Verify app.component.ts exports AppComponent.
  2. Check the file path.
  3. Run ng serve.
Form Directives Not WorkingCause: Missing FormsModule or ReactiveFormsModule.Fix:
  • Import FormsModule for template-driven forms or ReactiveFormsModule for reactive forms in the component’s imports.
Validation Errors Not ShowingCause: Incorrect error checking or missing touched condition.Fix:
  • Use *ngIf="form.get('field')?.hasError('error') && form.get('field')?.touched".

Best Practices for Angular Forms
  • Choose the Right Form Type: Use template-driven for simple forms, reactive for complex ones.
  • Validate Early: Show errors only after touched to avoid overwhelming users.
  • Use Custom Validators: Encapsulate complex rules in reusable validators.
  • Secure Forms: Sanitize inputs and use server-side validation for critical data.
  • Test Forms: Write unit tests for form logic using Jasmine.


0 comments:

Featured Post

Master Angular 20 Basics: A Complete Beginner’s Guide with Examples and Best Practices

Welcome to the complete Angular 20 learning roadmap ! This series takes you step by step from basics to intermediate concepts , with hands...

Subscribe

 
Toggle Footer
Top