Core Concepts

3 min read
Rapid overview

Angular Core Concepts

Focus on standalone components, strong typing, and predictable data flow. Build on TypeScript fundamentals before applying these patterns.

Learning Path (60-90 min)

StepFocusOutcome
1Components + templatesEstablish standalone patterns, inputs/outputs, and template clarity.
2Data flow + DIApply OnPush, service boundaries, and injection scopes.
3Routing + formsImplement lazy routes, guards/resolvers, and reactive forms.
4HTTP + RxJSCompose streams, handle errors, and cancel safely.
5State + testingChoose state patterns and validate behavior with tests.
6Performance + architectureTune rendering and align tooling/architecture choices.

Next up: Angular Fundamentals Exercises

Application structure

  • Standalone components first: Prefer standalone components over NgModules for most features; use feature modules only when integration requires them.
  • Feature slicing: Group by feature/domain (routes, components, services, models) to keep boundaries clear.
  • Environment config: Keep runtime config separate from code; avoid leaking secrets.

Components and templates

  • Inputs/outputs: Keep @Input() props simple and typed; emit specific payloads via @Output() EventEmitters.
  • Change detection: Default ChangeDetectionStrategy.OnPush for predictable rendering; push new references instead of mutating objects/arrays.
  • Pipes and directives: Use pure pipes for presentation transformations; write structural directives sparingly for control flow or access control.

Dependency injection

  • Providers: Prefer providedIn: 'root' for shared services; scope to components for per-instance state. Use injection tokens for primitives/config.
  • Hierarchies: Understand how child injectors override parent providers; leverage this for feature-specific behavior.

Routing

  • Lazy loading: Use loadChildren with standalone route definitions to keep bundles small.
  • Guards/resolvers: Authenticate/authorize in guards; fetch required data in resolvers and surface via ActivatedRoute.
  • Preloading: Apply selective preloading for high-traffic routes; avoid over-preloading on low-bandwidth environments.

Forms

  • Reactive forms: Prefer FormControl, FormGroup, and validators for testable, composable forms.
  • Validation: Combine built-in and custom validators; provide user-friendly error messages and accessibility cues.
  • State sync: Keep form state as source of truth; derive UI state (dirty/touched/errors) from controls.

HttpClient and data flow

  • Observables: Keep HTTP calls typed; use shareReplay(1) for cacheable streams.
  • Error handling: Centralize interceptors for auth/refresh/logging; return typed error objects.
  • Cancellation: Use takeUntil(destroy$) in components to dispose subscriptions.

RxJS patterns

  • Data streams: Compose with switchMap for request switching, mergeMap for fan-out, concatMap for ordered processing.
  • State: Use BehaviorSubjects or state services for shared state; expose readonly Observables to consumers.
  • Backoff: Implement retry with backoff for transient failures; avoid infinite retries.

State management

  • NgRx Store: Centralized state with reducers, actions, and effects for large apps.
  • Component Store: Localized state scoped to a feature or component tree.
  • Signals vs Observables: Prefer signals for synchronous UI state, observables for async streams.

Testing

  • TestBed: Configure minimal TestBed per spec; mock providers and HttpClient.
  • Harnesses: Use Angular component harnesses to interact with DOM in tests.
  • Async testing: Leverage fakeAsync/tick or waitForAsync depending on the scenario; prefer deterministic, fast tests.
  • Jasmine/Karma: Default unit testing stack for many Angular apps.
  • Playwright: End-to-end testing for critical user flows.

Accessibility and styling

  • Semantic HTML: Use proper elements for structure and screen readers.
  • Keyboard support: Ensure tab order and focus states are visible.
  • ARIA sparingly: Add roles/labels only when semantics are missing.
  • SCSS structure: Keep global.scss for shared styles and component SCSS for local scope.

Tooling and architecture

  • NX and monorepos: Use affected builds, clear project boundaries, and shared libraries.
  • Micro frontends: Align route shells and shared UI packages with ownership boundaries.

Offline and storage

  • Service workers: Cache assets and API responses for offline-friendly UX.
  • IndexedDB: Store structured data for offline queues or large datasets.

Performance

  • Rendering: Combine OnPush with trackBy in *ngFor to reduce churn.
  • Change detection triggers: Avoid heavy synchronous work in lifecycle hooks; push work to RxJS pipelines.
  • Bundling: Use budgets, lazy routes, and component-level code splitting where applicable.

Questions & Answers

Q: How does Zone.js work?

A: Zone.js patches async APIs (Promises, timers, DOM events, XHR) to track pending microtasks and macrotasks. Angular's NgZone listens for the task queue to stabilize and then runs change detection; run heavy work outside the zone and re-enter for UI updates.

Q: What is the difference between signals and observables?

A: Signals are synchronous, dependency-tracked values for local UI state. Observables are asynchronous, push-based streams that require subscriptions (or async pipe) and are better for events, HTTP, and multi-value flows.