Contents

Angular Enterprise Dashboard - Phase 3A.2: From Route to Component — Signal Inputs & the KPI Dashboard

In the previous post, our resolver pre-fetched the dashboard data and handed it to the Router. But how does the component actually receive it?

Zero-Boilerplate Data Binding

Traditionally, you’d inject ActivatedRoute, subscribe to data, and manually assign values. That’s a lot of ceremony for something that should be simple.

In this post, we’ll see how withComponentInputBinding() and signal input() create a seamless, zero-boilerplate bridge between route data and component state.


This is a one-time configuration in app.config.ts:

provideRouter(
  routes,
  withComponentInputBinding(), // ← This line changes everything
  withViewTransitions(),
),

What it does: It tells Angular’s Router to automatically bind resolved route data, route parameters, and query parameters to component input() properties — by matching the key name.


Remember, our route defined the resolver with the key dashboardData:

resolve: {
  dashboardData: dashboardResolver;
}

In our component, we simply declare a signal input with the same name:

@Component({
  /* ... */
})
export class DashboardComponent {
  /** Resolved data injected automatically via withComponentInputBinding(). */
  readonly dashboardData = input.required<DashboardData>();

  // ...
}

That’s it. No ActivatedRoute. No .subscribe(). No ngOnInit. The data is just there, ready to use — as a Signal.

graph LR
    Resolver["dashboardResolver"] --> RouteData["Route Data: { dashboardData: ... }"]
    RouteData --> Binding["withComponentInputBinding()"]
    Binding --> Input["input.required<DashboardData>()"]
    Input --> Template["Template: dashboardData()"]

With the data available as a signal, our template uses Angular’s modern built-in control flow — @for and @switch — instead of the legacy *ngFor and *ngIf directives.

<section class="kpi-grid">
  @for (metric of dashboardData().metrics; track metric.id) {
  <article class="kpi-card" [class]="'trend-' + metric.trend">
    <!-- ... card content ... -->
  </article>
  }
</section>

Why track metric.id? The track expression tells Angular how to identify each item. When data refreshes, Angular can efficiently update only the cards that changed, instead of tearing down and re-creating the entire list.

Different metrics need different formatting — revenue as currency, conversion as a percentage, etc. We use @switch for clean, readable branching:

<span class="kpi-value">
  @switch (metric.unit) { @case ('currency') { {{ metric.value |
  currency:'USD':'symbol':'1.0-0' }} } @case ('percent') { {{ metric.value |
  number:'1.2-2' }}% } @case ('count') { {{ metric.value | number }} } }
</span>

Each card reuses our glassmorphism design tokens from Phase 2.4:

.kpi-card {
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-lg, 16px);
  transition:
    transform 0.2s ease,
    box-shadow 0.2s ease;
}

.kpi-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

And the trend colors are driven by a dynamic CSS class:

.trend-up .kpi-trend {
  color: #10b981;
} /* Green */
.trend-down .kpi-trend {
  color: #ef4444;
} /* Red */
.trend-stable .kpi-trend {
  color: #94a3b8;
} /* Slate */

FeatureClassic @Input()Signal input()
TypeProperty decoratorFunction returning InputSignal
ReactivityRequires ngOnChangesNatively reactive (it’s a signal!)
Required valuesRequires runtime checksinput.required() — compile-time guarantee
With Router bindingNeeds ActivatedRouteAutomatic with withComponentInputBinding()

Signal inputs aren’t just syntax sugar — they fundamentally change how data flows through your component. Because they are Signals, any computed() or effect() that depends on them will automatically react when the route data changes.


Our dashboard renders with data, looks premium, and uses modern Angular patterns. But we’re missing the final polish. In Phase 3A.3, we’ll add View Transitions for cinematic route changes and enable Incremental Hydration for SSR performance.


Explore the full DashboardComponent in features/dashboard/dashboard.component.ts on GitHub!

Related Content