170 companion flashcards · AI-assisted study content · Open the deck →
This deck offers a solid introduction to Angular, one of the most widely used frameworks for building modern web applications. The cards walk you through the core building blocks of the framework, starting with the basics of what Angular is and how components, decorators, and data binding fit together. From there, you'll explore services, dependency injection, and modules before moving into more practical topics like lifecycle hooks, routing, and lazy loading.
It's a great fit for beginners who are just starting out with Angular, as well as developers preparing for interviews or revising fundamentals they may not have touched in a while. If you already have some familiarity with TypeScript and web development, you'll find the questions move quickly through the foundational concepts and give you a useful way to test your recall.
To get the most out of the deck, try working through it in short, focused sessions rather than cramming everything at once. Spacing your reviews over several days helps move the concepts from short-term memory into long-term retention, which is especially helpful for Angular's many decorators and lifecycle methods. When you get a card wrong, take a moment to recall where in a project you'd actually use that feature — pairing the definition with a mental example tends to stick much better than the wording alone.
Angular is a TypeScript-based front-end framework developed by Google for building dynamic single-page applications. It provides a comprehensive solution that includes components, services, routing, and dependency injection out of the box, allowing developers to focus on application logic rather than wiring infrastructure. At the heart of every Angular app is the component, which is the fundamental building block. A component consists of a TypeScript class decorated with @Component(), an HTML template, optional CSS styles, and metadata such as selector, templateUrl, and styleUrls. The @Component() decorator marks a class as an Angular component and tells Angular how to process, instantiate, and use the component at runtime.
Data binding is the mechanism that connects the component class to the template. Angular supports four forms: interpolation using double curly braces for displaying values, property binding with square brackets to set element properties, event binding with parentheses to respond to user actions, and two-way binding using the banana-in-a-box syntax [(ngModel)] to keep the view and model in sync. Services are classes decorated with @Injectable() that encapsulate reusable business logic, data access, or utility functions. When provided at the root level with @Injectable({ providedIn: 'root' }), they become singletons shared across the application through dependency injection, where dependencies are passed into a class's constructor rather than instantiated by the class itself.
Angular organizes code into NgModules, which are classes decorated with @NgModule(). The declarations array registers components, directives, and pipes that belong to the current module, while imports brings in other NgModules whose exported classes are needed by templates in this module. Components expose lifecycle hooks that fire at specific moments: ngOnChanges when input properties change, ngOnInit once after the first ngOnChanges when input bindings are available and which is preferred over the constructor for fetching initial data, and ngOnDestroy just before Angular destroys the component, used for cleanup like unsubscribing from Observables to prevent memory leaks. Components communicate via @Input() for parent-to-child data flow, @Output() with EventEmitter for child-to-parent events, and @ViewChild or @ContentChild for direct references, where ViewChild queries the component's own template and ContentChild queries projected content.
Directives are classes that add behavior to elements in the DOM and come in three flavors. Component directives are directives with a template, which is what components themselves are. Structural directives change the DOM layout; the most common are *ngIf, which conditionally includes or removes an element from the DOM based on a boolean expression, and *ngFor, which repeats a template for each item in a collection, ideally using trackBy to improve performance by helping Angular identify which items changed. Attribute directives change the appearance or behavior of an element, with ngClass and ngStyle being built-in examples. Custom attribute directives are created by decorating a class with @Directive({ selector: '[appHighlight]' }) and can use @HostListener to react to host element events and inject ElementRef to access the underlying DOM node. The :host selector in component CSS targets the host element itself, while :host-context() allows styling based on ancestor states.
Pipes are functions that transform data in templates and are used with the pipe operator. Angular ships with built-in pipes like DatePipe, UpperCasePipe, LowerCasePipe, CurrencyPipe, DecimalPipe, JsonPipe, and AsyncPipe. The AsyncPipe is especially important because it automatically subscribes to an Observable or Promise, returns the emitted value, and automatically unsubscribes when the component is destroyed to prevent memory leaks, while also triggering change detection on new emissions. Custom pipes are created by implementing the PipeTransform interface on a class decorated with @Pipe({ name: 'truncate' }), with the transform method defining how the input value is converted to output.
Understanding the difference between ng-container, ng-template, and ng-content is essential. ng-container is a non-rendering host used to group elements when applying multiple structural directives. ng-template defines inert content that is not rendered until explicitly instantiated, for example through ViewContainerRef.createEmbeddedView or as the else branch of *ngIf. ng-content enables content projection, allowing a parent to insert content into a child component's template, with multi-slot projection using the select attribute to route content to specific locations. ngTemplateOutlet renders an ng-template by reference and accepts a context object, while ngProjectAs re-maps projected content to a specific selector when wrapping it in a structural directive. ViewEncapsulation controls whether component styles leak out, with Emulated as the default that adds attribute selectors, None for global styles, and ShadowDom for native shadow DOM. Renderer2 abstracts DOM operations and is preferred over direct ElementRef manipulation because it supports server-side rendering and security.
Angular routing maps URL paths to components, enabling navigation in a single-page application without full page reloads. Routes are defined as an array of Route objects and registered with RouterModule.forRoot(routes) in module-based apps or provideRouter(routes) in standalone apps. The router-outlet directive marks where routed components render, and a wildcard path handles unmatched routes. Lazy loading delays the loading of a feature module until the user navigates to its route, configured with loadChildren or loadComponent, which reduces the initial bundle size. The forRoot method is used once in the root module to register the Router service, while forChild is used in feature modules to register additional routes without re-providing the service.
Route guards control whether navigation to or from a route is allowed. CanActivate runs before a route is activated and returns a boolean, UrlTree, or an Observable or Promise that resolves to one, with false cancelling navigation, making it ideal for checking authentication. CanDeactivate guards leaving a route, useful for warning about unsaved changes. CanLoad controls whether a lazy-loaded module loads. CanMatch runs before route matching and is preferred over CanActivate for feature flags and role-based route selection because returning false lets the router try the next route definition. Resolve pre-fetches data before a route is activated, ensuring the component has its required data before rendering and avoiding empty states. Modern Angular favors functional guards that are plain functions using inject() instead of class-based services.
The ActivatedRoute service provides information about the current route. Its params and queryParams are Observables that emit on every change, while snapshot is a non-Observable static value. Prefer paramMap over params for type safety because paramMap returns a strongly typed object with get() and getAll() methods. The runGuardsAndResolvers route property controls when guards and resolvers re-run on the same component, with options including 'paramsChange', 'paramsOrQueryParamsChange', 'always', and 'pathParamsChange'. NavigationExtras configure router.navigate() with options like queryParams, queryParamsHandling ('merge' or 'preserve'), fragment, state, replaceUrl, and relativeTo. RouterLinkActive applies a CSS class when the link is active and should be combined with [routerLinkActiveOptions]="{ exact: true }" for the root path. The router emits events during navigation, including NavigationStart, GuardsCheckStart, RoutesRecognized, ResolveStart, NavigationEnd, NavigationCancel, and NavigationError, which can be observed for loading indicators or analytics. Other advanced options include PreloadingStrategy for background loading of lazy modules, HashLocationStrategy for hash-based URLs that avoid server-side rewrites, and RouteReuseStrategy for tab-like component persistence.
Angular offers two approaches to handling forms. Template-driven forms use directives like ngModel in the template and are simpler for basic forms, while reactive forms use FormGroup and FormControl in the component class, offering more control, easier testing, and better handling of dynamic or complex forms. Reactive forms require importing ReactiveFormsModule. A reactive form is created by instantiating a FormGroup that contains FormControl instances, each with a default value and an optional array of Validators. The form is bound in the template with [formGroup] and individual inputs with formControlName.
FormBuilder is an injectable service that provides shorthand syntax for creating FormGroup, FormControl, and FormArray instances, using array notation to specify initial values and validators. Since Angular 14, FormBuilder.nonNullable produces typed, non-nullable controls where initial values are required and reset() restores them. FormArray manages a dynamic list of FormControls or FormGroups, useful for repeating form sections whose count changes, with methods like push() and removeAt(). FormRecord is similar to FormGroup but supports dynamic keys, useful when the field set is not known at compile time. Typed reactive forms infer control types automatically, so this.form.controls.name.value is correctly typed as string rather than any, with FormControl<string | null> for nullable cases.
Validators can be composed in arrays and applied to controls, and errors from all validators are aggregated on the control's errors property. Cross-field validators receive the full AbstractControl (typically a FormGroup) and validate across fields, for example checking that password and confirm match. Async validators return an Observable or Promise that resolves to validation errors, ideal for server-side checks like unique email validation; call updateValueAndValidity() to re-run them. The updateOn option changes when a control's value is updated and validated, with options of 'change' (the default), 'blur', or 'submit'. Reactive forms track control state with flags: markAsDirty and markAsPristine for user-interaction tracking, and markAsTouched and markAsUntouched for focus tracking, both used to control when error messages appear. valueChanges emits the new value whenever the form value changes, while statusChanges emits the new VALID, INVALID, PENDING, or DISABLED status when validation state changes.
RxJS is the reactive extensions library for JavaScript that Angular relies on heavily for HTTP requests via HttpClient, reactive form value changes, router events, and event handling. At its core is the Observable, a lazy stream of data that does not execute until subscribed to and can emit multiple values over time, unlike a Promise which executes eagerly and resolves once. Observables are also cancellable via unsubscribe(). A Subject is both an Observable and an Observer that multicasts values to multiple subscribers. Common variants include BehaviorSubject which stores the latest value and emits it to new subscribers, ReplaySubject which replays a specified number of past emissions, and AsyncSubject which emits only the last value upon completion.
RxJS operators transform and combine streams. map transforms emitted values, filter filters emissions by condition, tap performs side effects without modifying the stream, and catchError handles errors in the stream. The flattening operators each serve a different concurrency need: switchMap maps each emission to an inner Observable and switches to the latest one, cancelling previous inner Observables, which is ideal for search-as-you-type; mergeMap maps to an inner Observable and subscribes concurrently, useful for parallel writes; concatMap queues inner Observables to run in order; exhaustMap ignores new emissions while one is in flight, useful for submit buttons to prevent double-clicks. Cold Observables create a new producer per subscription, like HttpClient.get(), while hot Observables share the same stream across subscribers, like fromEvent or Subject. shareReplay({ bufferSize: 1, refCount: true }) multicasts an Observable and replays the latest value to new subscribers, common for caching HTTP results.
HttpClient is the service in @angular/common/http for making HTTP requests, returning Observables and supporting get(), post(), put(), delete(), typed responses with generics, request and response headers, query parameters, and progress events. The observe option changes what is returned: 'body' (the default), 'response' for full HttpResponse with headers and status, or 'events' for HttpEvent including progress. The reportProgress option enables upload and download progress events, but only with XHR, not with withFetch(). HttpParams and HttpHeaders are immutable, so set() and append() return new instances that must be reassigned. Errors arrive as HttpErrorResponse, and you can handle them with the second argument to subscribe() or with catchError, accessing err.status, err.message, and err.error. Modern Angular uses provideHttpClient() with withFetch() and withInterceptors() instead of HttpClientModule and the HTTP_INTERCEPTORS multi-token; interceptors are now simple functions that use inject() to access dependencies. takeUntilDestroyed() from @angular/core/rxjs-interop automatically completes an Observable when the host directive or component is destroyed, while DestroyRef provides a generic mechanism for registering cleanup callbacks.
Signals are a reactive primitive introduced in Angular 16 that wrap a value and notify interested consumers when the value changes. A signal is created with signal(initialValue), read by calling it as a function, and updated with set() or update(). Unlike BehaviorSubject, signals are synchronous and integrate automatically with Angular's change detection, so there is no need for subscribe() or the async pipe. computed() creates a derived signal whose value is automatically recalculated when any of its tracked signal dependencies change, and it is both lazy and memoized, recomputing only when read and a dependency has changed. effect() runs a side-effecting function whenever any signal it reads changes, executing asynchronously via the microtask queue after change detection. Effects must be created in an injection context unless an explicit injector is provided or manualCleanup is set. untracked() lets you read signals inside an effect without creating a dependency, useful for reading current values inside handlers. The toSignal() and toObservable() interop functions allow bridging between signals and RxJS.
Standalone components, directives, and pipes (introduced in Angular 14) do not need to be declared in an NgModule, instead declaring their own dependencies in the imports array of @Component(). The application is bootstrapped with bootstrapApplication(AppComponent, appConfig), which replaces the module-based bootstrapModule() flow. The appConfig object provides top-level services like provideRouter(routes, withComponentInputBinding(), withViewTransitions()) and provideHttpClient(withInterceptors([...]), withFetch()). Standalone exports require explicit re-export from barrel files when reusing across boundaries.
Angular 17 introduced new built-in block control flow that replaces the *ngIf, *ngFor, and *ngSwitch structural directives with native template syntax: @if (cond) { ... } @else { ... }, @for (item of items; track item.id) { ... } @empty { ... }, and @switch (val) { @case (x) { ... } @default { ... } }. The new control flow is faster, has built-in track, and requires no imports of CommonModule. The track expression is required in @for to identify items uniquely and avoid unnecessary DOM re-creation, throwing a compile-time error if omitted. The @empty block renders content when the iterable is empty. Angular 18 added the @let block, which declares a local template variable scoped to the surrounding block that is reactive and re-evaluates when signals it reads change. The @defer block enables fine-grained code-splitting per template block, lazily loading content only when a trigger fires, with @placeholder, @loading, and @error blocks providing graceful fallbacks. Triggers include on viewport(ref), on idle, on immediate, on timer(Xms), on interaction(ref), on hover(ref), and when condition(), and they can be combined with semicolons. Angular 17.1 added signal-based inputs via input(), with transform support for built-in numberAttribute and booleanAttribute helpers and alias support for decoupling public API names from internal class properties. model() creates a two-way bindable signal that replaces separate @Input() and @Output() pairs, and output() is the signal-era equivalent of @Output() with EventEmitter. Angular 17.2 added signal-based queries: viewChild() returns a single-element Signal while viewChildren() returns a Signal of all matches, and contentChild() queries projected content, all push-based and emitting when elements appear.
The inject() function retrieves a dependency from the active injection context without using a constructor, enabling cleaner code and use in functional guards, interceptors, and factory providers that are not classes. Outside an injection context, inject() throws unless given an explicit Injector, and the { optional: true } option prevents the error when the dependency may be missing. The classic modifier decorators still apply: @Self() limits the search to the current injector, @SkipSelf() starts from the parent injector, @Optional() injects null instead of throwing when not found, and @Host() stops the search at the host component. forwardRef(() => SomeClass) resolves a class reference later, useful when classes reference each other in the same file. Angular maintains a tree of injectors parallel to the component tree, so providers in providers: [] create a new injector for that component and its descendants, enabling scoped instances like one UserService per feature module. The viewProviders array is visible only to the component's own view and not to projected content, while providers is visible to both. InjectionToken creates a non-class DI token for injecting values, interfaces, or primitives, with provider configuration using useValue for a literal, useClass for a class instance, useFactory for a function-computed value, and useExisting to alias an existing token. The multi: true option creates a multi-provider, allowing multiple values to be associated with a single token, as used by HTTP_INTERCEPTORS and NG_VALIDATORS.
Change detection is Angular's mechanism for keeping the view in sync with component data. By default Angular uses a zone-based strategy that checks all components from root to leaves on every event, but ChangeDetectionStrategy.OnPush optimizes this by checking the component only when an @Input() reference changes, an event originates from the component, an Observable linked with AsyncPipe emits, or markForCheck() is called manually. ChangeDetectorRef.markForCheck() re-checks the component on the next cycle, while detach() and reattach() give full manual control. ApplicationRef.tick() triggers a full application-wide change detection cycle, generally avoided in production. NgZone is Angular's wrapper around Zone.js that tracks async operations to trigger change detection; NgZone.runOutsideAngular() runs code that should not trigger CD, and NgZone.run() re-enters the zone. In Angular 18, apps can run without Zone.js via provideExperimentalZonelessChangeDetection(), relying on signals and OnPush for change detection, which yields a smaller bundle and faster performance. For animation support, provideAnimations() enables the full engine, provideAnimationsAsync() lazy-loads it, and provideNoopAnimations() runs synchronously for tests. Common change-detection pitfalls include ExpressionChangedAfterItHasBeenCheckedError, which fires in dev mode when a binding's value changes after Angular read it, often caused by updating @Input-bound data in ngAfterViewInit and fixable with a deferred update.
The Angular CLI (ng) is the command-line tool for scaffolding and managing projects. ng new creates a new project, ng generate component, service, or module creates code via schematics, ng serve starts the dev server, ng build produces a production build, and ng test runs unit tests. ng add installs a package and runs its setup schematic, while ng update upgrades dependencies and runs migration schematics that automatically refactor code. Environment files like environment.ts and environment.prod.ts store configuration values that differ between environments, with Angular CLI automatically swapping the file during build. An Angular workspace in angular.json can contain multiple projects, including apps and libraries; ng generate library creates a publishable package built with ng-packagr. JIT compiles templates in the browser, slower with larger bundles, while AOT compiles at build time as the default for production, yielding smaller bundles and faster startup. Ivy is the current rendering engine, providing smaller bundles and better debugging. Angular Universal enables server-side rendering via provideClientHydration(), which reuses the server-rendered DOM on the client to preserve focus, scroll position, and event listeners; must-use withFetch() and avoid direct DOM access in lifecycle hooks. Angular 19 added incremental hydration, which defers server-side rendering of specific template blocks via @defer (hydrate on viewport) to reduce SSR cost. Testing relies on TestBed, which creates a testing module, and ComponentFixture returned by TestBed.createComponent() exposes componentInstance, debugElement, nativeElement, and detectChanges(). ComponentHarness from @angular/cdk/testing provides a stable, abstracted testing API that survives markup changes, while ng-mocks is a popular library for mocking modules and services. Global error handling implements the ErrorHandler interface to centralize logging, APP_INITIALIZER's modern replacement is provideAppInitializer(), and provideEnvironmentInitializer() runs after providers are configured but before bootstrap for side effects like telemetry registration.
components, services, routing, and dependency injection out of the box.ngModel in the template and are simpler for basic forms.FormGroup and FormControl in the component class, offering more control, easier testing, and better handling of dynamic or complex forms. Reactive forms require importing ReactiveFormsModule.get() method with a generic type:this.http.get<User[]>('/api/users').subscribe({
next: users => this.users = users,
error: err => console.error('Failed:', err)
});count()subscribe() and works with operatorsasync pipe or markForCheck()debounceTime; signals do not nativelytoSignal() / toObservable() to interoperate.providers is visible to the component and its content children (projected content).viewProviders is visible only to the component's own view (template + view children), not to projected content.viewProviders when a service must not leak into content that the parent may project in.PathLocationStrategy (HTML5 history API, e.g., /users). HashLocationStrategy uses URL fragments: /#/users. It avoids server-side rewrite requirements:{ provide: LocationStrategy, useClass: HashLocationStrategy }provideRouter(routes, withHashLocation()).readonly header = viewChild<ElementRef>('hdr');
readonly projected = contentChild<MyComponent>(MyComponent);Signals and integrate with effect(). Signal queries are push-based — they emit when the queried element appears, unlike the old @ViewChild static fields.{ reportProgress: true } to enable upload/download progress events:this.http.post('/upload', formData, { reportProgress: true, observe: 'events' });HttpEventType.UploadProgress and DownloadProgress events. Only works with XHR; not with withFetch().updateOn changes when the control's value is updated/validated:'change' (default)'blur''submit'new FormControl('', { validators: ..., updateOn: 'blur' }).ViewContainerRef.createComponent() to mount components at runtime:this.vcr.createComponent(MyComponent);this.vcr.createComponent(await import('./lazy.component').then(m => m.LazyComponent));Drill this topic
170 flashcards on Angular Framework — free, no signup needed to start.
Study Angular Framework flashcardsLearnWiki pages are generated with AI assistance from LearnCoachAssist's reviewed study catalog and may contain errors — verify anything critical against your course materials.