Agent skill
angular-development
Angular development patterns including modules, components, services, dependency injection, signals, and enterprise architecture.
Install this agent skill to your Project
npx add-skill https://github.com/a5c-ai/babysitter/tree/main/library/specializations/web-development/skills/angular-development
SKILL.md
Angular Development Skill
Expert assistance for building enterprise Angular applications with modern patterns and best practices.
Capabilities
- Create Angular components with standalone API
- Implement services with dependency injection
- Configure routing with lazy loading
- Build reactive forms and template-driven forms
- Use Angular signals for reactivity
- Set up enterprise architecture patterns
Usage
Invoke this skill when you need to:
- Build Angular applications
- Create modular architecture
- Implement forms and validation
- Set up Angular routing
- Configure dependency injection
Inputs
| Parameter | Type | Required | Description |
|---|---|---|---|
| componentName | string | Yes | Component name |
| standalone | boolean | No | Use standalone components (default: true) |
| features | array | No | routing, forms, http, signals |
| style | string | No | css, scss, less |
Configuration Example
{
"componentName": "UserProfile",
"standalone": true,
"features": ["signals", "forms"],
"style": "scss"
}
Component Patterns
Standalone Component with Signals
// components/user-profile.component.ts
import { Component, signal, computed, input, output } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
interface User {
id: string;
name: string;
email: string;
}
@Component({
selector: 'app-user-profile',
standalone: true,
imports: [CommonModule, FormsModule],
template: `
<div class="user-profile">
<div class="avatar">
{{ initials() }}
</div>
@if (isEditing()) {
<input [(ngModel)]="editedName" />
<button (click)="save()">Save</button>
<button (click)="cancel()">Cancel</button>
} @else {
<h2>{{ user().name }}</h2>
<p>{{ user().email }}</p>
@if (editable()) {
<button (click)="edit()">Edit</button>
}
}
</div>
`,
styleUrl: './user-profile.component.scss',
})
export class UserProfileComponent {
// Input signals
user = input.required<User>();
editable = input(false);
// Output
updated = output<User>();
// Internal signals
isEditing = signal(false);
editedName = signal('');
// Computed
initials = computed(() => {
return this.user()
.name.split(' ')
.map((n) => n[0])
.join('')
.toUpperCase();
});
edit() {
this.editedName.set(this.user().name);
this.isEditing.set(true);
}
save() {
this.updated.emit({ ...this.user(), name: this.editedName() });
this.isEditing.set(false);
}
cancel() {
this.isEditing.set(false);
}
}
Service with Dependency Injection
// services/user.service.ts
import { Injectable, inject, signal } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { toSignal } from '@angular/core/rxjs-interop';
import { catchError, map, of } from 'rxjs';
interface User {
id: string;
name: string;
email: string;
}
@Injectable({
providedIn: 'root',
})
export class UserService {
private http = inject(HttpClient);
private apiUrl = '/api/users';
// Signal-based state
private _users = signal<User[]>([]);
private _loading = signal(false);
private _error = signal<string | null>(null);
// Public readonly signals
users = this._users.asReadonly();
loading = this._loading.asReadonly();
error = this._error.asReadonly();
async fetchUsers() {
this._loading.set(true);
this._error.set(null);
this.http
.get<User[]>(this.apiUrl)
.pipe(
catchError((err) => {
this._error.set(err.message);
return of([]);
})
)
.subscribe((users) => {
this._users.set(users);
this._loading.set(false);
});
}
async createUser(data: Omit<User, 'id'>) {
return this.http.post<User>(this.apiUrl, data).pipe(
map((user) => {
this._users.update((users) => [...users, user]);
return user;
})
);
}
async updateUser(id: string, data: Partial<User>) {
return this.http.patch<User>(`${this.apiUrl}/${id}`, data).pipe(
map((updated) => {
this._users.update((users) =>
users.map((u) => (u.id === id ? updated : u))
);
return updated;
})
);
}
}
Reactive Forms
// components/user-form.component.ts
import { Component, inject, output } from '@angular/core';
import { CommonModule } from '@angular/common';
import {
ReactiveFormsModule,
FormBuilder,
FormGroup,
Validators,
} from '@angular/forms';
@Component({
selector: 'app-user-form',
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
template: `
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<div class="form-group">
<label for="name">Name</label>
<input id="name" formControlName="name" />
@if (form.get('name')?.errors?.['required'] && form.get('name')?.touched) {
<span class="error">Name is required</span>
}
</div>
<div class="form-group">
<label for="email">Email</label>
<input id="email" type="email" formControlName="email" />
@if (form.get('email')?.errors?.['email'] && form.get('email')?.touched) {
<span class="error">Invalid email format</span>
}
</div>
<div class="form-group">
<label for="password">Password</label>
<input id="password" type="password" formControlName="password" />
@if (form.get('password')?.errors?.['minlength'] && form.get('password')?.touched) {
<span class="error">Password must be at least 8 characters</span>
}
</div>
<button type="submit" [disabled]="form.invalid">Submit</button>
</form>
`,
})
export class UserFormComponent {
private fb = inject(FormBuilder);
submitted = output<{ name: string; email: string; password: string }>();
form: FormGroup = this.fb.group({
name: ['', [Validators.required]],
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(8)]],
});
onSubmit() {
if (this.form.valid) {
this.submitted.emit(this.form.value);
}
}
}
Routing Configuration
// app.routes.ts
import { Routes } from '@angular/router';
import { authGuard } from './guards/auth.guard';
export const routes: Routes = [
{
path: '',
loadComponent: () =>
import('./pages/home/home.component').then((m) => m.HomeComponent),
},
{
path: 'login',
loadComponent: () =>
import('./pages/login/login.component').then((m) => m.LoginComponent),
},
{
path: 'dashboard',
canActivate: [authGuard],
loadComponent: () =>
import('./pages/dashboard/dashboard.component').then(
(m) => m.DashboardComponent
),
},
{
path: 'users',
canActivate: [authGuard],
loadChildren: () =>
import('./features/users/users.routes').then((m) => m.USERS_ROUTES),
},
{
path: '**',
loadComponent: () =>
import('./pages/not-found/not-found.component').then(
(m) => m.NotFoundComponent
),
},
];
// guards/auth.guard.ts
import { inject } from '@angular/core';
import { Router, type CanActivateFn } from '@angular/router';
import { AuthService } from '../services/auth.service';
export const authGuard: CanActivateFn = () => {
const auth = inject(AuthService);
const router = inject(Router);
if (auth.isAuthenticated()) {
return true;
}
return router.createUrlTree(['/login']);
};
Best Practices
- Use standalone components by default
- Leverage signals for reactive state
- Implement lazy loading for routes
- Use reactive forms for complex forms
- Apply dependency injection patterns
Target Processes
- angular-enterprise-development
- frontend-architecture-design
- form-validation-implementation
- state-management-setup
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
gsd-tools
Central utility skill for GSD operations. Provides config parsing, slug generation, timestamps, path operations, and orchestrates calls to other specialized skills. Acts as the unified entry point that the original gsd-tools.cjs provided via its lib/ modules (commands, config, core, init).
model-profile-resolution
Resolve model profile (quality/balanced/budget) at orchestration start and map agents to specific models. Enables cost/quality tradeoffs by selecting appropriate AI models for each agent role.
verification-suite
Plan structure validation, phase completeness checks, reference integrity verification, and artifact existence confirmation. Provides the structured verification layer ensuring GSD artifacts are well-formed and complete.
state-management
STATE.md reading, writing, and field-level updates. Provides cross-session state persistence via .planning/STATE.md with structured fields for current task, completed phases, blockers, decisions, and quick tasks.
git-integration
Git commit patterns, formats, and conventions for GSD methodology. Provides atomic commits per task, structured commit messages, planning file commits, branch management, and milestone tag operations.
frontmatter-parsing
YAML frontmatter parsing and manipulation for .planning/ documents. Provides read, write, update, query, and validation operations on frontmatter blocks in GSD markdown artifacts.
Didn't find tool you were looking for?