基于 SOC 职业分类
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/ffsshhttiikk/opencode-agents-skills --skill angular命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
| name | angular |
| description | TypeScript-based web application framework by Google |
| category | web-development |
| difficulty | advanced |
| tags | ["frontend","typescript","framework","enterprise"] |
| author | |
| version | 17 |
| last_updated | 2024-01-10T00:00:00.000Z |
I am Angular, a platform and framework for building single-page client applications using HTML and TypeScript. Developed and maintained by Google, I provide a comprehensive solution for enterprise-scale web application development. My architecture is built around component-based design, where applications are composed of reusable, nested components with well-defined inputs and outputs. I leverage TypeScript for type safety, enabling better tooling, autocompletion, and compile-time error detection. My dependency injection system promotes loose coupling and testability, while my RxJS integration provides powerful reactive programming capabilities. I include a complete routing solution, form handling (both reactive and template-driven), HTTP client for API communication, and testing utilities out of the box. My opinionated structure and strong conventions make me ideal for large teams building maintainable, scalable applications. The latest versions introduce standalone components, signals for fine-grained reactivity, and server-side rendering capabilities.
Components: The fundamental building blocks that combine a TypeScript class with an HTML template and CSS styles, decorated with @Component().
Dependency Injection: A design pattern where services are provided to components rather than components creating them, enabling loose coupling and testability.
Modules: NgModules organize application code into cohesive feature sets with NgModule() decorators that declare components, services, and dependencies.
Standalone Components: Modern Angular approach eliminating the need for NgModules, allowing direct component composition.
Signals: New reactive primitive providing fine-grained reactivity with signal(), computed(), and effect() for granular updates.
RxJS Observables: Streams of data that components can subscribe to for handling events, HTTP requests, and asynchronous operations.
Routing: Full-featured client-side router with lazy loading, guards, resolvers, and nested route support.
Change Detection: Automatic mechanism that checks bindings and updates the view when data changes, with OnPush strategy for performance.
// user-list.component.ts
import { Component, signal, computed, effect } from '@angular/core'
import { CommonModule } from '@angular/common'
import { UserService } from './user.service'
import { UserCardComponent } from './user-card.component'
@Component({
selector: 'app-user-list',
standalone: true,
imports: [CommonModule, UserCardComponent],
template: `
<div class="user-list">
<div class="header">
<h2>Users ({{ users().length }})</h2>
<button (click)="refresh()" [disabled]="loading()">
Refresh
</button>
</div>
<div *ngIf="loading()" class="loading">Loading...</div>
<div *ngIf="error()" class="error">{{ error() }}</div>
<div class="users">
<app-user-card
*ngFor="let user of filteredUsers(); trackBy: trackByUserId"
[user]="user"
(selected)="selectUser($event)">
</app-user-card>
</div>
<div *ngIf="!loading() && filteredUsers().length === 0" class="empty">
No users found
</div>
</div>
`,
styles: [`
.user-list { padding: 20px; }
.header { display: flex; justify-content: space-between; align-items: center; }
.error { color: red; padding: 10px; background: #fee; border-radius: 4px; }
.users { display: grid; gap: 16px; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }
`]
})
export class UserListComponent {
private userService = inject(UserService)
users = signal<[]>([])
searchQuery = ()
loading = ()
error = signal< | >()
filteredUsers = ( {
query = .().()
.().(
user..().(query) ||
user..().(query)
)
})
() {
( {
.()
})
}
() {
..()
..()
..().({
: ..(users),
: ..(err.),
: ..()
})
}
() {
.()
}
() {
.(, user)
}
() {
user.
}
}
// auth.service.ts
import { Injectable, inject, signal } from '@angular/core'
import { HttpClient } from '@angular/common/http'
import { Router } from '@angular/router'
import { Observable, BehaviorSubject, tap, catchError, throwError } from 'rxjs'
export interface User {
id: string
email: string
name: string
role: 'admin' | 'user'
}
export interface AuthState {
user: User | null
token: string | null
isAuthenticated: boolean
}
@Injectable({ providedIn: 'root' })
export class AuthService {
private http = inject(HttpClient)
private router = inject(Router)
state = <>({
: ,
: ,
:
})
state$ = ..()
() {
...
}
() {
...
}
(: , : ): <{: ; : }> {
..<{: ; : }>(, { email, password })
.(
( {
.(, response.)
..({
: response.,
: response.,
:
})
})
)
}
() {
.()
..({ : , : , : })
..([])
}
(): <{: }> {
..<{: }>(, {})
.(
( {
.(, response.)
..({ ....., : response. })
}),
( {
.()
( error)
})
)
}
() {
token = .()
(token) {
..<>().({
: {
..({ user, token, : })
},
: {
.()
}
})
}
}
}
// user-form.component.ts
import { Component, inject } from '@angular/core'
import { CommonModule } from '@angular/common'
import { FormBuilder, ReactiveFormsModule, Validators, AbstractControl } from '@angular/forms'
import { UserService } from './user.service'
@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" type="text" formControlName="name" />
<div *ngIf="form.get('name')?.touched && form.get('name')?.errors" class="errors">
<small *ngIf="form.get('name')?.errors?.['required']">Name is required</small>
<small *ngIf="form.get('name')?.errors?.['minlength']">Minimum 2 characters</small>
</div>
</div>
<div class="form-group">
<label for="email">Email</label>
<input id="email" type="email" formControlName="email" />
<div *ngIf="form.get('email')?.touched && form.get('email')?.errors" class="errors">
<small *ngIf="form.get('email')?.errors?.['required']">Email is required</small>
<small *ngIf="form.get('email')?.errors?.['email']">Invalid email format</small>
</div>
</div>
<div class="form-group">
<label for="password">Password</label>
<input id="password" type="password" formControlName="password" />
<div *ngIf="form.get('password')?.touched && form.get('password')?.errors" class="errors">
<small *ngIf="form.get('password')?.errors?.['required']">Password is required</small>
<small *ngIf="form.get('password')?.errors?.['minlength']">Minimum 8 characters</small>
</div>
</div>
<div class="form-group">
<label for="confirmPassword">Confirm Password</label>
<input id="confirmPassword" type="password" formControlName="confirmPassword" />
<small *ngIf="form.errors?.['passwordMismatch'] && form.get('confirmPassword')?.touched">
Passwords do not match
</small>
</div>
<div class="form-group">
<label for="role">Role</label>
<select id="role" formControlName="role">
<option value="user">User</option>
<option value="admin">Admin</option>
<option value="guest">Guest</option>
</select>
</div>
<button type="submit" [disabled]="form.invalid || submitting">
{{ submitting ? 'Saving...' : 'Save User' }}
</button>
</form>
`,
: []
})
{
fb = ()
userService = ()
submitting =
form = ..({
: [, [., .()]],
: [, [., .]],
: [, [., .()]],
: [],
: []
}, { : . })
() {
password = control.()
confirmPassword = control.()
(password?. !== confirmPassword?.) {
confirmPassword?.({ : })
{ : }
}
}
() {
(..) {
. =
{ confirmPassword, ...userData } = ..
..(userData ).({
: ..(),
: . =
})
}
}
}
// auth.interceptor.ts
import { HttpInterceptorFn } from '@angular/common/http'
import { inject } from '@angular/core'
import { AuthService } from './auth.service'
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const authService = inject(AuthService)
const token = localStorage.getItem('token')
if (token) {
const cloned = req.clone({
headers: req.headers.set('Authorization', `Bearer ${token}`)
})
return next(cloned)
}
return next(req)
}
// auth-error.interceptor.ts
export const errorInterceptor: HttpInterceptorFn = (req, next) => {
return next(req).pipe(
catchError(error => {
(error. === ) {
().()
}
( error)
})
)
}
{ , provideHttpClient, withInterceptors }
{ routes }
{ authInterceptor, errorInterceptor }
: = {
: [
(routes),
(([authInterceptor, errorInterceptor]))
]
}
// auth.guard.ts
import { inject } from '@angular/core'
import { Router, CanActivateFn } from '@angular/router'
import { AuthService } from './auth.service'
export const authGuard: CanActivateFn = (route, state) => {
const authService = inject(AuthService)
const router = inject(Router)
if (authService.isAuthenticated) {
return true
}
return router.createUrlTree(['/login'], {
queryParams: { redirect: state.url }
})
}
// role.guard.ts
export const roleGuard: CanActivateFn = (route, state) => {
const authService = inject(AuthService)
const router = inject(Router)
const requiredRole = route.data['role']
(authService.?. === requiredRole) {
}
router.([])
}
{ }
{ authGuard, roleGuard }
: = [
{
: ,
: ().( m.)
},
{
: ,
: ().( m.)
},
{
: ,
: ().( m.),
: [authGuard]
},
{
: ,
: ().( m.),
: [authGuard, roleGuard],
: { : },
: [
{ : , : () },
{ : , : () }
]
},
{ : , : }
]