用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/Objective-Arts/lens-dist --skill angular-core命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
基于 SOC 职业分类
| name | angular-core |
| description | Angular patterns and testable architecture |
| allowed-tools | [] |
Miško Hevery's core belief: If it's hard to test, it's hard to use. Testability is not a feature—it's a design constraint that produces better architecture.
"The secret to writing testable code is to write code that is easy to test."
This sounds circular but isn't. Code that's easy to test has:
DI isn't just a pattern—it's the foundation of maintainable systems.
Not this:
class UserService {
private http = new HttpClient(); // Hidden dependency
private cache = new CacheService(); // Hidden dependency
getUser(id: string) {
return this.http.get(`/users/${id}`);
}
}
This:
@Injectable({ providedIn: 'root' })
class UserService {
constructor(
private http: HttpClient, // Explicit, injectable
private cache: CacheService // Explicit, injectable
) {}
getUser(id: string) {
return this.http.get(`/users/${id}`);
}
}
Why it matters:
Only inject what you directly use. Don't inject a service to get another service.
Not this:
class OrderComponent {
constructor(private userService: UserService) {}
getDiscount() {
// Reaching through userService to get pricingService
return this.userService.pricingService.calculateDiscount();
}
}
This:
class OrderComponent {
constructor(private pricingService: PricingService) {}
getDiscount() {
return this.pricingService.calculateDiscount();
}
}
Constructors are for assignment only. No logic, no calls, no initialization.
Not this:
class DashboardComponent {
data: any[];
constructor(private api: ApiService) {
this.data = this.api.fetchData(); // Logic in constructor
this.processData(); // Method call in constructor
}
}
This:
class DashboardComponent implements OnInit {
data: any[];
constructor(private api: ApiService) {} // Assignment only
ngOnInit() {
this.api.fetchData().subscribe(data => {
this.data = this.processData(data);
});
}
}
Why: Constructors run during DI resolution. Side effects there are:
Angular components should compose, not inherit.
Not this:
class BaseTableComponent {
sort() { /* ... */ }
filter() { /* ... */ }
paginate() { /* ... */ }
}
class UserTableComponent extends BaseTableComponent {
// Inherits everything, even what it doesn't need
}
This:
// Composable services
@Injectable()
class SortService { sort<T>(data: T[]) { /* ... */ } }
@Injectable()
class FilterService { filter<T>(data: T[]) { /* ... */ } }
class UserTableComponent {
constructor(
private sortService: SortService,
private filterService: FilterService
// Only inject what you need
) {}
}
Separate concerns: Smart components manage state, presentational components render.
SMART COMPONENT PRESENTATIONAL COMPONENT
────────────── ────────────────────────
Knows about services Knows only @Input/@Output
Manages state Stateless (mostly)
Handles side effects Pure rendering
Few in app Many in app
Hard to test Easy to test
Example:
// Smart component - has dependencies
@Component({
template: `<user-card [user]="user$ | async" (delete)="onDelete($event)">`
})
class UserContainerComponent {
user$ = this.store.select(selectCurrentUser);
constructor(private store: Store) {}
onDelete(userId: string) { this.store.dispatch(deleteUser({ userId })); }
}
// Presentational component - pure I/O
@Component({
template: `<div>{{user.name}}</div><button (click)="delete.emit(user.id)">Delete</button>`
})
class UserCardComponent {
@Input() user: User;
@Output() delete = new EventEmitter<string>();
}
Global state (window, localStorage, static variables) breaks testability.
Not this:
class ThemeService {
getTheme() {
return localStorage.getItem('theme'); // Global state
}
}
This:
// Abstract the global behind an injectable token
const STORAGE = new InjectionToken<Storage>('storage');
@Injectable()
class ThemeService {
constructor(@Inject(STORAGE) private storage: Storage) {}
getTheme() {
return this.storage.getItem('theme');
}
}
// In tests: provide mock storage
// In app: provide localStorage
With Angular's signals, prefer fine-grained reactivity over coarse change detection.
// Modern Angular with signals
@Component({
template: `
<div>Count: {{ count() }}</div>
<div>Doubled: {{ doubled() }}</div>
`
})
class CounterComponent {
count = signal(0);
doubled = computed(() => this.count() * 2);
increment() {
this.count.update(c => c + 1);
}
}
Benefits:
Before committing any Angular code, ask:
Apply these checks:
new keyword for services (except DTOs/models)providedIn: 'root' or explicit providers)Use a different skill when:
design-patternsjava (similar DI principles, different idioms)clarityangular-perfHevery is the Angular architecture skill—use it for DI, testability, and component design.
"The key to testability is the ability to construct the object under test in isolation." — Miško Hevery