Let's say you have the route /A/B/C (B being a chi...
Created on: April 11, 2025
Created on: April 11, 2025
Let's say you have the route /A/B/C (B being a child of A, C being a child of B), with each node having a guard and a resolver... what would be the execution order of guards + resolvers overall? What changes if B and C would be lazy loaded modules or components?
In Angular, both the parent and child routes run their guards and resolvers in a predictable top‐down order: first the parent’s guards and resolvers, then the child’s, then the grandchild’s, and so on. Lazy loading adds an extra “canLoad” step (if you use it) and defers loading of the child route configuration until actually needed. Below is the typical sequence in more detail.
Suppose A
, B
, and C
are all part of the same (eagerly loaded) module, each with its own canActivate
guard and resolve
:
canActivate
guard runs.canActivate
guard runs.canActivate
guard runs./A/B/C
.Note: If there are
canDeactivate
guards, they generally run when you leave a route rather than when you enter. Also, if any guard fails, the router cancels navigation immediately and won’t attempt further steps.
If instead B
is a lazily loaded module (and within it, C
is also a child route):
canActivate
guard runs./B
is a lazy‐loaded module. If you have a canLoad
guard on the lazy‐loaded module, that canLoad
guard runs first to decide whether the module can even be fetched.canLoad
passes (or there is none), Angular loads B’s module.canActivate
guard runs./C
is a child route, then C’s canActivate
guard runs./A/B/C
.Essentially, once you lazy load a module, the router has to fetch its entire routing configuration at runtime (assuming canLoad
permits it). Then, Angular proceeds with the usual guard→resolver sequence for routes inside that newly loaded module.
If C is another lazy-loaded route within B’s module, you would see a similar pattern:
canLoad
+ canActivate
/resolve for B.canLoad
(if present on C’s module),canActivate
,canActivate
guards pass.canLoad
step that decides if the child module can even be fetched.That’s really the main difference: when a route is lazy loaded, the module’s routing configuration (and thus its guards/resolvers) only come into play after Angular has fetched that module—assuming no canLoad
guard prevents it. Otherwise, the order of execution (guard → resolver for parent, then guard → resolver for child) remains the same.
Are you sure that the order is not guard A, guard B, guard C, resolver A, resolver B, resolver C?
In Angular’s router, the guards and resolvers do not all run in one “guards first, resolvers second” sweep. Instead, they run per route segment in a top‐down fashion. Concretely, for a route tree like /A/B/C
:
canActivate
guards run.canActivate
guards run.canActivate
guards run.Only once a route’s guards have passed does the router run that route’s resolvers. Then it proceeds to the child route.
Hence, the actual order is:
A.guard → A.resolver → B.guard → B.resolver → C.guard → C.resolver
rather than:
A.guard → B.guard → C.guard → A.resolver → B.resolver → C.resolver
Lazy Loading Consideration
If B
or C
are lazy‐loaded, there may be an additional canLoad
guard step before the router even fetches their route configuration. Once the module is loaded, the pattern above (guards → resolvers per route level) stays the same.
Ok, but if A/B/C is accessed, why would we first resolve the data for A and B if the G guard denies access?
First, it’s important to clarify that Angular processes each segment of the route tree in order. It does not run all the guards first and then all the resolvers. Instead, it activates each route (i.e., runs its guards and, if they pass, runs its resolvers) before moving to the next child.
So for a route /A/B/C
, Angular does:
A’s guards (e.g. canActivate
).
Move on to B’s guards.
Finally, C’s guards.
You might wonder: “If we know the route eventually fails at C, why bother resolving data for A and B first?”
Short answer:
Concretely:
/A/B/C
, Angular first checks and activates A.At that point, the router has already activated and resolved data for A and B. There is no built-in mechanism that defers parent resolvers until all child guards have been confirmed.
If a parent’s data isn’t actually needed unless you get all the way down to a child route—that’s more of a design question around how the routes and resolvers are organized. The router’s default approach remains “activate the parent first (guard + resolver) before the child even comes into the picture.”
Out of the box, Angular doesn’t have a “look-ahead” mode that tries to evaluate child guards before running parent resolvers. The sequence is top-down. However, you have a few alternatives:
In other words, if it is wasteful to resolve A or B’s data when you know 90% of the time the user might fail at C, you can restructure or unify your guard logic to fail as early as possible and skip unneeded resolvers.