KS
Killer-Skills

angular-forms — how to use angular-forms how to use angular-forms, what is angular-forms, angular-forms alternative, angular-forms vs reactive forms, angular-forms install, angular-forms setup guide, angular signal forms tutorial, angular forms api documentation

v1.0.0
GitHub

About this Skill

Ideal for Frontend Development Agents specializing in Angular application architecture and type-safe form management. angular-forms is a feature of Angular that provides a reactive forms API for building type-safe, reactive forms with automatic two-way binding and schema-based validation.

Features

Builds type-safe, reactive forms using Angular's Signal Forms API
Provides automatic two-way binding for efficient form management
Supports schema-based validation for robust form validation
Offers reactive field state for dynamic form updates
Uses the @angular/core import for seamless integration

# Core Topics

ROU-Technology ROU-Technology
[0]
[0]
Updated: 3/7/2026

Quality Score

Top 5%
42
Excellent
Based on code quality & docs
Installation
SYS Universal Install (Auto-Detect)
Cursor IDE Windsurf IDE VS Code IDE
> npx killer-skills add ROU-Technology/ng-utils/angular-forms

Agent Capability Analysis

The angular-forms MCP Server by ROU-Technology is an open-source Categories.community integration for Claude and other AI agents, enabling seamless task automation and capability expansion. Optimized for how to use angular-forms, what is angular-forms, angular-forms alternative.

Ideal Agent Persona

Ideal for Frontend Development Agents specializing in Angular application architecture and type-safe form management.

Core Value

Enables agents to construct type-safe, reactive forms with automatic two-way binding and schema-based validation using Angular's experimental Signal Forms API. This provides robust form state management and validation capabilities for complex Angular applications.

Capabilities Granted for angular-forms MCP Server

Building type-safe reactive forms with automatic binding
Implementing schema-based validation for complex forms
Creating experimental Signal Forms in Angular v21+

! Prerequisites & Limits

  • Experimental feature (Angular v21 only)
  • Not recommended for production applications requiring stability
Project
SKILL.md
10.2 KB
.cursorrules
1.2 KB
package.json
240 B
Ready
UTF-8

# Tags

[No tags]
SKILL.md
Readonly

Angular Signal Forms

Build type-safe, reactive forms using Angular's Signal Forms API. Signal Forms provide automatic two-way binding, schema-based validation, and reactive field state.

Note: Signal Forms are experimental in Angular v21. For production apps requiring stability, see references/form-patterns.md for Reactive Forms patterns.

Basic Setup

typescript
1import { Component, signal } from '@angular/core'; 2import { form, FormField, required, email } from '@angular/forms/signals'; 3 4interface LoginData { 5 email: string; 6 password: string; 7} 8 9@Component({ 10 selector: 'app-login', 11 imports: [FormField], 12 template: ` 13 <form (submit)="onSubmit($event)"> 14 <label> 15 Email 16 <input type="email" [formField]="loginForm.email" /> 17 </label> 18 @if (loginForm.email().touched() && loginForm.email().invalid()) { 19 <p class="error">{{ loginForm.email().errors()[0].message }}</p> 20 } 21 22 <label> 23 Password 24 <input type="password" [formField]="loginForm.password" /> 25 </label> 26 @if (loginForm.password().touched() && loginForm.password().invalid()) { 27 <p class="error">{{ loginForm.password().errors()[0].message }}</p> 28 } 29 30 <button type="submit" [disabled]="loginForm().invalid()">Login</button> 31 </form> 32 `, 33}) 34export class LoginComponent { 35 // Form model - a writable signal 36 loginModel = signal<LoginData>({ 37 email: '', 38 password: '', 39 }); 40 41 // Create form with validation schema 42 loginForm = form(this.loginModel, (schemaPath) => { 43 required(schemaPath.email, { message: 'Email is required' }); 44 email(schemaPath.email, { message: 'Enter a valid email address' }); 45 required(schemaPath.password, { message: 'Password is required' }); 46 }); 47 48 onSubmit(event: Event) { 49 event.preventDefault(); 50 if (this.loginForm().valid()) { 51 const credentials = this.loginModel(); 52 console.log('Submitting:', credentials); 53 } 54 } 55}

Form Models

Form models are writable signals that serve as the single source of truth:

typescript
1// Define interface for type safety 2interface UserProfile { 3 name: string; 4 email: string; 5 age: number | null; 6 preferences: { 7 newsletter: boolean; 8 theme: 'light' | 'dark'; 9 }; 10} 11 12// Create model signal with initial values 13const userModel = signal<UserProfile>({ 14 name: '', 15 email: '', 16 age: null, 17 preferences: { 18 newsletter: false, 19 theme: 'light', 20 }, 21}); 22 23// Create form from model 24const userForm = form(userModel); 25 26// Access nested fields via dot notation 27userForm.name // FieldTree<string> 28userForm.preferences.theme // FieldTree<'light' | 'dark'>

Reading Values

typescript
1// Read entire model 2const data = this.userModel(); 3 4// Read field value via field state 5const name = this.userForm.name().value(); 6const theme = this.userForm.preferences.theme().value();

Updating Values

typescript
1// Replace entire model 2this.userModel.set({ 3 name: 'Alice', 4 email: 'alice@example.com', 5 age: 30, 6 preferences: { newsletter: true, theme: 'dark' }, 7}); 8 9// Update single field 10this.userForm.name().value.set('Bob'); 11this.userForm.age().value.update(age => (age ?? 0) + 1);

Field State

Each field provides reactive signals for validation, interaction, and availability:

typescript
1const emailField = this.form.email(); 2 3// Validation state 4emailField.valid() // true if passes all validation 5emailField.invalid() // true if has validation errors 6emailField.errors() // array of error objects 7emailField.pending() // true if async validation in progress 8 9// Interaction state 10emailField.touched() // true after focus + blur 11emailField.dirty() // true after user modification 12 13// Availability state 14emailField.disabled() // true if field is disabled 15emailField.hidden() // true if field should be hidden 16emailField.readonly() // true if field is readonly 17 18// Value 19emailField.value() // current field value (signal)

Form-Level State

The form itself is also a field with aggregated state:

typescript
1// Form is valid when all interactive fields are valid 2this.form().valid() 3 4// Form is touched when any field is touched 5this.form().touched() 6 7// Form is dirty when any field is modified 8this.form().dirty()

Validation

Built-in Validators

typescript
1import { 2 form, required, email, min, max, 3 minLength, maxLength, pattern 4} from '@angular/forms/signals'; 5 6const userForm = form(this.userModel, (schemaPath) => { 7 // Required field 8 required(schemaPath.name, { message: 'Name is required' }); 9 10 // Email format 11 email(schemaPath.email, { message: 'Invalid email' }); 12 13 // Numeric range 14 min(schemaPath.age, 18, { message: 'Must be 18+' }); 15 max(schemaPath.age, 120, { message: 'Invalid age' }); 16 17 // String/array length 18 minLength(schemaPath.password, 8, { message: 'Min 8 characters' }); 19 maxLength(schemaPath.bio, 500, { message: 'Max 500 characters' }); 20 21 // Regex pattern 22 pattern(schemaPath.phone, /^\d{3}-\d{3}-\d{4}$/, { 23 message: 'Format: 555-123-4567', 24 }); 25});

Conditional Validation

typescript
1const orderForm = form(this.orderModel, (schemaPath) => { 2 required(schemaPath.promoCode, { 3 message: 'Promo code required for discounts', 4 when: ({ valueOf }) => valueOf(schemaPath.applyDiscount), 5 }); 6});

Custom Validators

typescript
1import { validate } from '@angular/forms/signals'; 2 3const signupForm = form(this.signupModel, (schemaPath) => { 4 // Custom validation logic 5 validate(schemaPath.username, ({ value }) => { 6 if (value().includes(' ')) { 7 return { kind: 'noSpaces', message: 'Username cannot contain spaces' }; 8 } 9 return null; 10 }); 11});

Cross-Field Validation

typescript
1const passwordForm = form(this.passwordModel, (schemaPath) => { 2 required(schemaPath.password); 3 required(schemaPath.confirmPassword); 4 5 // Compare fields 6 validate(schemaPath.confirmPassword, ({ value, valueOf }) => { 7 if (value() !== valueOf(schemaPath.password)) { 8 return { kind: 'mismatch', message: 'Passwords do not match' }; 9 } 10 return null; 11 }); 12});

Async Validation

typescript
1import { validateHttp } from '@angular/forms/signals'; 2 3const signupForm = form(this.signupModel, (schemaPath) => { 4 validateHttp(schemaPath.username, { 5 request: ({ value }) => `/api/check-username?u=${value()}`, 6 onSuccess: (response: { taken: boolean }) => { 7 if (response.taken) { 8 return { kind: 'taken', message: 'Username already taken' }; 9 } 10 return null; 11 }, 12 onError: () => ({ 13 kind: 'networkError', 14 message: 'Could not verify username', 15 }), 16 }); 17});

Conditional Fields

Hidden Fields

typescript
1import { hidden } from '@angular/forms/signals'; 2 3const profileForm = form(this.profileModel, (schemaPath) => { 4 hidden(schemaPath.publicUrl, ({ valueOf }) => !valueOf(schemaPath.isPublic)); 5});
html
1@if (!profileForm.publicUrl().hidden()) { 2 <input [formField]="profileForm.publicUrl" /> 3}

Disabled Fields

typescript
1import { disabled } from '@angular/forms/signals'; 2 3const orderForm = form(this.orderModel, (schemaPath) => { 4 disabled(schemaPath.couponCode, ({ valueOf }) => valueOf(schemaPath.total) < 50); 5});

Readonly Fields

typescript
1import { readonly } from '@angular/forms/signals'; 2 3const accountForm = form(this.accountModel, (schemaPath) => { 4 readonly(schemaPath.username); // Always readonly 5});

Form Submission

typescript
1import { submit } from '@angular/forms/signals'; 2 3@Component({ 4 template: ` 5 <form (submit)="onSubmit($event)"> 6 <input [formField]="form.email" /> 7 <input [formField]="form.password" /> 8 <button type="submit" [disabled]="form().invalid()">Submit</button> 9 </form> 10 `, 11}) 12export class LoginComponent { 13 model = signal({ email: '', password: '' }); 14 form = form(this.model, (schemaPath) => { 15 required(schemaPath.email); 16 required(schemaPath.password); 17 }); 18 19 onSubmit(event: Event) { 20 event.preventDefault(); 21 22 // submit() marks all fields touched and runs callback if valid 23 submit(this.form, async () => { 24 await this.authService.login(this.model()); 25 }); 26 } 27}

Arrays and Dynamic Fields

typescript
1interface Order { 2 items: Array<{ product: string; quantity: number }>; 3} 4 5@Component({ 6 template: ` 7 @for (item of orderForm.items; track $index; let i = $index) { 8 <div> 9 <input [formField]="item.product" placeholder="Product" /> 10 <input [formField]="item.quantity" type="number" /> 11 <button type="button" (click)="removeItem(i)">Remove</button> 12 </div> 13 } 14 <button type="button" (click)="addItem()">Add Item</button> 15 `, 16}) 17export class OrderComponent { 18 orderModel = signal<Order>({ 19 items: [{ product: '', quantity: 1 }], 20 }); 21 22 orderForm = form(this.orderModel, (schemaPath) => { 23 applyEach(schemaPath.items, (item) => { 24 required(item.product, { message: 'Product required' }); 25 min(item.quantity, 1, { message: 'Min quantity is 1' }); 26 }); 27 }); 28 29 addItem() { 30 this.orderModel.update(m => ({ 31 ...m, 32 items: [...m.items, { product: '', quantity: 1 }], 33 })); 34 } 35 36 removeItem(index: number) { 37 this.orderModel.update(m => ({ 38 ...m, 39 items: m.items.filter((_, i) => i !== index), 40 })); 41 } 42}

Displaying Errors

html
1<input [formField]="form.email" /> 2 3@if (form.email().touched() && form.email().invalid()) { 4 <ul class="errors"> 5 @for (error of form.email().errors(); track error) { 6 <li>{{ error.message }}</li> 7 } 8 </ul> 9} 10 11@if (form.email().pending()) { 12 <span>Validating...</span> 13}

Styling Based on State

html
1<input 2 [formField]="form.email" 3 [class.is-invalid]="form.email().touched() && form.email().invalid()" 4 [class.is-valid]="form.email().touched() && form.email().valid()" 5/>

Reset Form

typescript
1async onSubmit() { 2 if (!this.form().valid()) return; 3 4 await this.api.submit(this.model()); 5 6 // Clear interaction state 7 this.form().reset(); 8 9 // Clear values 10 this.model.set({ email: '', password: '' }); 11}

For Reactive Forms patterns (production-stable), see references/form-patterns.md.

Related Skills

Looking for an alternative to angular-forms or building a Categories.community AI Agent? Explore these related open-source MCP Servers.

View All

widget-generator

Logo of f
f

widget-generator is an open-source AI agent skill for creating widget plugins that are injected into prompt feeds on prompts.chat. It supports two rendering modes: standard prompt widgets using default PromptCard styling and custom render widgets built as full React components.

149.6k
0
Design

chat-sdk

Logo of lobehub
lobehub

chat-sdk is a unified TypeScript SDK for building chat bots across multiple platforms, providing a single interface for deploying bot logic.

73.0k
0
Communication

zustand

Logo of lobehub
lobehub

The ultimate space for work and life — to find, build, and collaborate with agent teammates that grow with you. We are taking agent harness to the next level — enabling multi-agent collaboration, effortless agent team design, and introducing agents as the unit of work interaction.

72.8k
0
Communication

data-fetching

Logo of lobehub
lobehub

The ultimate space for work and life — to find, build, and collaborate with agent teammates that grow with you. We are taking agent harness to the next level — enabling multi-agent collaboration, effortless agent team design, and introducing agents as the unit of work interaction.

72.8k
0
Communication