| name | Angular Conventions |
| description | Coding standards, architectural rules, and project-specific conventions for the Angular application. |
Angular 21 Best Practices & Senior Developer Guidelines
To ensure high-quality, maintainable, and modern code, adhere to the following guidelines. These are non-negotiable for new feature development.
1. Modern Reactivity: Signals First
Angular 21 relies heavily on Signals for fine-grained reactivity.
-
Inputs: Use input() and input.required() instead of @Input().
readonly userId = input.required<string>();
readonly options = input<Option[]>([])
@Input() userId!: string;
-
Queries: Use viewChild(), contentChildren(), etc.
readonly submitBtn = viewChild<ElementRef>('submit');
-
Outputs: Use output() instead of @Output().
readonly validated = output<boolean>();
-
State: Use signal() and computed() for local component state.
readonly count = signal(0);
readonly doubleCount = computed(() => this.count() * 2);
2. Control Flow Syntax
Always use the built-in control flow syntax. It is more readable and performant than *ngIf and *ngFor.
@if (user(); as u) {
<user-card [user]="u" />
} @else {
<login-prompt />
} @for (item of items(); track item.id) {
<list-item [item]="item" />
} @empty {
<li>No items found</li>
}
3. RxJS Interop
While Signals are preferred for synchronous state, RxJS is still the standard for asynchronous streams (HTTP, WebSockets).
- Use
toSignal to consume Observables in templates synchronously.
readonly users = toSignal(this.userService.getUsers(), { initialValue: [] });
- Use
toObservable when you need to switch from a Signal to a Stream (e.g., for debounce).
toObservable(this.searchTerm)
.pipe(debounceTime(300))
.subscribe(...)
4. Component Architecture
- File Structure: Always use separate files for logic (
.ts), template (.html), and styles (.scss). NEVER use inline template or styles.
- Imports: explicity import only what you need.
- Change Detection: ALWAYS use
ChangeDetectionStrategy.OnPush.
@Component({
selector: 'app-feature',
imports: [CommonModule, MatButtonModule],
templateUrl: './feature.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
- Smart vs. Dumb:
- Smart (Container): Talks to Services/Store, handles data fetching, passes data down.
- Dumb (Presentational): Receives data via
input(), emits events via output(). No dependency injection of Services.
5. Performance Optimization
6. Strict Typing
- No
any: explicit types or generalized generics are required.
- Strict Null Checks: Handle
null / undefined explicitly.
- Zod: Use Zod for runtime validation of external data (API responses, JSON files).
7. Styles
- Tailwind First: Use utility classes for layout (flex, grid), spacing (m-, p-), and typography.
- Component Styles: Use SCSS files only for complex, component-specific styles that are hard to express with Tailwind.
8. Internationalization (i18n)
The app is translated into over 20 languages. NEVER hardcode user-facing text in templates or components, EXCEPT for files within the apps/picsa-apps/dashboard application, which does not require strict translation enforcement.
9. Angular Material & UI Components
This project uses Angular Material 21 with specific design patterns. Standard legacy Angular Material directives (like mat-button) have been superseded or abstracted in this project.
- Buttons: ALWAYS use the
matButton directive with its specific variants (e.g., <button matButton="filled" color="primary">).
- CRITICAL: Do NOT use or recommend legacy directives like
mat-button, mat-raised-button, etc.
- CRITICAL: Do NOT flag
matButton as a "duplicate attribute" or "incorrect directive selector". In this architecture, matButton is the strictly correct and expected directive selector.