بنقرة واحدة
angular-form
Generates Angular reactive forms with validation, error handling, and state management
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Generates Angular reactive forms with validation, error handling, and state management
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Generates Angular animations for smooth UI transitions and interactions
Generates Angular components with best practices and patterns
Generates an Angular dialog component extending BaseDialogComponent
Generates Angular directives for DOM manipulation and behavior enhancement
Generates Angular error boundary components for graceful error handling
Generates an Angular feature component (Standalone, OnPush)
| name | angular-form |
| description | Generates Angular reactive forms with validation, error handling, and state management |
This skill helps you generate Angular reactive forms with proper validation, error handling, and state management following Angular best practices.
import { Component, inject, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';
interface UserForm {
name: string;
email: string;
age: number;
}
@Component({
selector: 'app-user-form',
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
template: `
<form [formGroup]="userForm" (ngSubmit)="onSubmit()">
<div class="form-group">
<label for="name">Name</label>
<input
id="name"
type="text"
formControlName="name"
[class.error]="isFieldInvalid('name')"
/>
@if (isFieldInvalid('name')) {
<div class="error-message">{{ getFieldError('name') }}</div>
}
</div>
<div class="form-group">
<label for="email">Email</label>
<input
id="email"
type="email"
formControlName="email"
[class.error]="isFieldInvalid('email')"
/>
@if (isFieldInvalid('email')) {
<div class="error-message">{{ getFieldError('email') }}</div>
}
</div>
<div class="form-group">
<label for="age">Age</label>
<input id="age" type="number" formControlName="age" [class.error]="isFieldInvalid('age')" />
@if (isFieldInvalid('age')) {
<div class="error-message">{{ getFieldError('age') }}</div>
}
</div>
<button type="submit" [disabled]="userForm.invalid || isSubmitting()">
{{ isSubmitting() ? 'Submitting...' : 'Submit' }}
</button>
</form>
`,
styles: [
`
.form-group {
margin-bottom: 1rem;
}
label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
}
input {
width: 100%;
padding: 0.5rem;
border: 1px solid #ddd;
border-radius: 4px;
}
input.error {
border-color: #dc3545;
}
.error-message {
color: #dc3545;
font-size: 0.875rem;
margin-top: 0.25rem;
}
button {
padding: 0.75rem 1.5rem;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:disabled {
background: #6c757d;
cursor: not-allowed;
}
`,
],
})
export class UserFormComponent {
private readonly fb = inject(FormBuilder);
userForm: FormGroup;
isSubmitting = signal(false);
constructor() {
this.userForm = this.fb.group({
name: ['', [Validators.required, Validators.minLength(2)]],
email: ['', [Validators.required, Validators.email]],
age: [18, [Validators.required, Validators.min(18), Validators.max(120)]],
});
}
isFieldInvalid(fieldName: keyof UserForm): boolean {
const control = this.userForm.get(fieldName);
return !!(control && control.invalid && (control.dirty || control.touched));
}
getFieldError(fieldName: keyof UserForm): string {
const control = this.userForm.get(fieldName);
if (!control || !control.errors) return '';
if (control.errors['required']) return 'This field is required';
if (control.errors['email']) return 'Please enter a valid email address';
if (control.errors['minlength'])
return `Minimum length is ${control.errors['minlength'].requiredLength} characters`;
if (control.errors['min']) return `Minimum value is ${control.errors['min'].min}`;
if (control.errors['max']) return `Maximum value is ${control.errors['max'].max}`;
return 'Invalid value';
}
async onSubmit() {
if (this.userForm.valid) {
this.isSubmitting.set(true);
try {
const formValue: UserForm = this.userForm.value;
console.log('Form submitted:', formValue);
// Simulate API call
await new Promise((resolve) => setTimeout(resolve, 1000));
// Reset form on success
this.userForm.reset();
alert('Form submitted successfully!');
} catch (error) {
console.error('Form submission error:', error);
alert('An error occurred while submitting the form');
} finally {
this.isSubmitting.set(false);
}
} else {
// Mark all fields as touched to show validation errors
Object.keys(this.userForm.controls).forEach((key) => {
this.userForm.get(key)?.markAsTouched();
});
}
}
}
import { Component, inject, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule, FormBuilder, FormGroup, FormArray, Validators } from '@angular/forms';
interface Skill {
name: string;
level: 'beginner' | 'intermediate' | 'advanced';
}
interface UserProfileForm {
firstName: string;
lastName: string;
skills: Skill[];
}
@Component({
selector: 'app-user-profile-form',
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
template: `
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
<div class="form-row">
<div class="form-group">
<label>First Name</label>
<input type="text" formControlName="firstName" />
</div>
<div class="form-group">
<label>Last Name</label>
<input type="text" formControlName="lastName" />
</div>
</div>
<div class="skills-section">
<h3>Skills</h3>
<div formArrayName="skills">
@for (skill of skills.controls; track $index) {
<div [formGroupName]="$index" class="skill-item">
<input type="text" formControlName="name" placeholder="Skill name" />
<select formControlName="level">
<option value="beginner">Beginner</option>
<option value="intermediate">Intermediate</option>
<option value="advanced">Advanced</option>
</select>
<button type="button" (click)="removeSkill($index)">Remove</button>
</div>
}
</div>
<button type="button" (click)="addSkill()">Add Skill</button>
</div>
<button type="submit" [disabled]="profileForm.invalid">Save Profile</button>
</form>
`,
})
export class UserProfileFormComponent {
private readonly fb = inject(FormBuilder);
profileForm: FormGroup;
constructor() {
this.profileForm = this.fb.group({
firstName: ['', Validators.required],
lastName: ['', Validators.required],
skills: this.fb.array([]),
});
}
get skills(): FormArray {
return this.profileForm.get('skills') as FormArray;
}
createSkill(): FormGroup {
return this.fb.group({
name: ['', Validators.required],
level: ['beginner', Validators.required],
});
}
addSkill() {
this.skills.push(this.createSkill());
}
removeSkill(index: number) {
this.skills.removeAt(index);
}
onSubmit() {
if (this.profileForm.valid) {
const formValue: UserProfileForm = this.profileForm.value;
console.log('Profile saved:', formValue);
}
}
}
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
// Password strength validator
export function passwordStrengthValidator(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const value = control.value;
if (!value) return null;
const hasUpperCase = /[A-Z]/.test(value);
const hasLowerCase = /[a-z]/.test(value);
const hasNumeric = /[0-9]/.test(value);
const hasSpecial = /[#?!@$%^&*-]/.test(value);
const isLongEnough = value.length >= 8;
const passwordValid = hasUpperCase && hasLowerCase && hasNumeric && hasSpecial && isLongEnough;
return !passwordValid
? {
passwordStrength: {
hasUpperCase,
hasLowerCase,
hasNumeric,
hasSpecial,
isLongEnough,
},
}
: null;
};
}
// Confirm password validator
export function confirmPasswordValidator(passwordControlName: string): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
if (!control.parent) return null;
const password = control.parent.get(passwordControlName);
const confirmPassword = control;
if (!password || !confirmPassword) return null;
return password.value !== confirmPassword.value ? { confirmPassword: true } : null;
};
}
// Async validator example
export function uniqueEmailValidator(userService: UserService): AsyncValidatorFn {
return (control: AbstractControl): Observable<ValidationErrors | null> => {
return control.valueChanges.pipe(
debounceTime(300),
take(1),
switchMap((email) => userService.checkEmailExists(email)),
map((exists) => (exists ? { emailExists: true } : null)),
catchError(() => of(null)),
);
};
}
import { Injectable, signal, computed } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
export interface FormState<T = any> {
data: T | null;
loading: boolean;
error: string | null;
success: boolean;
}
@Injectable({
providedIn: 'root',
})
export class FormService {
private readonly fb = inject(FormBuilder);
// Generic form state management
createFormState<T>(initialData?: Partial<T>) {
return {
form: signal<FormGroup | null>(null),
state: signal<FormState<T>>({
data: initialData || null,
loading: false,
error: null,
success: false,
}),
isDirty: computed(() => this.form()?.dirty ?? false),
isValid: computed(() => this.form()?.valid ?? false),
hasErrors: computed(() => this.form()?.invalid ?? false),
};
}
// Utility methods for form handling
markFormGroupTouched(form: FormGroup) {
Object.keys(form.controls).forEach((key) => {
const control = form.get(key);
control?.markAsTouched();
});
}
getFormErrors(form: FormGroup): Record<string, string[]> {
const errors: Record<string, string[]> = {};
Object.keys(form.controls).forEach((key) => {
const control = form.get(key);
if (control?.errors) {
errors[key] = Object.keys(control.errors);
}
});
return errors;
}
resetForm(form: FormGroup, initialValue?: any) {
form.reset(initialValue);
form.markAsPristine();
form.markAsUntouched();
}
}
import { Component, inject, signal, computed, effect } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-advanced-form',
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
template: `
<div class="form-container">
<form [formGroup]="contactForm" (ngSubmit)="onSubmit()">
<!-- Form fields -->
<div class="form-actions">
<button type="button" (click)="reset()" [disabled]="!isDirty()">Reset</button>
<button type="submit" [disabled]="!isValid() || isSubmitting()">
{{ isSubmitting() ? 'Saving...' : 'Save' }}
</button>
</div>
</form>
<!-- Form status -->
<div class="form-status" *ngIf="formStatus()">
<div [ngClass]="formStatus()?.type" class="status-message">
{{ formStatus()?.message }}
</div>
</div>
</div>
`,
})
export class AdvancedFormComponent {
private readonly fb = inject(FormBuilder);
contactForm: FormGroup;
isSubmitting = signal(false);
// Computed signals for form state
isDirty = computed(() => this.contactForm?.dirty ?? false);
isValid = computed(() => this.contactForm?.valid ?? false);
formStatus = signal<{ type: 'success' | 'error' | 'info'; message: string } | null>(null);
constructor() {
this.contactForm = this.fb.group({
name: ['', [Validators.required, Validators.minLength(2)]],
email: ['', [Validators.required, Validators.email]],
message: ['', [Validators.required, Validators.maxLength(500)]],
});
// Watch form changes
effect(() => {
if (this.isDirty()) {
this.formStatus.set({ type: 'info', message: 'You have unsaved changes' });
}
});
}
async onSubmit() {
if (this.contactForm.valid) {
this.isSubmitting.set(true);
this.formStatus.set(null);
try {
const formData = this.contactForm.value;
// Submit logic here
await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate API call
this.formStatus.set({ type: 'success', message: 'Form submitted successfully!' });
this.contactForm.reset();
} catch (error) {
this.formStatus.set({ type: 'error', message: 'Failed to submit form. Please try again.' });
} finally {
this.isSubmitting.set(false);
}
}
}
reset() {
this.contactForm.reset();
this.formStatus.set(null);
}
}
import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { UserFormComponent } from './user-form.component';
describe('UserFormComponent', () => {
let component: UserFormComponent;
let fixture: ComponentFixture<UserFormComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [UserFormComponent, ReactiveFormsModule],
}).compileComponents();
fixture = TestBed.createComponent(UserFormComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should have invalid form when empty', () => {
expect(component.userForm.valid).toBeFalsy();
});
it('should validate required fields', () => {
const nameControl = component.userForm.get('name');
nameControl?.setValue('');
expect(nameControl?.valid).toBeFalsy();
expect(component.isFieldInvalid('name')).toBeTruthy();
});
it('should validate email format', () => {
const emailControl = component.userForm.get('email');
emailControl?.setValue('invalid-email');
expect(emailControl?.valid).toBeFalsy();
expect(component.getFieldError('email')).toContain('valid email');
});
it('should submit form when valid', fakeAsync(() => {
spyOn(window, 'alert');
component.userForm.setValue({
name: 'John Doe',
email: 'john@example.com',
age: 25,
});
component.onSubmit();
tick(1000); // Wait for async operation
expect(window.alert).toHaveBeenCalledWith('Form submitted successfully!');
}));
});
This form skill integrates with other Universal Skills:
Reactive forms provide robust, scalable form handling with excellent developer experience and user interactions! 📝✨