Lesson 16 of 30
One config array, one `<router-outlet>`, and a route param can now land straight on a signal input — no ActivatedRoute subscription required.
15 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
Angular's router does one job: it looks at the current URL, matches it against a list of routes you declared up front, and decides which component to render where. The "where" is a <router-outlet> placeholder in your template — the router swaps whatever's inside it as the URL changes, without a full page reload.
Everything else — links that don't reload the page, the back button working correctly, route params, guards, lazy chunks — is built on top of that one idea: a declarative table mapping URL shapes to components.
You register routes as a plain array, wire it up once in your app config, then reference the router in templates with two directives: routerLink and routerLinkActive.
// app.routes.ts
import { Routes } from '@angular/router';
import { ProductListComponent } from './products/product-list.component';
import { ProductDetailComponent } from './products/product-detail.component';
export const routes: Routes = [
{ path: '', component: ProductListComponent },
{ path: 'products/:id', component: ProductDetailComponent },
];
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter, withComponentInputBinding } from '@angular/router';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [provideRouter(routes, withComponentInputBinding())],
};
withComponentInputBinding() is the part that trips people up if they learned Angular before it existed. It tells the router: for any route param, query param, or resolved data key that matches an input name on the routed component, bind it there directly — no ActivatedRoute injection, no manual subscription.
// product-detail.component.ts
import { Component, input } from '@angular/core';
@Component({
selector: 'app-product-detail',
standalone: true,
template: `<h2>Product {{ id() }}</h2>`,
})
export class ProductDetailComponent {
id = input.required<string>(); // bound straight from the :id segment
}
Navigate to /products/42 and id() reads '42' — the router matched the route, pulled :id out of the URL, and pushed it onto the signal input, the same mechanism you already use for parent-to-child data.
<!-- app.component.html -->
<nav>
<a routerLink="/" routerLinkActive="active">Home</a>
<a [routerLink]="['/products', product.id]">{{ product.name }}</a>
</nav>
<router-outlet />
routerLink intercepts the click and does an in-app navigation instead of a browser page load. routerLinkActive adds a CSS class when that link's route is currently active — that's how nav bars highlight the current page without you tracking state manually.
{ path: '**', component: NotFoundComponent } has to go last, or it swallows everything after it.routerLink isn't just href. Using a plain <a href="/products"> triggers a full browser navigation and throws away all in-memory app state. routerLink is what keeps navigation client-side.input.required() on a route param can still be empty if the route config is wrong. The binding only fires if the input's name matches the route's param/data key exactly — a typo silently means the input just never receives a value (it'll error at runtime since it's required, but the failure mode isn't obvious from the component alone).If you know React Router, this is the same mental model with an outlet swapped for JSX: a route table maps paths to components, and a <Outlet />-equivalent renders the match. The genuinely new piece here is that Angular's router can push matched route data straight onto a component's signal inputs — the same primitive from the signal inputs lesson, reused as the router's data-delivery mechanism instead of a separate ActivatedRoute API.