Overview
The HeaderComponent provides a consistent top navigation bar across all pages of the application. It displays dynamic page titles based on the current route and includes user information and search functionality.
Component Definition
selector
string
default: "app-header"
The CSS selector used to include this component in templates
This is a standalone component that can be imported directly
Dependencies: CommonModule
Source Code Location
File : src/app/header/header.ts:8
import { Component , inject , signal } from '@angular/core' ;
import { CommonModule } from '@angular/common' ;
import { Router , NavigationEnd , ActivatedRoute } from '@angular/router' ;
import { filter , map , mergeMap } from 'rxjs/operators' ;
import { toSignal } from '@angular/core/rxjs-interop' ;
@ Component ({
selector: 'app-header' ,
standalone: true ,
imports: [ CommonModule ],
templateUrl: './header.html' ,
styleUrl: './header.css'
})
export class HeaderComponent {
private readonly router = inject ( Router );
private readonly activatedRoute = inject ( ActivatedRoute );
public readonly title = toSignal (
this . router . events . pipe (
filter ( event => event instanceof NavigationEnd ),
map (() => this . activatedRoute ),
map ( route => {
while ( route . firstChild ) {
route = route . firstChild ;
}
return route ;
}),
mergeMap ( route => route . data ),
map ( data => data [ 'title' ] || 'Odontologia' )
),
{ initialValue: 'Odontologia' }
);
}
Properties
title
title
Signal<string>
default: "'Odontologia'"
A reactive signal containing the current page title derived from route data
The title property uses Angular’s toSignal() function to convert the router’s event stream into a signal. It:
Listens for NavigationEnd events
Traverses to the deepest child route
Extracts the title from route data
Falls back to “Odontologia” if no title is defined
Implementation : src/app/header/header.ts:18-32
Template Structure
The header is divided into two main sections:
Displays a personalized greeting: “¡Hola, Dr. Admin! 👋”
Shows the dynamic page title using signal interpolation: {{ title() }}
Search input with icon for searching patients and appointments (non-functional in current implementation)
User avatar displaying initials (“AD”) and notification bell icon
Usage
The HeaderComponent is included in the main app layout:
import { Component } from '@angular/core' ;
import { RouterOutlet } from '@angular/router' ;
import { Menu } from './menu/menu' ;
import { HeaderComponent } from './header/header' ;
@ Component ({
selector: 'app-root' ,
imports: [ RouterOutlet , Menu , HeaderComponent ],
templateUrl: './app.html' ,
styleUrl: './app.css'
})
export class App {
// App logic
}
Setting Route Titles
To set the page title for a route, add a title property to the route data:
import { Routes } from '@angular/router' ;
import { Patient } from './patient/patient' ;
import { Appointment } from './appointment/appointment' ;
export const routes : Routes = [
{
path: 'patient' ,
component: Patient ,
data: { title: 'Pacientes' }
},
{
path: 'citas' ,
component: Appointment ,
data: { title: 'Citas' }
}
];
Styling
The header uses scoped CSS with the following structure:
Layout
Typography
Interactive
header-container : Flexbox container with space-between alignment
header-info : Left section with vertical layout
header-actions : Right section with horizontal layout
welcome-msg : Smaller, muted text for greeting
section-title : Large, bold heading for page title
search-bar : Input with icon and border styling
user-avatar : Circular avatar with background color
notification bell : Icon button with hover effects
Dependency Injection Pattern
The component uses Angular’s modern inject() function instead of constructor injection:
private readonly router = inject ( Router );
private readonly activatedRoute = inject ( ActivatedRoute );
This approach is more concise and works seamlessly with standalone components.
RxJS Integration
The component demonstrates reactive programming with RxJS operators:
Filter events
Only process NavigationEnd events using the filter operator
Navigate route tree
Find the deepest child route using map transformations
Extract data
Use mergeMap to access route data observables
Convert to signal
Transform the observable stream into a signal with toSignal
Customization
Change Welcome Message
Edit the welcome message in header.html:3:
< span class = "welcome-msg" > ¡Hola, {{ userName }}! 👋 </ span >
Add User Avatar Logic
Replace static initials with dynamic user data:
export class HeaderComponent {
private readonly userService = inject ( UserService );
protected readonly userInitials = computed (() => {
const user = this . userService . currentUser ();
return user ? user . initials : 'AD' ;
});
}
Implement Search Functionality
Add search logic to the component:
protected searchQuery = signal ( '' );
protected onSearch ( event : Event ): void {
const input = event . target as HTMLInputElement ;
this . searchQuery . set ( input . value );
// Implement search logic
}
Menu Sidebar navigation component
Architecture Main application structure