Angular Form
Angular Form Validation
Table of Contents
- Angular Forms Overview
- Key concept of Demonstrated
- Potential Interview Question
- Route concupt
- Best practices
- Common Pitfalls
1. Angular Forms Overview
Template-Driven Forms
Key Features
- Use
FormsModuleandngModeldirective - Two-way data binding with
[(ngModel)] - Validation attributes in HTML (required, minLength)
- From state traking :
touched,valid,invalid,dirty
Example:
<input [(ngModel)]="username" required minLength="3">
When to Use:
- Simple forms with basic validation
- Quick prototypes
- Forms that mirror data model directly
Reactive Forms
Key Feature:
- Use
ReactiveFormsModule,FormGroup,FormCntrol - Programtic control and validation
- Custom validators:
noSpeceValidetorsandabusiveWordValidetors - Better testability and scalability
Example:
this.login = new FormGroup({
name: new FormCntrol('', [Validators.required, noSpaceValidators]),
email: new FormControl('', [Validators.required, Validators.email]),
password: new FormCntrol('', [Validators.required, Validators.minLength(6)])
});
When to Use:
- Complex forms with dynamic validation
- Cross-field validation
- Better control over from state
- Easier unit testing
Form Arrays
Key features:
- Dynamic form controls (add/ remove skills)
- Use
FormArrayfor collections - Allows user to add/ remove from fields dynamically
Example:
get skills(): FormArray {
return this,registrationForm.get('skills') as FromArray;
}
addSkill(){
this.skills.push(new FormControl(''));
}
removeSkill(index: number){
this.skills.removeAt(index);
}
2. Key Concept of Demonstrated
Data Binding
- Interpolation:
{{val}}- One-way binding from component to view
- Used for displaying component properties
- Property Binding:
[value]="val"- One-way binding from component to DOM perporty
- Update DOM when component perporty changes
- Event Binding:
(click)="method()"- One-way binding form view to component
- Responds to user events
- Two-Way Binding:
[(ngModel)]="val"- Combines property and event binding
- Synchronize view and component
Form Validation
Built-in Validators
Validators.required // Field must have value
Validators.email // Must be valid email format
Validators.minLength(6) // Minimum length requirement
Validators.maxLength(6) // Maximum length requirement
Validators.pattern() // Regex pattern matching
Custom Validetors
No Space Validators
export function noSpaceValidators( control: AbstractControl): ValidationError | null {
if(control.value !=null && control.value.indexof(' ')!= -1){
return {noSpace: true};
}
retuen null;
}
Abusive Word Validator
export function abusiveWordValidators( control: AbstractControl): ValidationError | null {
const abusiveWords = ['fool', 'idiot', 'dumb'];
const controlValue = (control.value as string).toLowerCase();
const ContainsAbusiveWord = abusiveWords.some(word =>
controlValue.inclueds(word)
);
return ContainsAbusiveWord ? {abusiveWord: true}: null;
}
Validation States
touched/untouched: has user interacted with the field?dirty/pristine: has the value changed from initial state?valid/invalid: Does it pass all validetors ?
CSS Classes for Validation
Angular automatically adds CSS classes based on validation state:
/*Vaild required fileds -green border */
.ng-valid[required] {
border-left: 5px solid #42A948;
}
/*Invalid fields - red border */
.ng-invalid:not(form){
border-left: 5px solid #a94442;
}
/*Touched fields */
.ng-touched { }
/* Dirty field */
.ng-Dirty { }
3. Potential Interview Questions
Basic level interview Questions
Q1: What's the difference between Template-Driven and Reactive Forms?
Answer:
| Aspect | Template-Driven | Reactive |
|---|---|---|
| Setup | FormsModule | ReactiveFormsModule |
| Data Model | Implicit (created by directives) | Explicit (created in component) |
| Data Flow | Asynchronous | Synchronous |
| Form Validation | Directive | Functions in component |
| Mutability | Muteble | Immutable |
| Scalablility | Less scalable | More scalable |
| Testing | Difficult | Easy |
When to use Template-Driven: Simple forms, rapid prototyping, straightforword validation
When to use Reactive: Complex validation logic, dynamic forms , better testability
Q2: Explain form control states in Angular?
Angular:
// Touched vs Untouched
touched: boolean // User has visited the field (focused and blurred)
untouched: boolean // User has't interacted with the field yet
// Dirty vs Pristien
dirty: boolean // Value has change form initial state
Pristine: boolean //Value has't change from initial state
// Valid vs Invalid
valid: boolean // Passes all validation rules
invalid: boolean // Fails one or more validation rules
Best Practices: Check both invalid and touched before showing errors:
@if(name.invalid && name.touched){
<div class="alert alert-danger">
Name is requied
</div>
}
Q3: How do you access form control values in Reactive Forms?
Answer:
//Method 1: Using get() method
this.login.get('name')?.value;
// Method 2: Using controls property
this.login.controls.name.value;
// Method 3: Using getter (Recommended)
get name(){
return this.login.controls.name;
}
// In template
@if(name.invalid && name.touched) { }
Intermediate Level Questions
Q4: How do you create and use coustom validators?
Answer:
Step 1: Create the validator function
export function abusiveWordValidator (control: AbstractContorl): ValidationErrors | null {
const abusiveWords = ['fool', 'idot', 'dunb'];
const controlValue = (control.value as string).toLowerCase();
const containsAbusivewords = abusiveWords.some(word => controlValue.includes(word));
return containsAbusivewords ? {abusiveword: true }: null;
}
Step 2: Apply to FormControl
this.commentForm = new FormGroup({
comments: new FormControl('', [abusiveWordValidator])
});
Step 3: Display errors in template
@if(comments.invalid && comments.touched){
<div class= "alert alert-danger">
@if(comments.error?.['abusiveword']){
Comments contain abusive word. Please remove them.
}
</div>
}
Q5: How do you work with FormArray for dynamic form fields?
Answer:
Component Code:
registrationForm = new FormGroup({
skills: new FormArray([])
});
get skills(): FormArray {
return this.registrationForm.get('skills') as FormArray;
}
addSkill(){
this.skills.push(new FormControl(''));
}
removeSkill(index: number){
this.skills.removeAt(index);
}
Template Code:
<div formArrayName="skills">
@for(skill of skills.controls; track $index){
<div>
<input [formControlName]="$index">
<button (click)="removeSkill($index)"> Remove </button>
</div>
}
</div>
<button (click)="addSkill()">Add Skill </button>
Q6: How do you display validation errrors conditionally?
Answer:
Using angular 17+ Control Flow (@if)
@if(name.invalid && name.touched){
<div class="alert alert-denger mt-2">
@if(name.error?.['required']){
Name is required
}
@if(name.hasError('noSpace')){
Name can not contain spaces!
}
@if(name.error?.['minlength']){
Password must be at least
{{name.error?.['minlength'].requiredLength }} characters long
}
</div>
}
Key Points:
- Check
invalid && touchedto avoid showing error immediatly - Use optional chaining
error?.['errorKey']to prevent null errors - Use
hasError()menthod for cleaner syntex
Q7: What is the difference between errors?.['required'] ans hasError('required')?
Answer:
Both check for validation errors, but with sutable differences:
// Method 1: Using errors property with optional chaining
@if(name.error?.['required']) { }
// Method 2: Using hasError method
@if(name.hasError('required')) { }
Differences:
hasError()is a method that safely checks without throughing errorserror?.[]required optional chaining to prevent null reference errorshasError()is more readable and recommendederror?.[]is useful when you need the error object value
Advance Level Questions
Q 8: What is FormBuilder and why use it?
Answer:
FormBuilder provides syntactic sugar for creating from controls:
Without FormBuilder:
this.login = new FormGroup({
name: new FormControl('', [Validators.required]),
email: new FormControl('', [Validators.required, Validators.email]),
password: new FormControl('', [Validators.required, Validetors.minLength(6)]),
});
With FormBuilder
constructor( private fb: FormBuilder) {}
this.login = this.fb.group({
name: ['', [Validators.required]],
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(6)]]
});
Bennifits:
- More concise syntex
- Less Boilerplate code
- Easier to read and maintain
- Same functionallity as manual approch
Q9: How do you handle form submission and validation?
Answer:
// Component
submitted = false;
onSubmit(){
if(this.login.valid){
console.log(this.login.value);
this.submitted = true;
// Send data to API
} else {
// Mark all fileds as touched to show errors
this.login.markAllAsTouched();
}
}
<!-- Template -->
<form [formGroup]="login" (ngSubmit)="onSubmit()">
<!-- form fields -->
<button [disabled]="login.invalid" type="submit">Submit</button>
</form>
@if(submitted){
<div class="alert alert-success">
Form submitted successfully!
{{login.value | json }}
</div>
}
Best Practices:
- Disable submit button when form is invalid
- Call
markAllTouched()to show all errors on submit attemp - Show success message after successful submission
- Reset form if needed:
this.login.reset()
10. Explain async validators and when to use them
Answer:
Async validators return Promise or Observable and are used for server-side valication:
// Custom async validator
function usernameAvailableValidator(userService: Userservice){
return (control: AbstractControl): Observable<ValidationErrors | null> => {
return userService.checkUserName(control.value).pipe(
map(available => available ? null : {usernameTaken: true }),
catchError(() => of(null))
);
};
}
// Usage
this.form = new FormGroup({
username: new FormControl('', [Validators.required], // Sync validators
[usernameAvailableValidator(this.userService)] //Async validators
)
});
When to Uage:
- Check if username/email already exists
- Validate data against database
- External API validation
- Any validation required server call
Important: Async validators run AFTER sync validators pass
11: What are standalone components and their benefits?
Answer:
Your components use standalone components (Angular 14+):
@Component ({
selector: 'app-reactive-form',
standalone: true,
imports: [ReactiveFormsModule, JsonPipe, RouterModule, CommonModule],
templateUrl: './reactive-form.component.html',
styleUrls: ['./reactive-form.component.css']
})
Benefite: -✅ No need for NgModule -✅ Explicit dependencies (imports array) -✅ Better tree-shaking (smaller bundle size) -✅ Easier to understand component dependencies -✅ Simpler testing setup -✅ Future-proof (Angular's direction)
Migration: Can coexist with NgModule-based components
Q12: How do you implement cross-field validation?
Answer:
Validate multiple fields together:
function passwordMatchValidators (control: AbstractControl): Validators | null {
const password = control.get('password');
const confirmPassword = control.get('confirmPassword');
if(!password ||!confirmPassword) return null;
return password.value === confirmPassword.value ? null : {passwordMismatch: true };
}
// Apply to FormGroup
this.form = new FormGroup({
password: new FormControl(''),
confirmPassword: new FormControl('')
}, {validators: passwordMismatchValidator });
// Check in template
@if(form.error?.['passwordMismatch'] && form.touched){
<div>Password do not match </div>
}
Bast Practices
1. Sepration of Concerns
✅ Good: Validators in seperate files
validators/
|-- abusiveWordValidators.ts
|-- nospaceValidators.ts
❌ Bad: All logic in component file
2. Type Safe
✅ Good: Using Typescript interfaces
interface Country {
code: string;
name: string;
}
countries: Country[] = [{code: 'US', name: 'United states'}, {code:'IN', name: 'India'}];
3. Accessibility
✅ Good: Proper label-input associations
<label for="email" class="from-label">Email:</label>
<input id="email" class="form-control" formControlName="email">
❌ Bad:No label or incorrect association
4. User Experince
✅ Good: Show errors only after user intraction
@if(name.invalid && name.touched){
<div class="alert alert-danger">Error message</div>
}
❌ Bad: Show errors immediately on page load
5. Component structure
✅ Good: Use getters for form controls
get name(){
return this.login.controls.name;
}
Benefits:
- Cleaner template code
- Type Safty
- Easier to refactor
6. Form Reset
✅ Good: Proper reset handling
resetForm() {
this.login.reset();
this.submitted= false;
}
7. Responsive Design
✅ Good: Bootstrap classes for responsiveness
<div class="container col-8">
<div class="mb-3">
<input class="form-control">
</div>
</div>
Quick reference Cheat sheet
Form Control Properties
control.value //current value
control.valid // is valid?
control.invalid // is invalid?
control.touched //User interacted?
control.untouched // Not interacted yet?
control.dirty // Value changed?
control.pristine // value not change?
control.errors // validation error object
control.hasError('key') // Check specific error
Form control methods
control.setValue(value) //set value
control.patchValue(value) // partial update
control.reset() // reset to intial
control.markAsTouched() //Mask as touched
control.updateValueAndUpdate() // Recalculate validation
Comments
Post a Comment