askesis

Angular essentials: 50 cards on signals, components and RxJS

Modern Angular: standalone components, signals, inputs and outputs, dependency injection, RxJS basics, @if and @for, routing and OnPush change detection.

Web developmentBeginner50 cardsFree

Practice now

Study without an account

0 of 50 seen0 graduated
What is an Angular component?

Scheduled with the default Askesis timings. Progress is saved in this browser.

All 50 cards

  1. 1
    What is an Angular component?
    A TypeScript class with the @Component decorator that ties together a CSS selector, a template and styles
    ts
    @Component({
      selector: "app-hello",
      template: `<h1>Hello {{ name }}</h1>`,
    })
    export class Hello {
      name = "Ada"
    }
  2. 2
    What is a standalone component?
    A component that lists its own dependencies in imports instead of being declared in an NgModule
    New projects use them since Angular 17, and since v19 standalone: true is the default, so you no longer write it.
  3. 3
    Bootstrap a standalone Angular app
    ts
    bootstrapApplication(App, {
      providers: [provideRouter(routes), provideHttpClient()],
    })
  4. 4
    Template syntax: {{ }}, [ ] and ( )
    {{ total }} interpolates text; [disabled]="busy" binds a property; (click)="save()" listens to an event
  5. 5
    What is [(ngModel)]?
    Two-way binding (“banana in a box”): a property binding plus an event binding in one. ngModel needs FormsModule in the component’s imports
  6. 6
    Show content conditionally with built-in control flow
    html
    @if (user) {
      <p>Hi {{ user.name }}</p>
    } @else {
      <button (click)="login()">Log in</button>
    }
    Built-in control flow arrived in Angular 17 (stable in 18) and replaces *ngIf.
  7. 7
    Render a list with @for
    html
    @for (item of items; track item.id) {
      <li>{{ item.name }}</li>
    } @empty {
      <li>No items yet</li>
    }
  8. 8
    Why does @for require track?
    It identifies each item so Angular can keep and move existing DOM nodes when the list changes instead of re-creating them. Track a unique ID
    track $index is only fine for static lists.
  9. 9
    Implicit variables available inside @for
    $index, $first, $last, $even, $odd and $count
  10. 10
    What does @defer do?
    Lazy-loads the components, directives and pipes inside the block in a separate chunk when a trigger fires, like on viewport or on interaction
    The default trigger is on idle. Optional @placeholder, @loading and @error blocks show other states.
  11. 11
    What does @let do in a template?
    Declares a read-only local variable: @let total = price() * qty(); (Angular 18.1+)
  12. 12
    Create, read and update a signal
    ts
    count = signal(0)
    
    this.count()                 // read: call it
    this.count.set(5)
    this.count.update((c) => c + 1)
    Reading a signal in a template or a computed registers it as a dependency.
  13. 13
    What does computed() create?
    A read-only signal derived from other signals; it is evaluated lazily, memoized and recalculated only when a dependency changes
    ts
    fullName = computed(() => `${this.first()} ${this.last()}`)
  14. 14
    What is effect() for?
    Running side effects, like logging or syncing to localStorage, whenever the signals it reads change; it always runs at least once
    Don’t use effects to copy one signal into another; use computed or linkedSignal.
  15. 15
    Why doesn’t the template update?
    ts
    items = signal<string[]>([])
    add(x: string) { this.items().push(x) }
    Mutating the array in place doesn’t notify the signal. Set a new value: this.items.update((list) => [...list, x])
  16. 16
    Declare signal inputs
    ts
    name = input<string>()            // optional
    userId = input.required<number>() // must be bound
    The parent binds <app-user [userId]="id" /> and the child reads this.userId(). The older @Input() decorator still works.
  17. 17
    Emit an event from a child with output()
    ts
    saved = output<User>()
    
    this.saved.emit(user)
    The parent listens with (saved)="onSaved($event)".
  18. 18
    What does model() create?
    A writable signal input that supports two-way binding: the child calls value.set(…) and the parent binds with [(value)]="count"
  19. 19
    What is dependency injection in Angular?
    Classes declare what they need and Angular’s injector creates and supplies the instances, so components never construct services themselves
    This makes services easy to share and to replace with fakes in tests.
  20. 20
    Inject a dependency with inject()
    ts
    export class UserList {
      private http = inject(HttpClient)
    }
    inject() only works in an injection context: field initializers, constructors and provider factories.
  21. 21
    What does @Injectable({ providedIn: "root" }) do?
    Registers the service with the root injector: one shared instance for the whole app, and tree-shaken away if nothing injects it
  22. 22
    What happens when a component lists a service in its own providers?
    Each instance of that component gets its own service instance, shared with its child components (hierarchical injectors)
  23. 23
    What is an Angular service?
    A class, usually @Injectable, that holds logic or state shared across components, like data access, caching or logging
  24. 24
    Provide a non-class value, like a config URL
    ts
    export const API_URL = new InjectionToken<string>("API_URL")
    
    // providers: [{ provide: API_URL, useValue: "https://api.example.com" }]
    private apiUrl = inject(API_URL)
  25. 25
    Observable vs Promise
    An Observable is lazy, can emit many values over time and is cancelled by unsubscribing; a Promise starts immediately and settles once
  26. 26
    Why is no request sent?
    ts
    loadUsers() {
      this.http.get<User[]>("/api/users")
    }
    HttpClient returns a cold Observable: nothing happens until something subscribes, for example .subscribe(), the async pipe or toSignal()
  27. 27
    What does switchMap do?
    Maps each value to an inner Observable and unsubscribes from the previous one, cancelling stale requests; ideal for search-as-you-type
    ts
    results$ = this.query$.pipe(
      debounceTime(300),
      switchMap((q) => this.api.search(q)),
    )
  28. 28
    mergeMap vs concatMap vs exhaustMap
    mergeMap runs inner Observables concurrently; concatMap queues them in order; exhaustMap ignores new values while one is still running
    exhaustMap suits a submit button that shouldn’t fire twice.
  29. 29
    Three ways to avoid leaking subscriptions in a component
    The async pipe, takeUntilDestroyed() in the pipe, or toSignal(): each unsubscribes automatically when the component is destroyed
  30. 30
    What does toSignal() do?
    Subscribes to an Observable and exposes its latest value as a signal, unsubscribing on destroy; the value is undefined until the first emission unless you pass initialValue
    It and toObservable() come from @angular/core/rxjs-interop.
  31. 31
    What is a BehaviorSubject?
    A Subject that needs an initial value, stores the latest value and emits it immediately to every new subscriber
  32. 32
    What does {{ user$ | async }} do?
    Subscribes to user$, renders its latest value, marks the view for checking on each emission and unsubscribes when the component is destroyed
    The $ suffix is a naming convention for Observables.
  33. 33
    Define routes, including a 404 page
    ts
    export const routes: Routes = [
      { path: "", component: Home },
      { path: "users/:id", component: UserDetail },
      { path: "**", component: NotFound },
    ]
    The first matching route wins, so the ** wildcard goes last.
  34. 34
    Lazy-load a route’s component
    ts
    {
      path: "admin",
      loadComponent: () => import("./admin/admin").then((m) => m.Admin),
    }
    Use loadChildren to lazy-load a whole set of child routes.
  35. 35
    Where do routed components render, and how do you link to them?
    Inside <router-outlet />; link with routerLink="/users" or navigate in code with router.navigate(["/users", id])
  36. 36
    Read the :id route parameter
    ts
    private route = inject(ActivatedRoute)
    id = this.route.snapshot.paramMap.get("id")
    With provideRouter(routes, withComponentInputBinding()), a matching id = input<string>() receives it directly.
  37. 37
    Protect a route with a guard
    ts
    export const authGuard: CanActivateFn = () =>
      inject(Auth).isLoggedIn() || inject(Router).createUrlTree(["/login"])
    
    // { path: "admin", canActivate: [authGuard], … }
    Returning a UrlTree redirects instead of just blocking.
  38. 38
    What is change detection?
    Angular checking component templates for bindings whose values changed and updating the DOM to match
  39. 39
    What does Zone.js do for Angular?
    It patches async browser APIs (events, timers, promises, XHR) so Angular knows when to run change detection
    Zoneless change detection, driven by signals and events, is stable since Angular 20.2 and the default for new apps since v21.
  40. 40
    When is an OnPush component checked?
    When an input gets a new reference, an event fires in it or its children, a signal read in its template changes, an async pipe emits, or markForCheck() is called
  41. 41
    With OnPush, why doesn’t the child update?
    ts
    this.user.name = "Grace" // user is passed as an input
    The input still holds the same object reference, so OnPush sees no change. Pass a new object: this.user = { ...this.user, name: "Grace" }
  42. 42
    constructor vs ngOnInit
    Use the constructor (or field initializers) for injection; ngOnInit runs once after the first input values are set, so initialization that needs inputs goes there
    With signal inputs you can often use computed instead.
  43. 43
    What are ngOnChanges and ngOnDestroy for?
    ngOnChanges runs whenever bound inputs change, first before ngOnInit; ngOnDestroy runs just before the component is removed, for cleanup
  44. 44
    Register cleanup without implementing ngOnDestroy
    ts
    inject(DestroyRef).onDestroy(() => clearInterval(this.timer))
  45. 45
    What does <ng-content /> do?
    Content projection: it renders the markup the parent puts between the component’s tags
    Use <ng-content select="[card-title]" /> for several named slots.
  46. 46
    What is a pipe?
    A template function that transforms a value for display, like {{ price | currency: "EUR" }} or {{ when | date: "short" }}
    Pipes are pure by default: they re-run only when the input value or reference changes.
  47. 47
    What is #box in <input #box />?
    A template reference variable: it refers to the element (or component) elsewhere in the template, e.g. (click)="search(box.value)"
  48. 48
    Are component styles global?
    No. With the default ViewEncapsulation.Emulated, Angular adds generated attributes so a component’s styles apply only to its own template
  49. 49
    Common Angular CLI commands
    ng new creates a project, ng serve runs the dev server, ng generate component name (ng g c) scaffolds, ng build builds for production
  50. 50
    Are *ngIf and *ngFor still recommended?
    No. They are deprecated since Angular 20 in favor of @if, @for and @switch; ng generate @angular/core:control-flow migrates templates

Frequently asked questions

Which Angular version does this deck cover?

Current Angular (v20 and later), with version notes where a feature arrived recently, such as built-in control flow (17), standalone by default (19) and zoneless apps (20.2 and 21).

Do I still need to learn RxJS?

Some. Signals now handle most component state, but HttpClient, the router and many libraries return Observables, so you should know subscribing, switchMap and how to avoid leaks.

Signals or Observables?

Signals for synchronous state that templates read; Observables for streams of events over time, like requests or keystrokes. toSignal and toObservable convert between them.