Skip to content
This repository was archived by the owner on Aug 23, 2025. It is now read-only.

Commit 9e45120

Browse files
committed
Rules Angular
1 parent d192f34 commit 9e45120

1 file changed

Lines changed: 365 additions & 0 deletions

File tree

Lines changed: 365 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,365 @@
1+
---
2+
description: APPLY Angular best practices WHEN developing a scalable web application architecture in Angular
3+
globs: src/*/*.ts, src/*/*.html,src/*/*.scss
4+
alwaysApply: false
5+
---
6+
7+
You are an expert in TypeScript, Angular, and scalable web application development. You write maintainable, performant, and accessible code following Angular and TypeScript best practices.
8+
9+
## Context
10+
- Develop a standalone Angular application using TypeScript.
11+
- Ensure clarity, readability, optimal performance, and enterprise-ready architecture.
12+
- Follow best practices from Angular, Angular Material, TypeScript, and SASS official documentation.
13+
- Implement Clean Architecture principles and modern Angular patterns.
14+
15+
## Naming Conventions
16+
- **File Names**: Use kebab-case with descriptive suffixes
17+
- `*.component.ts` for Components
18+
- `*.service.ts` for Services
19+
- `*.facade.ts` for Facade services
20+
- `*.repository.ts` for Repository services
21+
- `*.directive.ts` for Directives
22+
- `*.pipe.ts` for Pipes
23+
- `*.spec.ts` for Tests
24+
- `*.model.ts` for Interfaces/Types
25+
- **Variable Naming**:
26+
- camelCase for variables, functions, properties
27+
- PascalCase for classes, interfaces, types, enums
28+
- UPPER_SNAKE_CASE for constants
29+
- **Component Selectors**: Use consistent prefix (e.g., `app-`, `shared-`, `feature-`)
30+
31+
32+
### Code Structure Pattern
33+
```typescript
34+
// 1. Imports
35+
import { Component, inject, signal } from '@angular/core';
36+
import { MatButtonModule } from '@angular/material/button';
37+
38+
// 2. Interfaces/Types
39+
interface UserData {
40+
id: string;
41+
name: string;
42+
}
43+
44+
// 3. Component Definition
45+
@Component({
46+
selector: 'app-user-profile',
47+
standalone: true,
48+
imports: [MatButtonModule],
49+
templateUrl: './user-profile.component.html',
50+
styleUrls: ['./user-profile.component.scss'],
51+
changeDetection: ChangeDetectionStrategy.OnPush
52+
})
53+
export class UserProfileComponent {
54+
// 4. Injected Dependencies
55+
private readonly userService = inject(UserService);
56+
57+
// 5. Signals and State
58+
protected readonly user = signal<UserData | null>(null);
59+
protected readonly isLoading = signal<boolean>(false);
60+
61+
// 6. Computed Values
62+
protected readonly displayName = computed(() =>
63+
this.user()?.name ?? 'Anonymous User'
64+
);
65+
66+
// 7. Lifecycle Hooks
67+
ngOnInit(): void {
68+
this.loadUser();
69+
}
70+
71+
// 8. Public Methods
72+
public refreshUser(): void {
73+
this.loadUser();
74+
}
75+
76+
// 9. Protected Methods (Template accessible)
77+
protected onSaveClick(): void {
78+
this.saveUser();
79+
}
80+
81+
// 10. Private Methods
82+
private loadUser(): void {
83+
// Implementation
84+
}
85+
}
86+
```
87+
88+
## Angular-Specific Guidelines
89+
90+
### Best Practices
91+
- Always use standalone components over NgModules
92+
- Don't use explicit standalone: true (it is implied by default)
93+
- Use signals for state management
94+
- Implement lazy loading for feature routes
95+
- Use NgOptimizedImage for all static images.
96+
- Control Flow: Use `@if`, `@for`, `@switch` instead of structural directives
97+
- Defer Blocks: Implement lazy loading for non-critical content
98+
- View Transitions: Use Angular's view transition API
99+
- Signal-based Routing: Leverage signal inputs in routes
100+
101+
```html
102+
<!-- Modern Control Flow -->
103+
@if (user(); as currentUser) {
104+
<div class="user-profile">
105+
<h2>{{ currentUser.name }}</h2>
106+
@for (role of currentUser.roles; track role.id) {
107+
<span class="role-badge">{{ role.name }}</span>
108+
}
109+
</div>
110+
} @else {
111+
<div class="login-prompt">Please log in</div>
112+
}
113+
114+
<!-- Defer Blocks for Performance -->
115+
@defer (when shouldLoadChart) {
116+
<app-analytics-chart [data]="chartData()" />
117+
} @placeholder {
118+
<div class="chart-placeholder">Loading chart...</div>
119+
} @loading (minimum 500ms) {
120+
<mat-spinner></mat-spinner>
121+
} @error {
122+
<div class="error-message">Failed to load chart</div>
123+
}
124+
```
125+
126+
### Components
127+
- Keep components small and focused on a single responsibility
128+
- Use input() and output() functions instead of decorators
129+
- Use computed() for derived state
130+
- Set `changeDetection: ChangeDetectionStrategy.OnPush` in `@Component` decorator
131+
- Prefer inline templates for small components
132+
- Prefer Reactive forms instead of Template-driven ones
133+
- Do NOT use "ngClass" (NgClass), use "class" bindings instead
134+
- DO NOT use "ngStyle" (NgStyle), use "style" bindings instead
135+
- Always use standalone components
136+
- Separate files: Keep HTML, TypeScript, and SCSS in separate files
137+
- ChangeDetectionStrategy.OnPush: Mandatory for all components
138+
- Consistent selector prefix: Use app-wide naming convention
139+
- Input validation: Use proper typing and validation for @Input properties
140+
- Output naming: Use consistent event naming (e.g., `userSelected`, `formSubmitted`)
141+
142+
```typescript
143+
@Component({
144+
selector: 'app-user-card',
145+
imports: [MatCardModule, MatButtonModule],
146+
templateUrl: './user-card.component.html',
147+
styleUrls: ['./user-card.component.scss'],
148+
changeDetection: ChangeDetectionStrategy.OnPush
149+
})
150+
export class UserCardComponent {
151+
@Input({ required: true }) user!: User;
152+
@Output() userSelected = new EventEmitter<User>();
153+
154+
protected onSelectUser(): void {
155+
this.userSelected.emit(this.user);
156+
}
157+
}
158+
```
159+
160+
### Services and Dependency Injection
161+
- Use inject() function: Preferred over constructor injection
162+
- Proper visibility: Mark methods as public, protected, or private appropriately
163+
- Single Responsibility: One service per domain concern
164+
- Facade Pattern: Use facades for complex feature interactions
165+
- Design services around a single responsibility
166+
- Use the providedIn: 'root' option for singleton services
167+
- Use the inject() function instead of constructor injection
168+
169+
```typescript
170+
@Injectable({ providedIn: 'root' })
171+
export class UserService {
172+
private readonly http = inject(HttpClient);
173+
private readonly apiUrl = environment.apiUrl;
174+
175+
public getUsers(): Observable<User[]> {
176+
return this.http.get<User[]>(`${this.apiUrl}/users`);
177+
}
178+
179+
public getUserById(id: string): Observable<User> {
180+
return this.http.get<User>(`${this.apiUrl}/users/${id}`);
181+
}
182+
}
183+
184+
// Facade Pattern Example
185+
@Injectable({ providedIn: 'root' })
186+
export class UserFacade {
187+
private readonly userService = inject(UserService);
188+
private readonly notificationService = inject(NotificationService);
189+
190+
public readonly users = signal<User[]>([]);
191+
public readonly selectedUser = signal<User | null>(null);
192+
193+
public async loadUsers(): Promise<void> {
194+
try {
195+
const users = await firstValueFrom(this.userService.getUsers());
196+
this.users.set(users);
197+
} catch (error) {
198+
this.notificationService.showError('Failed to load users');
199+
}
200+
}
201+
}
202+
```
203+
204+
### State Management with Signals
205+
- Prefer signals: Use Angular signals for reactive state management
206+
- Signal composition: Leverage computed signals for derived state
207+
- Effect usage: Use effects sparingly, prefer computed signals
208+
- Resource API: Use for async data loading patterns
209+
- Use signals for local component state
210+
- Use computed() for derived state
211+
- Keep state transformations pure and predictable
212+
213+
214+
```typescript
215+
// Signal-based State Management
216+
export class ProductStore {
217+
// Base signals
218+
private readonly _products = signal<Product[]>([]);
219+
private readonly _selectedCategory = signal<string>('all');
220+
private readonly _isLoading = signal<boolean>(false);
221+
222+
// Read-only public signals
223+
public readonly products = this._products.asReadonly();
224+
public readonly selectedCategory = this._selectedCategory.asReadonly();
225+
public readonly isLoading = this._isLoading.asReadonly();
226+
227+
// Computed signals
228+
public readonly filteredProducts = computed(() => {
229+
const products = this._products();
230+
const category = this._selectedCategory();
231+
return category === 'all'
232+
? products
233+
: products.filter(p => p.category === category);
234+
});
235+
236+
public readonly productCount = computed(() => this.filteredProducts().length);
237+
238+
// Resource API for async data
239+
public readonly productsResource = resource({
240+
request: () => this.selectedCategory(),
241+
loader: ({ request: category }) => this.loadProductsByCategory(category)
242+
});
243+
244+
// State mutations
245+
public setProducts(products: Product[]): void {
246+
this._products.set(products);
247+
}
248+
249+
public selectCategory(category: string): void {
250+
this._selectedCategory.set(category);
251+
}
252+
}
253+
```
254+
255+
### Reactive Programming Best Practices
256+
- Async pipe: Always use async pipe in templates for Observables
257+
- Signal interop: Use `toSignal()` and `toObservable()` for RxJS integration
258+
- Subscription management: Use `takeUntilDestroyed()` for automatic cleanup
259+
- Error handling: Always handle Observable errors appropriately
260+
261+
```typescript
262+
export class DataComponent {
263+
private readonly destroyRef = inject(DestroyRef);
264+
private readonly dataService = inject(DataService);
265+
266+
// Convert Observable to Signal
267+
protected readonly data = toSignal(
268+
this.dataService.getData().pipe(
269+
catchError(error => {
270+
console.error('Data loading failed:', error);
271+
return of([]);
272+
})
273+
),
274+
{ initialValue: [] }
275+
);
276+
277+
// Manual subscription with cleanup
278+
private subscribeToUpdates(): void {
279+
this.dataService.getUpdates()
280+
.pipe(takeUntilDestroyed(this.destroyRef))
281+
.subscribe(update => this.handleUpdate(update));
282+
}
283+
}
284+
```
285+
286+
### Templates
287+
- Keep templates simple and avoid complex logic
288+
- Use native control flow (@if, @for, @switch) instead of *ngIf, *ngFor, *ngSwitch
289+
- Use the async pipe to handle observables
290+
291+
## TypeScript Best Practices
292+
- Use strict type checking
293+
- Prefer type inference when the type is obvious
294+
- Avoid the `any` type; use `unknown` when type is uncertain
295+
296+
## References
297+
298+
### Core Angular
299+
- [Angular Developer Guide](mdc:https:/angular.dev)
300+
- [Angular API Reference](mdc:https:/angular.dev/api)
301+
- [Angular CLI Documentation](mdc:https:/angular.dev/cli)
302+
- [Angular Update Guide](mdc:https:/angular.dev/update-guide)
303+
304+
### Components & Templates
305+
- [Component Overview](mdc:https:/angular.dev/guide/components)
306+
- [Template Syntax](mdc:https:/angular.dev/guide/templates)
307+
- [Control Flow](mdc:https:/angular.dev/guide/templates/control-flow)
308+
- [Defer Blocks](mdc:https:/angular.dev/guide/templates/defer)
309+
310+
### Signals & State Management
311+
- [Signals Overview](mdc:https:/angular.dev/guide/signals)
312+
- [Signal Inputs](mdc:https:/angular.dev/guide/signals#signal-inputs)
313+
- [Computed Signals](mdc:https:/angular.dev/guide/signals#computed-signals)
314+
- [Signal Effects](mdc:https:/angular.dev/guide/signals#effects)
315+
- [Resource API](mdc:https:/angular.dev/guide/signals/resource)
316+
- [RxJS Interop](mdc:https:/angular.dev/guide/rxjs-interop)
317+
318+
### Dependency Injection
319+
- [DI Overview](mdc:https:/angular.dev/guide/di)
320+
- [Injectable Services](mdc:https:/angular.dev/guide/di/creating-injectable-service)
321+
- [Injection Context](mdc:https:/angular.dev/guide/di/dependency-injection-context)
322+
- [Hierarchical Injectors](mdc:https:/angular.dev/guide/di/hierarchical-dependency-injection)
323+
324+
### HTTP & Data Loading
325+
- [HttpClient Guide](mdc:https:/angular.dev/guide/http)
326+
- [HTTP Interceptors](mdc:https:/angular.dev/guide/http/interceptors)
327+
- [HTTP Testing](mdc:https:/angular.dev/guide/http/testing)
328+
329+
### Forms
330+
- [Forms Overview](mdc:https:/angular.dev/guide/forms)
331+
- [Reactive Forms](mdc:https:/angular.dev/guide/forms/reactive-forms)
332+
- [Form Validation](mdc:https:/angular.dev/guide/forms/form-validation)
333+
- [Dynamic Forms](mdc:https:/angular.dev/guide/forms/dynamic-forms)
334+
335+
### Routing
336+
- [Router Overview](mdc:https:/angular.dev/guide/routing)
337+
- [Route Guards](mdc:https:/angular.dev/guide/routing/guards)
338+
- [Lazy Loading](mdc:https:/angular.dev/guide/routing/lazy-loading)
339+
340+
### Testing
341+
- [Testing Overview](mdc:https:/angular.dev/guide/testing)
342+
- [Component Testing](mdc:https:/angular.dev/guide/testing/components-basics)
343+
- [Service Testing](mdc:https:/angular.dev/guide/testing/services)
344+
- [E2E Testing](mdc:https:/angular.dev/guide/testing/e2e)
345+
346+
### Performance
347+
- [Performance Guide](mdc:https:/angular.dev/guide/performance)
348+
- [Bundle Optimization](mdc:https:/angular.dev/guide/performance/bundle-optimization)
349+
- [Image Optimization](mdc:https:/angular.dev/guide/image-optimization)
350+
351+
### Accessibility
352+
- [A11y Overview](mdc:https:/angular.dev/guide/accessibility)
353+
- [Angular CDK A11y](mdc:https:/material.angular.io/cdk/a11y/overview)
354+
355+
### Angular Material
356+
- [Material Design](mdc:https:/material.angular.io)
357+
- [Material Components](mdc:https:/material.angular.io/components)
358+
- [Material Theming](mdc:https:/material.angular.io/guide/theming)
359+
- [Material CDK](mdc:https:/material.angular.io/cdk)
360+
361+
### Best Practices
362+
- [Style Guide](mdc:https:/angular.dev/style-guide)
363+
- [Security Guide](mdc:https:/angular.dev/guide/security)
364+
- [Performance Best Practices](mdc:https:/angular.dev/guide/performance)
365+
- [Accessibility Best Practices](mdc:https:/angular.dev/guide/accessibility)

0 commit comments

Comments
 (0)