AngularJS Legacy Maintenance Interview Questions
50 questions and answers covering AngularJS Legacy Maintenance, from fundamentals through practical, debugging, and design-level topics.
50 of 50 interview questions
AngularJS reached end of life in 2022 -- what does that mean in practice for a team maintaining an existing AngularJS application?beginnerLegacy Context, $scope & Two-Way Binding
No new official security patches, bug fixes, or feature updates are being published by the framework's maintainers -- any security vulnerability discovered in AngularJS itself (as opposed to application code) will not be fixed upstream, which is why a maintenance team must treat unresolved framework-level issues as a standing risk and prioritize planning a migration path rather than assuming the framework will keep receiving fixes indefinitely.
Why should AngularJS (1.x) never be recommended as a starting point for a brand-new project, even though it's still safe to maintain an existing one?beginnerLegacy Context, $scope & Two-Way Binding
Since it's end-of-life with no ongoing official support, choosing it for a new project means starting behind on security and tooling from day one, with no path to future framework-level fixes -- a new project should use a currently-supported framework (like current Angular 2+, or another actively maintained option) instead; AngularJS maintenance work is exclusively about existing codebases that can't be rewritten immediately.
What is `$scope` in AngularJS, and how does it differ conceptually from a component class's `this` in current Angular?intermediateLegacy Context, $scope & Two-Way Binding
`$scope` is AngularJS's object representing the data and methods available to a given part of the template, glued together by the framework's own binding system; current Angular replaced this with plain TypeScript component classes where template bindings reference `this`-scoped class properties directly -- the two mechanisms are NOT interchangeable, and this is one of the clearest markers separating legacy AngularJS code from current Angular code.
What does two-way data binding via `ng-model` mean in AngularJS, and what is a risk of relying on it heavily in a large legacy codebase?intermediateLegacy Context, $scope & Two-Way Binding
`ng-model` automatically keeps a form input's value and a `$scope` property synchronized in both directions -- convenient for simple forms, but in a large legacy application, heavy reliance on implicit two-way binding across many nested scopes can make it genuinely difficult to trace WHERE a given value actually changed from, which is a common source of confusing bugs a maintainer has to untangle.
What is `$scope` inheritance in AngularJS, and why can it cause confusing bugs when a nested scope shadows a parent property?advancedLegacy Context, $scope & Two-Way Binding
Child scopes prototypically inherit from their parent scope, so reading a property that exists on the parent 'just works' from a child -- but WRITING a primitive value (like a string or number) from a child scope creates a new property on the CHILD scope instead of updating the parent's, silently breaking the expected two-way binding; this is a well-known legacy AngularJS pitfall, often worked around with a `.`-containing object property (the 'dot rule') instead of a bare primitive on `$scope`.
Common mistake: Binding a primitive value directly on a child $scope inside ng-repeat or a nested controller, then being surprised writes to it don't propagate to the parent scope due to prototypal inheritance shadowing.
What is an AngularJS module (`angular.module(...)`), and how does it relate to organizing a legacy application's code?beginnerLegacy Context, $scope & Two-Way Binding
A module is AngularJS's unit of code organization, bundling controllers, services, directives, and configuration under a named container that other modules can depend on -- when reading an unfamiliar legacy codebase, tracing a feature's module dependencies is often the fastest way to understand which controllers/services/directives are actually wired together for a given screen.
What is an AngularJS controller, and what is it responsible for in the older Model-View-Controller-flavored architecture AngularJS encouraged?beginnerLegacy Context, $scope & Two-Way Binding
A controller is a constructor function that sets up the initial state and behavior exposed on `$scope` for a specific view -- in a legacy codebase, controllers are often the first place to look to understand what data and actions a given screen/template actually has available to it.
What is 'controllerAs' syntax, and why might you find it used inconsistently across an older AngularJS codebase compared to raw `$scope` usage?advancedLegacy Context, $scope & Two-Way Binding
`controllerAs` binds a controller instance to a named alias in the template (`this.user` referenced as `vm.user`) instead of assigning directly to `$scope`, which was introduced as a later best-practice improvement to make the data source in a template more explicit and avoid some `$scope`-inheritance pitfalls -- a legacy codebase built up over years often mixes older raw-`$scope` controllers with newer `controllerAs`-style ones, reflecting how AngularJS best practices themselves evolved during the framework's active years.
Why is it important to identify which version of AngularJS (1.x) a legacy codebase is actually pinned to before making changes?intermediateLegacy Context, $scope & Two-Way Binding
AngularJS 1.x had meaningful behavioral differences across its own minor versions (and third-party directive libraries were often pinned to a specific compatible version range) -- confirming the exact pinned version before making changes avoids assuming behavior or APIs from a different 1.x version than the one actually deployed, and matters for assessing exactly which known vulnerabilities/CVEs are even applicable to this specific deployment.
What should a maintainer's FIRST priority be when picking up an unfamiliar, undocumented AngularJS legacy codebase -- rewriting, or something else?intermediateLegacy Context, $scope & Two-Way Binding
Understanding the existing application's actual behavior and structure well enough to make safe, low-risk changes -- jumping straight to a rewrite without first understanding the current system's real behavior (including undocumented edge cases business logic may depend on) is a common way legacy modernization efforts go wrong; reading/mapping the existing code and its test coverage (or lack thereof) should come before any large-scale rewrite decision.
What is a built-in AngularJS directive like `ng-repeat`, and what legacy performance concern is commonly associated with it on large lists?intermediateDirectives & Services
`ng-repeat` renders a template once per item in a collection, similar in spirit to a modern framework's list-rendering construct; on large lists, it's commonly associated with digest-cycle performance concerns, since (as covered later) each bound expression in the repeated template becomes a watcher that AngularJS re-checks on every digest cycle, and this can visibly slow down a legacy app with large unpaginated lists.
What is a custom AngularJS directive, at a high level, and what's one reason a legacy codebase might have accumulated many small custom directives?intermediateDirectives & Services
A custom directive extends HTML with new element/attribute behavior via a directive definition object, similar in spirit to how modern frameworks let you build reusable, encapsulated UI components -- legacy AngularJS codebases often accumulated many small custom directives as the primary way to achieve reusable UI logic, since AngularJS predates today's more standardized component-first patterns.
What does a directive's `scope` option (`scope: {}` for an isolate scope) control, and why does understanding it matter when reading unfamiliar custom directive code?advancedDirectives & Services
It controls whether the directive gets its OWN isolated scope (decoupled from the parent, with explicit `@`/`=`/`&` bindings defining exactly what data flows in) versus inheriting/sharing the parent scope directly -- misreading which scope mode a directive uses is a common source of confusion when tracing how data actually flows into an unfamiliar legacy directive.
What is an AngularJS service (or factory), and what pattern does it typically serve in a legacy application?intermediateDirectives & Services
A service/factory is AngularJS's mechanism for a singleton object holding shared logic or state, injected into controllers/directives via AngularJS's own dependency injection system -- typically used for concerns like shared business logic, cross-controller state, or wrapping `$http` calls to a backend API, similar in intent (though not implementation) to how current Angular uses injectable services.
What is the practical difference between AngularJS's `.service()` and `.factory()` registration methods?advancedDirectives & Services
`.service()` registers a constructor function that AngularJS instantiates with `new`; `.factory()` registers a function whose RETURN VALUE becomes the injectable -- functionally they can achieve the same result, and a legacy codebase often uses both inconsistently depending on which pattern a given contributor preferred at the time it was written.
Why is AngularJS's dependency injection via minifiable string names (or the array-annotation syntax) a legacy maintenance concern when a build's minifier renames function parameters?advancedDirectives & Services
AngularJS's DI can infer dependencies from a function's parameter NAMES, but minification renames parameters to short, meaningless names, silently breaking that inference at runtime unless the dependencies are declared explicitly via the array-annotation syntax (`['$http', function($http) {...}]`) or an `ngAnnotate`-style build step -- a legacy codebase missing this annotation on some services can work fine in local development (unminified) but break specifically in a minified production build, a classic legacy AngularJS gotcha.
Common mistake: Relying on AngularJS's implicit parameter-name DI inference without array annotations, which works in unminified dev builds but silently breaks once a production build minifies parameter names.
What does a directive's `link` function do, and why might it appear alongside (or instead of) a `controller` in an older custom directive?advancedDirectives & Services
The `link` function is where a directive attaches DOM-level behavior (event listeners, direct DOM manipulation) after the template is compiled and linked to the scope -- older directive code often mixed `link`-function DOM manipulation directly with controller logic, a pattern current component-based frameworks discourage in favor of more declarative, DOM-manipulation-free component code.
Why should direct DOM manipulation inside a legacy AngularJS directive's `link` function be treated cautiously when making changes to it?advancedDirectives & Services
Direct DOM manipulation bypasses AngularJS's own data-binding and digest-cycle awareness, meaning changes made this way won't automatically trigger AngularJS to notice and re-check bound expressions -- a maintainer changing such code needs to understand whether a manual `$scope.$apply()`/`$digest()` call is needed afterward to keep the rest of the framework's state in sync with the DOM change.
How would you safely add a new feature to an AngularJS service that's injected into many different controllers across a large legacy codebase?advancedDirectives & Services
Search the codebase for every injection point of that service first, understand which callers might be affected by a behavior change (not just a purely additive one), prefer adding a new method over changing an existing method's signature/behavior where possible, and (if any automated test coverage exists for the service or its consumers) run it before and after the change -- minimizing blast radius matters more in an old, likely under-tested codebase than in a newer, well-tested one.
What is AngularJS's built-in dependency injection container, and how does injecting a mock service in a unit test work for a legacy controller?advancedDirectives & Services
AngularJS ships its own DI container (distinct from, and predating, current Angular's DI system) that resolves named dependencies for controllers/services/directives; in a unit test, `angular.mock.module()` and `inject()` (from `ngMock`) let you substitute a real service with a mock/stub before the controller under test is instantiated, similar in spirit to how `TestBed` providers work in current Angular testing.
What is the AngularJS digest cycle, at a conceptual level?intermediateThe Digest Cycle & Performance Pitfalls
The digest cycle is AngularJS's dirty-checking loop -- it re-evaluates every registered 'watcher' expression, compares each result to its previous value, and re-runs until no more changes are detected (or a max-iteration safety limit is hit), updating the DOM for anything that changed -- this is architecturally very different from current Angular's more targeted, zone-based change detection.
What triggers a digest cycle in AngularJS, and why might a legacy codebase have manual `$scope.$apply()` calls scattered through it?advancedThe Digest Cycle & Performance Pitfalls
AngularJS's own directives (like `ng-click`) and services (like `$http`, `$timeout`) automatically trigger a digest cycle after they run; code that changes `$scope` data OUTSIDE AngularJS's awareness (e.g. a raw third-party library callback, or plain `setTimeout`) needs a manual `$scope.$apply()` to tell AngularJS a digest is needed -- legacy code integrating older non-Angular-aware libraries often has these manual calls scattered around specifically to bridge that gap.
Why does a legacy AngularJS application with hundreds or thousands of watchers commonly become noticeably slow?advancedThe Digest Cycle & Performance Pitfalls
Every watcher's expression is re-evaluated on EVERY digest cycle (potentially multiple times per cycle until stable), so the cost of a single digest scales roughly linearly with the total number of active watchers -- a legacy app that's accumulated many nested `ng-repeat`s, bindings, and custom directives over the years can end up with thousands of watchers, making every user interaction (which triggers a digest) noticeably sluggish.
What is one-time binding (`::value`) in AngularJS, and how does it help mitigate digest-cycle performance problems?advancedThe Digest Cycle & Performance Pitfalls
Prefixing an expression with `::` (e.g. `{{::user.name}}`) tells AngularJS to evaluate it once and then stop watching it for further changes, removing that binding from the ongoing digest-cycle cost entirely -- a common, low-risk performance mitigation for legacy data that's genuinely static after initial render (like a page title or a value that never changes post-load).
<h1>{{::pageTitle}}</h1>What is the `$http` service in AngularJS, and how does it compare conceptually to current Angular's `HttpClient`?intermediateThe Digest Cycle & Performance Pitfalls
`$http` is AngularJS's built-in service for making HTTP requests, returning a promise-like object -- it serves the same conceptual role as current Angular's Observable-based `HttpClient`, but with a different (promise-based) API shape and no built-in RxJS-operator composability; migrating `$http` call sites to `HttpClient` is a common, concrete step in an incremental AngularJS-to-Angular migration.
Why can a `$watch` on a deeply nested object with `objectEquality: true` (the third `true` argument) be an expensive pattern in a legacy codebase?advancedThe Digest Cycle & Performance Pitfalls
Deep-equality watching requires AngularJS to recursively compare the ENTIRE object on every digest cycle rather than a cheap reference check, which scales poorly for large or deeply nested objects -- a legacy codebase with several deep `$watch`es on large data structures is a common, identifiable source of a slow digest cycle worth investigating during a performance pass.
Common mistake: Adding a deep-equality $watch (objectEquality: true) on a large or deeply nested object without realizing it forces an expensive recursive comparison on every digest cycle.
What does the error 'digest already in progress' typically indicate in a legacy AngularJS codebase, and what commonly causes it?advancedThe Digest Cycle & Performance Pitfalls
It means code called `$scope.$apply()` (or `$digest()`) while a digest cycle was already running -- commonly caused by manually calling `$apply()` inside code that AngularJS ALREADY triggers a digest for automatically (like inside an `ng-click` handler), a subtle bug that's worth checking for whenever a maintainer needs to add a manual `$apply()`/`$digest()` call to bridge non-Angular-aware code.
How would you profile which part of a legacy AngularJS application is causing slow digest cycles?advancedThe Digest Cycle & Performance Pitfalls
Browser DevTools' performance/profiler tab can reveal long-running digest-related function calls; AngularJS also has community tooling (like `ng-stats`) that overlays real-time watcher counts and digest duration -- identifying which specific view or component has an outsized watcher count is usually the first step toward targeted mitigation (like adding one-time bindings or paginating a large `ng-repeat`).
Why might pagination or virtual scrolling be a practical performance fix for a legacy `ng-repeat` rendering a very large list?advancedThe Digest Cycle & Performance Pitfalls
`ng-repeat` creates a watcher (or several) per rendered item, so a list of thousands of items directly multiplies the digest cycle's per-cycle cost; rendering only a paginated subset (or only the currently-visible rows, via virtual scrolling) keeps the actual number of live watchers bounded regardless of the underlying dataset's total size, which is often a more practical near-term fix than a full framework migration.
Why does calling a function directly inside an interpolation binding (`{{ getTotal() }}`) in a legacy AngularJS template raise a performance concern, specifically because of how the digest cycle works?advancedThe Digest Cycle & Performance Pitfalls
The function is re-invoked on EVERY digest cycle (potentially multiple times per single cycle until stability is reached), so an expensive function called this way runs far more often than a developer might expect from reading the template alone -- a common legacy performance fix is computing the value once in the controller (or using a filter/one-time binding where appropriate) rather than calling an expensive function directly from the template repeatedly.
Common mistake: Calling an expensive function directly from a template interpolation, not realizing AngularJS re-invokes it on every single digest cycle rather than once per actual data change.
What is a practical first step for reading and understanding an unfamiliar, undocumented legacy AngularJS feature before changing it?beginnerMaintaining & Modernizing
Trace the feature from its route/template entry point down through its controller and injected services, noting which `$scope` properties the template actually binds to and which services those depend on -- building this map before changing anything reduces the risk of missing a non-obvious dependency or side effect that isn't documented anywhere.
Why is writing characterization tests (tests that document the EXISTING behavior, correct or not) often a better first move than immediately fixing a suspected bug in untested legacy AngularJS code?advancedMaintaining & Modernizing
Legacy code frequently has undocumented, business-relied-upon behavior that looks like a bug but isn't -- characterization tests capture what the code ACTUALLY does today, giving a maintainer a safety net to detect any unintended behavior change before deciding whether a given behavior is a genuine bug to fix or a real (if surprising) requirement to preserve.
What testing tools are commonly used for legacy AngularJS unit and end-to-end tests?intermediateMaintaining & Modernizing
Karma with Jasmine (and AngularJS's own `ngMock` module for injecting mocked dependencies) is the traditional AngularJS unit-testing stack; Protractor was AngularJS's original purpose-built end-to-end testing tool, though Protractor itself is also deprecated, so a legacy codebase's e2e suite may need migrating to a currently-maintained tool (like Playwright or Cypress) as part of modernization.
What is `ngUpgrade`, and what migration strategy does it enable?advancedMaintaining & Modernizing
`ngUpgrade` is a compatibility layer that lets AngularJS and current Angular run SIDE BY SIDE in the same application, letting a team migrate a legacy codebase incrementally -- component by component, route by route -- rather than requiring an all-at-once big-bang rewrite, which is significantly lower-risk for a large, actively-used legacy application.
What factors would you weigh when deciding between an incremental `ngUpgrade` migration and a full rewrite of a legacy AngularJS application?advancedMaintaining & Modernizing
A full rewrite risks a long period of parallel-maintaining two codebases and the well-known 'second-system' risk of a rewrite quietly dropping undocumented behavior the original handled; an incremental migration lets the app stay shippable and tested throughout, at the cost of running two frameworks side by side (with the associated bundle-size and complexity overhead) for the migration's duration -- the right choice depends on the app's size, how well-tested it is, team capacity, and how urgently full modernization is needed.
Why should a maintainer specifically check a legacy AngularJS application's dependency tree (AngularJS version and any third-party AngularJS directive libraries) for known security vulnerabilities?advancedMaintaining & Modernizing
Since AngularJS itself is end-of-life and receives no further official patches, any known CVE affecting the specific pinned version in use (or an unmaintained third-party AngularJS library it depends on) will remain unresolved unless the team applies an unofficial patch, upgrades to a still-maintained fork, or removes/replaces the vulnerable dependency themselves -- this is a genuine, standing security consideration specific to maintaining EOL framework code, not a hypothetical concern.
What is AngularJS's built-in protection against a specific class of template-injection vulnerability, and why should a maintainer never disable it without a very good, reviewed reason?advancedMaintaining & Modernizing
AngularJS auto-escapes interpolated bindings by default and uses `$sce` (Strict Contextual Escaping) to guard against unsafely rendering untrusted HTML/URLs/resource URLs; bypassing `$sce` (e.g. via `$sce.trustAsHtml()` on user-supplied content) to render raw HTML reopens a real cross-site-scripting risk, so any legacy code doing this deserves careful security review to confirm the content genuinely can't be attacker-controlled.
How would you prioritize which parts of a large legacy AngularJS application to modernize first, given limited time and budget?advancedMaintaining & Modernizing
Prioritize by a combination of business risk (which screens are most business-critical or highest-traffic), technical risk (which parts have the worst test coverage, or touch known-vulnerable dependencies), and natural migration boundaries (routes/features that are relatively self-contained migrate more cleanly with `ngUpgrade` than tightly-coupled shared services) -- a full rewrite-everything-at-once plan is rarely the pragmatic answer for a codebase that must keep shipping.
What is a realistic timeline expectation to set with stakeholders for migrating a large, actively-used legacy AngularJS application to current Angular (or another modern framework)?advancedMaintaining & Modernizing
For anything beyond a small application, an incremental migration typically takes months (sometimes longer, depending on team size and how tightly coupled the legacy code is), not weeks -- setting this expectation honestly with stakeholders up front, backed by a concrete phased plan (which routes/features migrate first and why), avoids the common failure mode of an underestimated migration stalling out halfway with the team maintaining two frameworks indefinitely.
Why is it important for a developer maintaining AngularJS code to also understand current Angular concepts, and where do the two frameworks genuinely diverge?advancedMaintaining & Modernizing
Understanding current Angular is essential both for planning a credible migration path and for avoiding accidentally applying current-Angular mental models (like component classes and `HttpClient` Observables) to AngularJS code where they don't apply -- the two frameworks share a name and a general SPA philosophy, but diverge completely in their underlying architecture: `$scope`-based dirty-checking versus component-class-based, more targeted change detection; promise-based `$http` versus Observable-based `HttpClient`; and directive-heavy reuse versus component-based reuse.
What is `$q` in AngularJS, and how does it differ from a native JavaScript `Promise`?advancedPromises, Debugging & Legacy Tooling
`$q` is AngularJS's own promise implementation, predating native Promises' widespread availability -- functionally similar (`.then()`/`.catch()`/`$q.all()`), but critically, `$q` promises resolve WITHIN AngularJS's digest cycle, automatically triggering change detection when they settle, whereas a native `Promise` resolving outside AngularJS's awareness may require a manual `$scope.$apply()` to update the view.
Why might mixing native Promises (from a modern third-party library) with `$q`-based legacy code cause a subtle bug where the UI doesn't update after an async operation completes?advancedPromises, Debugging & Legacy Tooling
A native Promise resolving doesn't automatically trigger an AngularJS digest cycle the way a `$q` promise does, so `$scope` changes made inside a native `.then()` callback may not be reflected in the view until some LATER, unrelated digest cycle happens to run -- the legacy fix is wrapping the native-Promise callback's scope changes in a manual `$scope.$apply()` (or converting the flow to `$q` for consistency).
Common mistake: Assuming a native Promise resolving inside legacy AngularJS code will update the view immediately, when it actually needs a manual $scope.$apply() to trigger a digest cycle.
Why is `$timeout` generally preferred over the native `setTimeout` inside AngularJS code?intermediatePromises, Debugging & Legacy Tooling
`$timeout` automatically triggers an AngularJS digest cycle after its callback runs, so any `$scope` changes made inside it are reflected in the view immediately; using native `setTimeout` runs the callback entirely outside AngularJS's awareness, requiring a manual `$scope.$apply()` afterward if it touches bound data -- `$timeout` is also easier to flush deterministically in Karma/Jasmine unit tests via `$timeout.flush()`.
What does the console error 'Unknown provider' typically mean in a legacy AngularJS application, and what's the first thing to check?intermediatePromises, Debugging & Legacy Tooling
It means AngularJS's injector couldn't resolve a named dependency requested by a controller/service/directive -- the first checks are confirming the dependency's name is spelled correctly at both the injection point and its registration, and confirming the module that registers it is actually listed as a dependency of the module doing the injecting (a very common cause in a legacy codebase with many interdependent modules).
What does the AngularJS error `ngRepeat:dupes` mean, and how would you fix it in a legacy template?intermediatePromises, Debugging & Legacy Tooling
It means `ng-repeat` was given a collection containing duplicate values (by default, AngularJS tracks `ng-repeat` items by their value, and can't distinguish two identical entries) -- the fix is providing an explicit `track by` expression (`ng-repeat="item in items track by item.id"`) using a genuinely unique property, rather than relying on default value-based tracking.
What is the difference between `$watch`, `$watchGroup`, and `$watchCollection` in AngularJS, and when would a legacy codebase use each?advancedPromises, Debugging & Legacy Tooling
`$watch` observes a single expression's value; `$watchGroup` observes an array of expressions and fires when any one of them changes; `$watchCollection` performs a shallow watch over an array/object's items (detecting additions/removals/reordering) without the expense of a full deep-equality watch -- choosing the right one for a given legacy watcher matters both for correctness and for avoiding the unnecessary performance cost of an overly deep watch.
Why can an unremoved `$rootScope.$on` event listener be a subtle memory-leak source in a legacy AngularJS single-page application?advancedPromises, Debugging & Legacy Tooling
`$rootScope` outlives every individual controller/component, so a listener registered on it via `$rootScope.$on` in a controller that's since been destroyed (e.g. the user navigated away) keeps running and keeps a reference to that destroyed controller's closure alive -- the legacy fix is capturing the deregistration function `$on` returns and calling it in the controller's `$destroy` event handler (`$scope.$on('$destroy', deregisterFn)`).
Common mistake: Registering a $rootScope.$on listener in a controller without capturing and calling its deregistration function on $destroy, leaking the controller's closure for the lifetime of the app.
What browser-based debugging techniques are useful for inspecting a legacy AngularJS application's live `$scope` state?advancedPromises, Debugging & Legacy Tooling
AngularJS exposes `angular.element(domNode).scope()` in the browser console, letting a developer inspect the live `$scope` object bound to any DOM node selected via DevTools -- along with breakpoints inside controllers/services and the `ng-stats` watcher-count overlay mentioned earlier, this is one of the most direct ways to understand what a legacy view's actual bound data looks like at runtime, especially when the code itself is undocumented.
What does AngularJS's `$exceptionHandler` service do, and why might overriding it be useful in a legacy application?advancedPromises, Debugging & Legacy Tooling
By default, `$exceptionHandler` logs uncaught errors from AngularJS expressions/digest-cycle code to the browser console; overriding it (registering a custom implementation) lets a legacy application forward those errors to a centralized error-tracking/monitoring service instead, which is often valuable for a legacy codebase with limited test coverage, where production error monitoring is one of the few reliable ways to learn about real-world edge cases.
How would you use `$q.all()` to coordinate multiple independent legacy `$http` calls that a controller needs before it can render?advancedPromises, Debugging & Legacy Tooling
`$q.all([this.$http.get(url1), this.$http.get(url2)])` returns a single promise that resolves once ALL the given promises resolve (or rejects as soon as any one rejects), letting a controller wait for several independent legacy API calls to finish before setting up the view's initial `$scope` state -- conceptually the direct `$q`-based counterpart to modern Angular's `forkJoin`.