Install
$ agentstack add skill-terminalskills-skills-angular ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Angular
Angular is an opinionated, full-featured frontend framework. It uses TypeScript, components with templates, dependency injection, RxJS for async data, and a CLI for scaffolding.
Installation
# Create new Angular project
npm i -g @angular/cli
ng new my-app --routing --style=scss
cd my-app
ng serve
Project Structure
# Angular project layout
src/app/
├── app.component.ts # Root component
├── app.config.ts # Application config
├── app.routes.ts # Route definitions
├── articles/
│ ├── article-list/ # List component
│ ├── article-detail/ # Detail component
│ ├── article.service.ts # Data service
│ └── article.model.ts # Interface/type
├── auth/
│ ├── auth.service.ts
│ ├── auth.guard.ts
│ └── auth.interceptor.ts
└── shared/
├── components/
└── pipes/
Components (Standalone)
// src/app/articles/article-list/article-list.component.ts — standalone component
import { Component, inject, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterLink } from '@angular/router';
import { ArticleService } from '../article.service';
import { Article } from '../article.model';
@Component({
selector: 'app-article-list',
standalone: true,
imports: [CommonModule, RouterLink],
template: `
Articles
@for (article of articles; track article.id) {
{{ article.title }}
{{ article.excerpt }}
} @empty {
No articles found.
}
`,
})
export class ArticleListComponent implements OnInit {
private articleService = inject(ArticleService);
articles: Article[] = [];
ngOnInit() {
this.articleService.getAll().subscribe((data) => (this.articles = data));
}
}
Signals (Modern Reactivity)
// src/app/articles/article-list/article-list.component.ts — signals-based component
import { Component, signal, computed, inject, OnInit } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { ArticleService } from '../article.service';
@Component({
selector: 'app-article-list',
standalone: true,
template: `
{{ filteredCount() }} articles found
@for (article of filtered(); track article.id) {
{{ article.title }}
}
`,
})
export class ArticleListComponent {
private svc = inject(ArticleService);
articles = toSignal(this.svc.getAll(), { initialValue: [] });
search = signal('');
filtered = computed(() =>
this.articles().filter((a) => a.title.toLowerCase().includes(this.search().toLowerCase()))
);
filteredCount = computed(() => this.filtered().length);
}
Services
// src/app/articles/article.service.ts — injectable data service
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Article } from './article.model';
@Injectable({ providedIn: 'root' })
export class ArticleService {
private http = inject(HttpClient);
private baseUrl = '/api/articles';
getAll(): Observable {
return this.http.get(this.baseUrl);
}
getBySlug(slug: string): Observable {
return this.http.get(`${this.baseUrl}/${slug}`);
}
create(article: Partial): Observable {
return this.http.post(this.baseUrl, article);
}
}
Routing
// src/app/app.routes.ts — application routes
import { Routes } from '@angular/router';
import { authGuard } from './auth/auth.guard';
export const routes: Routes = [
{ path: '', loadComponent: () => import('./home/home.component').then(m => m.HomeComponent) },
{ path: 'articles', loadComponent: () => import('./articles/article-list/article-list.component').then(m => m.ArticleListComponent) },
{ path: 'articles/:slug', loadComponent: () => import('./articles/article-detail/article-detail.component').then(m => m.ArticleDetailComponent) },
{ path: 'admin', loadComponent: () => import('./admin/admin.component').then(m => m.AdminComponent), canActivate: [authGuard] },
{ path: '**', redirectTo: '' },
];
Guards and Interceptors
// src/app/auth/auth.guard.ts — functional route guard
import { inject } from '@angular/core';
import { Router, CanActivateFn } from '@angular/router';
import { AuthService } from './auth.service';
export const authGuard: CanActivateFn = () => {
const auth = inject(AuthService);
const router = inject(Router);
return auth.isLoggedIn() ? true : router.createUrlTree(['/login']);
};
// src/app/auth/auth.interceptor.ts — HTTP interceptor
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { AuthService } from './auth.service';
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const token = inject(AuthService).getToken();
if (token) {
req = req.clone({ setHeaders: { Authorization: `Bearer ${token}` } });
}
return next(req);
};
Reactive Forms
// src/app/articles/article-form/article-form.component.ts — reactive form
import { Component, inject } from '@angular/core';
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
import { ArticleService } from '../article.service';
import { Router } from '@angular/router';
@Component({
selector: 'app-article-form',
standalone: true,
imports: [ReactiveFormsModule],
template: `
Create
`,
})
export class ArticleFormComponent {
private fb = inject(FormBuilder);
private svc = inject(ArticleService);
private router = inject(Router);
form = this.fb.nonNullable.group({
title: ['', [Validators.required, Validators.maxLength(200)]],
body: ['', Validators.required],
});
submit() {
if (this.form.valid) {
this.svc.create(this.form.getRawValue()).subscribe(() => this.router.navigate(['/articles']));
}
}
}
Application Config
// src/app/app.config.ts — application configuration
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { routes } from './app.routes';
import { authInterceptor } from './auth/auth.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideHttpClient(withInterceptors([authInterceptor])),
],
};
Key Patterns
- Use standalone components (default in Angular 17+) — no NgModules needed
- Use
inject()function instead of constructor injection for cleaner code - Use signals for synchronous state, RxJS for async streams and HTTP
- Lazy-load routes with
loadComponentfor smaller initial bundles - Use functional guards and interceptors (simpler than class-based)
- Use
@for/@if/@switchcontrol flow syntax (Angular 17+) instead of*ngFor/*ngIf
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: TerminalSkills
- Source: TerminalSkills/skills
- License: Apache-2.0
- Homepage: https://terminalskills.io/
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.