---
title: "Angular Timeline Header and Footer Template Example | Mobiscroll"
component: "Timeline"
framework: "Angular"
library_version: "6.1.5"
url: "https://demo.mobiscroll.com/angular/timeline/hour-day-week-month-quarter-year-header-footer-template"
---

# Header and Footer Template - Angular Timeline demo

Customize time-scale headers and add occupancy footers to a Angular Timeline — format cells from hour to year and display per-slot utilization.

- [Live demo](https://demo.mobiscroll.com/angular/timeline/hour-day-week-month-quarter-year-header-footer-template)
- [View code on GitHub](https://github.com/acidb/mobiscroll-demos-angular/tree/main/src/demos/eventcalendar/timeline/hour-day-week-month-quarter-year-header-footer-template)
- This demo in other frameworks: [JavaScript](https://demo.mobiscroll.com/javascript/timeline/hour-day-week-month-quarter-year-header-footer-template.md), [jQuery](https://demo.mobiscroll.com/jquery/timeline/hour-day-week-month-quarter-year-header-footer-template.md), [React](https://demo.mobiscroll.com/react/timeline/hour-day-week-month-quarter-year-header-footer-template.md), [Vue](https://demo.mobiscroll.com/vue/timeline/hour-day-week-month-quarter-year-header-footer-template.md)

## Description

The headers hold key information like the date, day of the week and in some cases it also holds the full date. Whenever you need to show extra information, or if you would like to change the styling or date format, time format you can use the various header templates, depending on the view configuration. You can also show a footer element, for displaying more information.

Add summaries, statistics or simply color the background depending on the number events that occur on that specific day, like in this example.

**Check out how you can customize the date header for the scheduler** [Let's go →](https://demo.mobiscroll.com/angular/scheduler/date-header-template)

## What this demo shows

- Shows six timeline configurations with hour, day, week, month, quarter, and year resolutions.
- **Timeline layout** Time periods are arranged horizontally, while resources are listed as rows on the left.
- **Header templates** Each timeline resolution uses a custom header format for its time scale, such as hours, days, weeks, months, quarters, or years.
- **Footer templates** Footer cells display the resource occupancy percentage for each visible time slot.
- **Occupancy calculation** Percentages are calculated from the number of distinct resources occupied by events in each time slot.
- **Density overview** Values such as 25%, 63%, 88%, and 100% highlight how fully booked each time slot is.
- **Header and footer rendering** The demo demonstrates custom rendering for timeline header and footer cells with aggregated scheduling metrics.
- **Dynamic updates** Occupancy values update as events are created, moved, resized, or removed.
- **Event cards** Events are shown as colored cards with a left color stripe, a bold title, and the exact start and end time.
- **Date positioning** Events are positioned by their assigned resource and exact date and time range.
- **Event interaction** Events highlight on hover and show drag and resize handles for repositioning or changing duration.
- **Event selection** Clicking an event selects and highlights it.
- **Event creation** New events can be created by double-clicking the timeline or by clicking and dragging across a time range.
- **Scrolling** The timeline supports horizontal and vertical scrolling for navigating through time and resources.

## Best for

- **Workforce planning** Compare staffing coverage and open capacity across different timeline resolutions.
- **Equipment scheduling** Track when shared equipment is booked and identify time slots with remaining availability.
- **Room booking** Review resource occupancy across rooms while keeping the individual event schedule visible.
- **Asset management** Monitor scheduled usage for multiple resources in a row-based timeline layout.
- **Capacity analysis** Show high-level utilization metrics without hiding the detailed events for each resource.
- **Custom scheduling dashboards** Add summary values, occupancy indicators, or other aggregated metrics to timeline headers and footers.

## Tutorial

### Hourly header/footer template

With an hourly (or sub-hourly) resolution the [`hourTemplate`](https://mobiscroll.com/docs/angular/eventcalendar/timeline#template-hourTemplate) and [`hourFooterTemplate`](https://mobiscroll.com/docs/angular/eventcalendar/timeline#template-hourFooterTemplate) options can be used for customizing the header and footer.

```html
<mbsc-eventcalendar
    [view]="hourView"
    [hourTemplate]="hourTemp"
    [hourFooterTemplate]="hourFooterTemp">
    <ng-template #hourTemp let-day>
        <div [ngClass]="'md-date-header-hour md-date-header-events-' + getEventOccurrence(day)">
            {{formatDate('h:mm A', day.date)}}
        </div>
    </ng-template>
    <ng-template #hourFooterTemp let-day>
        <div class="md-date-footer">{{getOccuppancy(day.events)}} %</div>
    </ng-template>
</mbsc-eventcalendar>
```

```ts
hourView: MbscEventcalendarView = {
    timeline: {
        type: 'day'
    }
};
                            
getOccuppancy(events: any) {
    let occuppancy = 0;
    if (events) {
        var resourceIds = [];
        var nr = 0;
        for (const event of events) {
            if (resourceIds.indexOf(event.resource) < 0) {
                nr++;
                resourceIds = [...resourceIds, event.resource];
            }
        }
        occuppancy = (nr * 100 / this.myResources.length).toFixed(0);
    }
    return occuppancy;
}

getEventOccurrence(args: any): any {
    let eventOccurrence = 'none';

    if (args.events) {
        var eventNr = args.events.length;
        if (eventNr === 0) {
            eventOccurrence = 'none';
        } else if (eventNr === 1) {
            eventOccurrence = 'one';
        } else if (eventNr < 4) {
            eventOccurrence = 'few';
        } else {
            eventOccurrence = 'more';
        }
    }
    return eventOccurrence;
}
```

```css
.md-date-header-hour {
    font-size: 12px;
    font-weight: 600;
    text-align: center;
    line-height: 20px;
    border-radius: 8px;
    margin: 3px;
    color: #000;
}

.md-date-header-events-one {
	background: #e9f5e4;
}

.md-date-header-events-few {
	background: #d0e3c6;
}

.md-date-header-events-more {
	background: #8ca77e;
}

.md-date-footer {
	font-size: 14px;
	font-weight: 600;
	text-align: center;
	line-height: 26px;
}
```

### Daily header/footer template

With a daily resolution, the [`timelineDayTemplate`](https://mobiscroll.com/docs/angular/eventcalendar/timeline#template-timelineDayTemplate) and [`dayFooterTemplate`](https://mobiscroll.com/docs/angular/eventcalendar/timeline#template-dayFooterTemplate) options can come in handy for customizing the header and footer.

```html
<mbsc-eventcalendar
    [view]="dayView"
    [timelineDayTemplate]="dayTemp"
    [dayFooterTemplate]="dayFooterTemp">
    <ng-template #dayTemp let-day>
        <div [ngClass]="'md-date-header-day md-date-header-events-' + getEventOccurrence(day)">
            <div class="md-date-header-day-name">
                {{formatDate('DDD', day.date)}}
            </div>
            <div class="md-date-header-day-nr">
                {{formatDate('DD', day.date)}}
            </div>
        </div>
    </ng-template>
    <ng-template #dayFooterTemp let-day>
        <div class="md-date-footer">{{getOccuppancy(day.events)}} %</div>
    </ng-template>
</mbsc-eventcalendar>
```

```ts
dayView: MbscEventcalendarView = {
    timeline: {
        type: 'month'
    }
};
                            
getOccuppancy(events: any) {
    let occuppancy = 0;
    if (events) {
        var resourceIds = [];
        var nr = 0;
        for (const event of events) {
            if (resourceIds.indexOf(event.resource) < 0) {
                nr++;
                resourceIds = [...resourceIds, event.resource];
            }
        }
        occuppancy = (nr * 100 / this.myResources.length).toFixed(0);
    }
    return occuppancy;
}

getEventOccurrence(args: any): any {
    let eventOccurrence = 'none';

    if (args.events) {
        var eventNr = args.events.length;
        if (eventNr === 0) {
            eventOccurrence = 'none';
        } else if (eventNr === 1) {
            eventOccurrence = 'one';
        } else if (eventNr < 4) {
            eventOccurrence = 'few';
        } else {
            eventOccurrence = 'more';
        }
    }
    return eventOccurrence;
}
```

```css
.md-date-header-day {
    text-align: center;
    line-height: 20px;
    border-radius: 8px;
    margin: 4px;
}

.md-date-header-day-name {
    font-size: 13px;
    font-weight: 600;
}

.md-date-header-day-nr {
    font-size: 14px;
}

.md-date-header-events-one {
	background: #e9f5e4;
}

.md-date-header-events-few {
	background: #d0e3c6;
}

.md-date-header-events-more {
	background: #8ca77e;
}

.md-date-footer {
	font-size: 14px;
	font-weight: 600;
	text-align: center;
	line-height: 26px;
}
```

### Weekly header/footer template

When the resolution is weekly, the [`weekTemplate`](https://mobiscroll.com/docs/angular/eventcalendar/timeline#template-weekTemplate) and [`weekFooterTemplate`](https://mobiscroll.com/docs/angular/eventcalendar/timeline#template-weekFooterTemplate) options can be used.

```html
<mbsc-eventcalendar
    [view]="weekView"
    [weekTemplate]="weekTemp"
    [weekFooterTemplate]="weekFooterTemp">
    <ng-template #weekTemp let-day>
        <div [ngClass]="'md-date-header-week md-date-header-events-' + getEventOccurrence(day)">
            {{formatDate('MMM DD', day.startDate)}} - {{formatDate('MMM DD', args.endDate)}}
        </div>
    </ng-template>
    <ng-template #weekFooterTemp let-day>
        <div class="md-date-footer">{{getOccuppancy(day.events)}} %</div>
    </ng-template>
</mbsc-eventcalendar>
```

```ts
weekView: MbscEventcalendarView = {
    timeline: {
        type: 'week',
        resolutionHorizontal: 'week',
        size: 6
    }
};
                            
getOccuppancy(events: any) {
    let occuppancy = 0;
    if (events) {
        var resourceIds = [];
        var nr = 0;
        for (const event of events) {
            if (resourceIds.indexOf(event.resource) < 0) {
                nr++;
                resourceIds = [...resourceIds, event.resource];
            }
        }
        occuppancy = (nr * 100 / this.myResources.length).toFixed(0);
    }
    return occuppancy;
}

getEventOccurrence(args: any): any {
    let eventOccurrence = 'none';

    if (args.events) {
        var eventNr = args.events.length;
        if (eventNr === 0) {
            eventOccurrence = 'none';
        } else if (eventNr === 1) {
            eventOccurrence = 'one';
        } else if (eventNr < 4) {
            eventOccurrence = 'few';
        } else {
            eventOccurrence = 'more';
        }
    }
    return eventOccurrence;
}
```

```css
.md-date-header-template .mbsc-timeline-header-week-text {
    display: block;
}

.md-date-header-template .mbsc-timeline-footer-week {
    text-align: center;
}

.md-date-header-week {
	text-align: center;
	font-weight: 600;
	color: initial;
	line-height: 20px;
	border-radius: 8px;
	margin: 3px;
}

.md-date-header-events-one {
	background: #e9f5e4;
}

.md-date-header-events-few {
	background: #d0e3c6;
}

.md-date-header-events-more {
	background: #8ca77e;
}

.md-date-footer {
	font-size: 14px;
	font-weight: 600;
	text-align: center;
	line-height: 26px;
}
```

### Monthly header/footer template

When displaying multiple months, the [`monthTemplate`](https://mobiscroll.com/docs/angular/eventcalendar/timeline#template-monthTemplate) and [`monthFooterTemplate`](https://mobiscroll.com/docs/angular/eventcalendar/timeline#template-monthFooterTemplate) options can be used for customizing the header and footer per month.

```html
<mbsc-eventcalendar
    [view]="monthView"
    [monthTemplate]="monthTemp"
    [monthFooterTemplate]="monthFooterTemp">
    <ng-template #monthTemp let-day>
        <div [ngClass]="'md-date-header-month md-date-header-events-' + getEventOccurrence(day)">
            {{formatDate('MMM', day.date)}}
        </div>
    </ng-template>
    <ng-template #monthFooterTemp let-day>
        <div class="md-date-footer">{{getOccuppancy(day.events)}} %</div>
    </ng-template>
</mbsc-eventcalendar>
```

```ts
monthView: MbscEventcalendarView = {
    timeline: {
        type: 'month',
        resolutionHorizontal: 'month',
        size: 6
    }
};
                            
getOccuppancy(events: any) {
    let occuppancy = 0;
    if (events) {
        var resourceIds = [];
        var nr = 0;
        for (const event of events) {
            if (resourceIds.indexOf(event.resource) < 0) {
                nr++;
                resourceIds = [...resourceIds, event.resource];
            }
        }
        occuppancy = (nr * 100 / this.myResources.length).toFixed(0);
    }
    return occuppancy;
}

getEventOccurrence(args: any): any {
    let eventOccurrence = 'none';

    if (args.events) {
        var eventNr = args.events.length;
        if (eventNr === 0) {
            eventOccurrence = 'none';
        } else if (eventNr === 1) {
            eventOccurrence = 'one';
        } else if (eventNr < 4) {
            eventOccurrence = 'few';
        } else {
            eventOccurrence = 'more';
        }
    }
    return eventOccurrence;
}
```

```css
.md-date-header-month {
	text-align: center;
	font-weight: 600;
	color: initial;
	line-height: 20px;
	border-radius: 8px;
	margin: 3px;
}

.md-date-header-events-one {
	background: #e9f5e4;
}

.md-date-header-events-few {
	background: #d0e3c6;
}

.md-date-header-events-more {
	background: #8ca77e;
}

.md-date-footer {
	font-size: 14px;
	font-weight: 600;
	text-align: center;
	line-height: 26px;
}
```

### Quarter header/footer template

When displaying multiple quarters, the [`quarterTemplate`](https://mobiscroll.com/docs/angular/eventcalendar/timeline#template-quarterTemplate) and [`quarterFooterTemplate`](https://mobiscroll.com/docs/angular/eventcalendar/timeline#template-quarterFooterTemplate) options can be used for customizing the header and footer per quarter.

```html
<mbsc-eventcalendar
    [view]="quarterView"
    [quarterTemplate]="quarterTemp"
    [quarterFooterTemplate]="quarterFooterTemp">
    <ng-template #quarterTemp let-day>
        <div [ngClass]="'md-date-header-quarter md-date-header-events-' + getEventOccurrence(day)">
            Quarter {{ (day.date.getMonth() / 3 + 1) }}
        </div>
    </ng-template>
    <ng-template #quarterFooterTemp let-day>
        <div class="md-date-footer">{{getOccuppancy(day.events)}} %</div>
    </ng-template>
</mbsc-eventcalendar>
```

```ts
quarterView: MbscEventcalendarView = {
    timeline: {
        type: 'year',
        resolutionHorizontal: 'quarter',
        size: 1
    }
};
                            
getOccuppancy(events: any) {
    let occuppancy = 0;
    if (events) {
        var resourceIds = [];
        var nr = 0;
        for (const event of events) {
            if (resourceIds.indexOf(event.resource) < 0) {
                nr++;
                resourceIds = [...resourceIds, event.resource];
            }
        }
        occuppancy = (nr * 100 / this.myResources.length).toFixed(0);
    }
    return occuppancy;
}

getEventOccurrence(args: any): any {
    let eventOccurrence = 'none';

    if (args.events) {
        var eventNr = args.events.length;
        if (eventNr === 0) {
            eventOccurrence = 'none';
        } else if (eventNr === 1) {
            eventOccurrence = 'one';
        } else if (eventNr < 4) {
            eventOccurrence = 'few';
        } else {
            eventOccurrence = 'more';
        }
    }
    return eventOccurrence;
}
```

```css
.md-date-header-quarter {
	text-align: center;
	font-weight: 600;
	color: initial;
	line-height: 20px;
	border-radius: 8px;
	margin: 3px;
}

.md-date-header-events-one {
	background: #e9f5e4;
}

.md-date-header-events-few {
	background: #d0e3c6;
}

.md-date-header-events-more {
	background: #8ca77e;
}

.md-date-footer {
	font-size: 14px;
	font-weight: 600;
	text-align: center;
	line-height: 26px;
}
```

### Yearly header/footer template

With a yearly resolution, the [`yearTemplate`](https://mobiscroll.com/docs/angular/eventcalendar/timeline#template-yearTemplate) and [`yearFooterTemplate`](https://mobiscroll.com/docs/angular/eventcalendar/timeline#template-yearFooterTemplate) options can be used for customizing the header and footer.

```html
<mbsc-eventcalendar
    [view]="yearView"
    [yearTemplate]="yearTemp"
    [yearFooterTemplate]="yearFooterTemp">
    <ng-template #yearTemp let-day>
        <div [ngClass]="'md-date-header-year md-date-header-events-' + getEventOccurrence(day)">
            {{formatDate('YYYY', day.date)}}
        </div>
    </ng-template>
    <ng-template #yearFooterTemp let-day>
        <div class="md-date-footer">{{getOccuppancy(day.events)}} %</div>
    </ng-template>
</mbsc-eventcalendar>
```

```ts
yearView: MbscEventcalendarView = {
    timeline: {
        type: 'year',
        resolutionHorizontal: 'year',
        size: 6
    }
};
                            
getOccuppancy(events: any) {
    let occuppancy = 0;
    if (events) {
        var resourceIds = [];
        var nr = 0;
        for (const event of events) {
            if (resourceIds.indexOf(event.resource) < 0) {
                nr++;
                resourceIds = [...resourceIds, event.resource];
            }
        }
        occuppancy = (nr * 100 / this.myResources.length).toFixed(0);
    }
    return occuppancy;
}

getEventOccurrence(args: any): any {
    let eventOccurrence = 'none';

    if (args.events) {
        var eventNr = args.events.length;
        if (eventNr === 0) {
            eventOccurrence = 'none';
        } else if (eventNr === 1) {
            eventOccurrence = 'one';
        } else if (eventNr < 4) {
            eventOccurrence = 'few';
        } else {
            eventOccurrence = 'more';
        }
    }
    return eventOccurrence;
}
```

```css
.md-date-header-year {
	text-align: center;
	font-weight: 600;
	color: initial;
	line-height: 20px;
	border-radius: 8px;
	margin: 3px;
}

.md-date-header-events-one {
	background: #e9f5e4;
}

.md-date-header-events-few {
	background: #d0e3c6;
}

.md-date-header-events-more {
	background: #8ca77e;
}

.md-date-footer {
	font-size: 14px;
	font-weight: 600;
	text-align: center;
	line-height: 26px;
}
```

## API used in this demo

- [`theme`](https://mobiscroll.com/docs/angular/eventcalendar/api#opt-theme) - option
- [`themeVariant`](https://mobiscroll.com/docs/angular/eventcalendar/api#opt-themeVariant) - option
- [`view`](https://mobiscroll.com/docs/angular/eventcalendar/api#opt-view) - option
- [`data`](https://mobiscroll.com/docs/angular/eventcalendar/api#opt-data) - option
- [`resources`](https://mobiscroll.com/docs/angular/eventcalendar/api#opt-resources) - option
- [`hourTemplate`](https://mobiscroll.com/docs/angular/eventcalendar/api#template-hourTemplate) - template
- [`hourFooterTemplate`](https://mobiscroll.com/docs/angular/eventcalendar/api#template-hourFooterTemplate) - template
- [`timelineDayTemplate`](https://mobiscroll.com/docs/angular/eventcalendar/api#template-timelineDayTemplate) - template
- [`dayFooterTemplate`](https://mobiscroll.com/docs/angular/eventcalendar/api#template-dayFooterTemplate) - template
- [`weekTemplate`](https://mobiscroll.com/docs/angular/eventcalendar/api#template-weekTemplate) - template
- [`weekFooterTemplate`](https://mobiscroll.com/docs/angular/eventcalendar/api#template-weekFooterTemplate) - template
- [`monthTemplate`](https://mobiscroll.com/docs/angular/eventcalendar/api#template-monthTemplate) - template
- [`monthFooterTemplate`](https://mobiscroll.com/docs/angular/eventcalendar/api#template-monthFooterTemplate) - template
- [`quarterTemplate`](https://mobiscroll.com/docs/angular/eventcalendar/api#template-quarterTemplate) - template
- [`quarterFooterTemplate`](https://mobiscroll.com/docs/angular/eventcalendar/api#template-quarterFooterTemplate) - template
- [`yearTemplate`](https://mobiscroll.com/docs/angular/eventcalendar/api#template-yearTemplate) - template
- [`yearFooterTemplate`](https://mobiscroll.com/docs/angular/eventcalendar/api#template-yearFooterTemplate) - template

## Code

### Component

```ts
import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import {
  formatDate,
  MbscCalendarEvent,
  MbscEventcalendarView,
  MbscModule,
  MbscResource,
  setOptions,
} from '@mobiscroll/angular';

setOptions({
  theme: 'ios',
  themeVariant: 'light'
});

@Component({
  selector: 'app-root', 
  templateUrl: './app.component.html',
  standalone: true,
  imports: [CommonModule, MbscModule],
})
export class AppComponent {
  hourView: MbscEventcalendarView = {
    timeline: {
      type: 'day',
    },
  };

  hourlyEvents: MbscCalendarEvent[] = [
    {
      id: 1,
      start: '2026-10-28T02:00',
      end: '2026-10-28T10:00',
      title: 'Event 1',
      resource: 1,
    },
    {
      id: 2,
      start: '2026-10-28T15:00',
      end: '2026-10-28T21:00',
      title: 'Event 2',
      resource: 1,
    },
    {
      id: 3,
      start: '2026-10-28T08:00',
      end: '2026-10-28T15:00',
      title: 'Event 3',
      resource: 2,
    },
    {
      id: 4,
      start: '2026-10-28T05:00',
      end: '2026-10-28T13:00',
      title: 'Event 4',
      resource: 3,
    },
    {
      id: 5,
      start: '2026-10-28T03:00',
      end: '2026-10-28T10:00',
      title: 'Event 5',
      resource: 4,
    },
    {
      id: 6,
      start: '2026-10-28T16:00',
      end: '2026-10-28T23:00',
      title: 'Event 6',
      resource: 4,
    },
    {
      id: 7,
      start: '2026-10-28T06:00',
      end: '2026-10-28T11:00',
      title: 'Event 7',
      resource: 5,
    },
    {
      id: 8,
      start: '2026-10-28T15:00',
      end: '2026-10-28T22:00',
      title: 'Event 8',
      resource: 5,
    },
    {
      id: 9,
      start: '2026-10-28T08:00',
      end: '2026-10-28T17:00',
      title: 'Event 9',
      resource: 6,
    },
    {
      id: 10,
      start: '2026-10-28T05:00',
      end: '2026-10-28T13:00',
      title: 'Event 10',
      resource: 7,
    },
    {
      id: 11,
      start: '2026-10-28T16:00',
      end: '2026-10-28T21:00',
      title: 'Event 11',
      resource: 7,
    },
    {
      id: 12,
      start: '2026-10-28T09:00',
      end: '2026-10-28T18:00',
      title: 'Event 12',
      resource: 8,
    },
  ];

  dayView: MbscEventcalendarView = {
    timeline: {
      type: 'month',
    },
  };

  dailyEvents: MbscCalendarEvent[] = [
    {
      id: 1,
      start: '2026-10-02T00:00',
      end: '2026-10-10T00:00',
      title: 'Event 1',
      resource: 1,
    },
    {
      id: 2,
      start: '2026-10-16T00:00',
      end: '2026-10-24T00:00',
      title: 'Event 2',
      resource: 1,
    },
    {
      id: 3,
      start: '2026-10-08T00:00',
      end: '2026-10-15T00:00',
      title: 'Event 3',
      resource: 2,
    },
    {
      id: 4,
      start: '2026-10-04T00:00',
      end: '2026-10-11T00:00',
      title: 'Event 4',
      resource: 3,
    },
    {
      id: 5,
      start: '2026-10-07T00:00',
      end: '2026-10-15T00:00',
      title: 'Event 5',
      resource: 4,
    },
    {
      id: 6,
      start: '2026-10-21T00:00',
      end: '2026-10-27T00:00',
      title: 'Event 6',
      resource: 4,
    },
    {
      id: 7,
      start: '2026-10-10T00:00',
      end: '2026-10-19T00:00',
      title: 'Event 7',
      resource: 5,
    },
    {
      id: 8,
      start: '2026-10-24T00:00',
      end: '2026-10-30T00:00',
      title: 'Event 8',
      resource: 5,
    },
    {
      id: 9,
      start: '2026-10-08T00:00',
      end: '2026-10-17T00:00',
      title: 'Event 9',
      resource: 6,
    },
    {
      id: 10,
      start: '2026-10-05T00:00',
      end: '2026-10-13T00:00',
      title: 'Event 10',
      resource: 7,
    },
    {
      id: 11,
      start: '2026-10-17T00:00',
      end: '2026-10-26T00:00',
      title: 'Event 11',
      resource: 7,
    },
    {
      id: 12,
      start: '2026-10-09T00:00',
      end: '2026-10-20T00:00',
      title: 'Event 12',
      resource: 8,
    },
  ];

  weekView: MbscEventcalendarView = {
    timeline: {
      type: 'week',
      resolutionHorizontal: 'week',
      size: 6,
    },
  };

  weeklyEvents: MbscCalendarEvent[] = [
    {
      id: 1,
      start: '2026-10-10T00:00',
      end: '2026-10-24T00:00',
      title: 'Event 1',
      resource: 1,
    },
    {
      id: 2,
      start: '2026-11-02T00:00',
      end: '2026-11-18T00:00',
      title: 'Event 2',
      resource: 1,
    },
    {
      id: 3,
      start: '2026-10-27T00:00',
      end: '2026-11-13T00:00',
      title: 'Event 3',
      resource: 2,
    },
    {
      id: 4,
      start: '2026-11-04T00:00',
      end: '2026-11-27T00:00',
      title: 'Event 4',
      resource: 3,
    },
    {
      id: 5,
      start: '2026-10-24T00:00',
      end: '2026-11-02T00:00',
      title: 'Event 5',
      resource: 4,
    },
    {
      id: 6,
      start: '2026-11-10T00:00',
      end: '2026-11-24T00:00',
      title: 'Event 6',
      resource: 4,
    },
    {
      id: 7,
      start: '2026-10-20T00:00',
      end: '2026-11-03T00:00',
      title: 'Event 7',
      resource: 5,
    },
    {
      id: 8,
      start: '2026-11-08T00:00',
      end: '2026-11-19T00:00',
      title: 'Event 8',
      resource: 5,
    },
    {
      id: 9,
      start: '2026-10-28T00:00',
      end: '2026-11-15T00:00',
      title: 'Event 9',
      resource: 6,
    },
    {
      id: 10,
      start: '2026-10-09T00:00',
      end: '2026-10-23T00:00',
      title: 'Event 10',
      resource: 7,
    },
    {
      id: 11,
      start: '2026-11-05T00:00',
      end: '2026-11-22T00:00',
      title: 'Event 11',
      resource: 7,
    },
    {
      id: 12,
      start: '2026-10-24T00:00',
      end: '2026-11-13T00:00',
      title: 'Event 12',
      resource: 8,
    },
  ];

  monthView: MbscEventcalendarView = {
    timeline: {
      type: 'month',
      resolutionHorizontal: 'month',
      size: 6,
    },
  };

  monthlyEvents: MbscCalendarEvent[] = [
    {
      id: 1,
      start: '2026-09-10T00:00',
      end: '2026-11-07T00:00',
      title: 'Event 1',
      resource: 1,
    },
    {
      id: 2,
      start: '2027-01-01T00:00',
      end: '2027-02-08T00:00',
      title: 'Event 2',
      resource: 1,
    },
    {
      id: 3,
      start: '2026-11-27T00:00',
      end: '2026-12-23T00:00',
      title: 'Event 3',
      resource: 2,
    },
    {
      id: 4,
      start: '2026-10-25T00:00',
      end: '2026-11-19T00:00',
      title: 'Event 4',
      resource: 3,
    },
    {
      id: 5,
      start: '2026-11-10T00:00',
      end: '2026-12-18T00:00',
      title: 'Event 5',
      resource: 4,
    },
    {
      id: 6,
      start: '2027-02-24T00:00',
      end: '2027-03-27T00:00',
      title: 'Event 6',
      resource: 4,
    },
    {
      id: 7,
      start: '2026-12-02T00:00',
      end: '2027-01-13T00:00',
      title: 'Event 7',
      resource: 5,
    },
    {
      id: 8,
      start: '2027-02-08T00:00',
      end: '2027-03-06T00:00',
      title: 'Event 8',
      resource: 5,
    },
    {
      id: 9,
      start: '2026-12-20T00:00',
      end: '2027-01-17T00:00',
      title: 'Event 9',
      resource: 6,
    },
    {
      id: 10,
      start: '2026-10-01T00:00',
      end: '2026-11-14T00:00',
      title: 'Event 10',
      resource: 7,
    },
    {
      id: 11,
      start: '2027-02-14T00:00',
      end: '2027-03-20T00:00',
      title: 'Event 11',
      resource: 7,
    },
    {
      id: 12,
      start: '2026-11-24T00:00',
      end: '2026-12-20T00:00',
      title: 'Event 12',
      resource: 8,
    },
  ];

  quarterView: MbscEventcalendarView = {
    timeline: {
      type: 'year',
      resolutionHorizontal: 'quarter',
      size: 1,
    },
  };

  quarterEvents: MbscCalendarEvent[] = [
    {
      id: 1,
      start: '2026-09-10T00:00',
      end: '2026-11-07T00:00',
      title: 'Event 1',
      resource: 1,
    },
    {
      id: 2,
      start: '2027-01-01T00:00',
      end: '2027-02-08T00:00',
      title: 'Event 2',
      resource: 1,
    },
    {
      id: 3,
      start: '2026-11-27T00:00',
      end: '2026-12-23T00:00',
      title: 'Event 3',
      resource: 2,
    },
    {
      id: 4,
      start: '2026-10-25T00:00',
      end: '2026-11-19T00:00',
      title: 'Event 4',
      resource: 3,
    },
    {
      id: 5,
      start: '2026-11-10T00:00',
      end: '2026-12-18T00:00',
      title: 'Event 5',
      resource: 4,
    },
    {
      id: 6,
      start: '2027-02-24T00:00',
      end: '2027-03-27T00:00',
      title: 'Event 6',
      resource: 4,
    },
    {
      id: 7,
      start: '2026-12-02T00:00',
      end: '2027-01-13T00:00',
      title: 'Event 7',
      resource: 5,
    },
    {
      id: 8,
      start: '2027-02-08T00:00',
      end: '2027-03-06T00:00',
      title: 'Event 8',
      resource: 5,
    },
    {
      id: 9,
      start: '2026-12-20T00:00',
      end: '2027-01-17T00:00',
      title: 'Event 9',
      resource: 6,
    },
    {
      id: 10,
      start: '2026-10-01T00:00',
      end: '2026-11-14T00:00',
      title: 'Event 10',
      resource: 7,
    },
    {
      id: 11,
      start: '2027-02-14T00:00',
      end: '2027-03-20T00:00',
      title: 'Event 11',
      resource: 7,
    },
    {
      id: 12,
      start: '2026-11-24T00:00',
      end: '2026-12-20T00:00',
      title: 'Event 12',
      resource: 8,
    },
  ];

  yearView: MbscEventcalendarView = {
    timeline: {
      type: 'year',
      resolutionHorizontal: 'year',
      size: 6,
    },
  };

  yearlyEvents: MbscCalendarEvent[] = [
    {
      id: 1,
      start: '2027-04-10T00:00',
      end: '2028-08-07T00:00',
      title: 'Event 1',
      resource: 1,
    },
    {
      id: 2,
      start: '2029-06-01T00:00',
      end: '2030-09-08T00:00',
      title: 'Event 2',
      resource: 1,
    },
    {
      id: 3,
      start: '2028-03-27T00:00',
      end: '2029-10-23T00:00',
      title: 'Event 3',
      resource: 2,
    },
    {
      id: 4,
      start: '2028-08-25T00:00',
      end: '2029-11-19T00:00',
      title: 'Event 4',
      resource: 3,
    },
    {
      id: 5,
      start: '2027-09-10T00:00',
      end: '2029-04-18T00:00',
      title: 'Event 5',
      resource: 4,
    },
    {
      id: 6,
      start: '2030-01-24T00:00',
      end: '2031-05-27T00:00',
      title: 'Event 6',
      resource: 4,
    },
    {
      id: 7,
      start: '2025-04-02T00:00',
      end: '2026-08-13T00:00',
      title: 'Event 7',
      resource: 5,
    },
    {
      id: 8,
      start: '2028-04-08T00:00',
      end: '2029-11-06T00:00',
      title: 'Event 8',
      resource: 5,
    },
    {
      id: 9,
      start: '2028-01-20T00:00',
      end: '2029-08-17T00:00',
      title: 'Event 9',
      resource: 6,
    },
    {
      id: 10,
      start: '2026-10-01T00:00',
      end: '2027-11-14T00:00',
      title: 'Event 10',
      resource: 7,
    },
    {
      id: 11,
      start: '2029-10-14T00:00',
      end: '2030-11-20T00:00',
      title: 'Event 11',
      resource: 7,
    },
    {
      id: 12,
      start: '2029-02-24T00:00',
      end: '2031-04-20T00:00',
      title: 'Event 12',
      resource: 8,
    },
  ];

  myResources: MbscResource[] = [
    {
      id: 1,
      name: 'Resource A',
      color: '#e20000',
    },
    {
      id: 2,
      name: 'Resource B',
      color: '#d6d145',
    },
    {
      id: 3,
      name: 'Resource C',
      color: '#4981d6',
    },
    {
      id: 4,
      name: 'Resource D',
      color: '#e25dd2',
    },
    {
      id: 5,
      name: 'Resource E',
      color: '#1dab2f',
    },
    {
      id: 6,
      name: 'Resource F',
      color: '#430404',
    },
    {
      id: 7,
      name: 'Resource G',
      color: '#f7961e',
    },
    {
      id: 8,
      name: 'Resource H',
      color: '#34c8e0',
    },
  ];

  formatDate = formatDate;

  getOccuppancy(events: MbscCalendarEvent[]) {
    let occuppancy = 0;
    if (events) {
      let resourceIds: string[] = [];
      let nr = 0;
      for (const event of events) {
        const resource = event.resource as string;
        if (resourceIds.indexOf(resource) < 0) {
          nr++;
          resourceIds = [...resourceIds, resource];
        }
      }
      occuppancy = (nr * 100) / this.myResources.length;
    }
    return occuppancy.toFixed(0);
  }

  getEventOccurrence(args: any): any {
    let eventOccurrence = 'none';

    if (args.events) {
      const eventNr = args.events.length;
      if (eventNr === 0) {
        eventOccurrence = 'none';
      } else if (eventNr === 1) {
        eventOccurrence = 'one';
      } else if (eventNr < 4) {
        eventOccurrence = 'few';
      } else {
        eventOccurrence = 'more';
      }
    }
    return eventOccurrence;
  }
}
```

### Template

```html
<div class="md-date-header-template">
  <div class="mbsc-form-group">
    <div class="mbsc-form-group-title">Hourly header template</div>
    <mbsc-eventcalendar
      [view]="hourView"
      [data]="hourlyEvents"
      [resources]="myResources"
      [hourTemplate]="hourTemp"
      [hourFooterTemplate]="hourFooterTemp"
    >
      <ng-template #hourTemp let-day>
        <div [ngClass]="'md-date-header md-date-header-hour md-date-header-events-' + getEventOccurrence(day)">
          {{formatDate('h:mm A', day.date)}}
        </div>
      </ng-template>
      <ng-template #hourFooterTemp let-day>
        <div class="md-date-footer">{{getOccuppancy(day.events)}} %</div>
      </ng-template>
    </mbsc-eventcalendar>
  </div>
  <div class="mbsc-form-group">
    <div class="mbsc-form-group-title">Daily header template</div>
    <mbsc-eventcalendar
      [view]="dayView"
      [data]="dailyEvents"
      [resources]="myResources"
      [timelineDayTemplate]="dayTemp"
      [dayFooterTemplate]="dayFooterTemp"
    >
      <ng-template #dayTemp let-day>
        <div [ngClass]="'md-date-header md-date-header-day md-date-header-events-' + getEventOccurrence(day)">
          <div class="md-date-header-day-name">{{formatDate('DDD', day.date)}}</div>
          <div class="md-date-header-day-nr">{{formatDate('DD', day.date)}}</div>
        </div>
      </ng-template>
      <ng-template #dayFooterTemp let-day>
        <div class="md-date-footer">{{getOccuppancy(day.events)}} %</div>
      </ng-template>
    </mbsc-eventcalendar>
  </div>
  <div class="mbsc-form-group">
    <div class="mbsc-form-group-title">Weekly header template</div>
    <mbsc-eventcalendar
      [view]="weekView"
      [data]="weeklyEvents"
      [resources]="myResources"
      [weekTemplate]="weekTemp"
      [weekFooterTemplate]="weekFooterTemp"
    >
      <ng-template #weekTemp let-day>
        <div [ngClass]="'md-date-header md-date-header-week md-date-header-events-' + getEventOccurrence(day)">
          {{formatDate('MMM DD', day.startDate)}} - {{formatDate('MMM DD', day.endDate)}}
        </div>
      </ng-template>
      <ng-template #weekFooterTemp let-day>
        <div class="md-date-footer">{{getOccuppancy(day.events)}} %</div>
      </ng-template>
    </mbsc-eventcalendar>
  </div>
  <div class="mbsc-form-group">
    <div class="mbsc-form-group-title">Monthly header template</div>
    <mbsc-eventcalendar
      [view]="monthView"
      [data]="monthlyEvents"
      [resources]="myResources"
      [monthTemplate]="monthTemp"
      [monthFooterTemplate]="monthFooterTemp"
    >
      <ng-template #monthTemp let-day>
        <div [ngClass]="'md-date-header md-date-header-month md-date-header-events-' + getEventOccurrence(day)">
          {{formatDate('MMM', day.date)}}
        </div>
      </ng-template>
      <ng-template #monthFooterTemp let-day>
        <div class="md-date-footer">{{getOccuppancy(day.events)}} %</div>
      </ng-template>
    </mbsc-eventcalendar>
  </div>
  <div class="mbsc-form-group">
    <div class="mbsc-form-group-title">Quarterly header template</div>
    <mbsc-eventcalendar
      [view]="quarterView"
      [data]="quarterEvents"
      [resources]="myResources"
      [quarterTemplate]="quarterTemp"
      [quarterFooterTemplate]="quarterFooterTemp"
    >
      <ng-template #quarterTemp let-day>
        <div [ngClass]="'md-date-header md-date-header-quarter md-date-header-events-' + getEventOccurrence(day)">
          Quarter {{ (day.date.getMonth() / 3 + 1) }}
        </div>
      </ng-template>
      <ng-template #quarterFooterTemp let-day>
        <div class="md-date-footer">{{getOccuppancy(day.events)}} %</div>
      </ng-template>
    </mbsc-eventcalendar>
  </div>
  <div class="mbsc-form-group">
    <div class="mbsc-form-group-title">Yearly header template</div>
    <mbsc-eventcalendar
      [view]="yearView"
      [data]="yearlyEvents"
      [resources]="myResources"
      [yearTemplate]="yearTemp"
      [yearFooterTemplate]="yearFooterTemp"
    >
      <ng-template #yearTemp let-day>
        <div [ngClass]="'md-date-header md-date-header-year md-date-header-events-' + getEventOccurrence(day)">
          {{formatDate('YYYY', day.date)}}
        </div>
      </ng-template>
      <ng-template #yearFooterTemp let-day>
        <div class="md-date-footer">{{getOccuppancy(day.events)}} %</div>
      </ng-template>
    </mbsc-eventcalendar>
  </div>
</div>
```

### CSS

```css
.md-date-header-template .mbsc-timeline-header-week-text {
  display: block;
}

.md-date-header {
  text-align: center;
  border-radius: 8px;
  line-height: 20px;
}

.md-date-header-day {
  padding: 3px;
  margin: 4px;
}

.md-date-header-hour {
  font-size: 12px;
  padding: 0;
  margin: 3px;
  color: #000;
  font-weight: 600;
}

.md-date-header-week,
.md-date-header-month,
.md-date-header-quarter,
.md-date-header-year {
  padding: 0 8px;
  margin: 3px;
  color: initial;
  font-weight: 600;
}

.md-date-header-template .mbsc-timeline-footer-week {
  text-align: center;
}

.md-date-header-day-name {
  font-size: 13px;
  font-weight: 600;
}

.md-date-header-day-nr {
  font-size: 14px;
}

.md-date-header-events-one {
  background: #e9f5e4;
}

.md-date-header-events-few {
  background: #d0e3c6;
}

.md-date-header-events-more {
  background: #8ca77e;
}

.md-date-footer {
  font-size: 14px;
  font-weight: 600;
  text-align: center;
  line-height: 26px;
}
```

## Implementation notes

- Render 6 separate `Eventcalendar` instances on the same page, one for each timeline resolution: hourly, daily, weekly, monthly, quarterly, and yearly. All instances share the same 8 resources (Resource A-H, each with a distinct color). Each instance has its own independent event dataset of 12 events positioned with `dyndatetime` offsets.
- **View configurations:**
  - Hourly: `timeline: { type: 'day' }` - one column per hour for today
  - Daily: `timeline: { type: 'month' }` - one column per day for the current month
  - Weekly: `timeline: { type: 'week', resolutionHorizontal: 'week', size: 6 }` - 6 week columns
  - Monthly: `timeline: { type: 'month', resolutionHorizontal: 'month', size: 6 }` - 6 month columns
  - Quarterly: `timeline: { type: 'year', resolutionHorizontal: 'quarter', size: 1 }` - 4 quarter columns within 1 year
  - Yearly: `timeline: { type: 'year', resolutionHorizontal: 'year', size: 6 }` - 6 year columns
- Implement two helper functions shared across all renderers:
  - `getEventOccurrence(events)` - categorizes the array length as `'none'` (0), `'one'` (1), `'few'` (2-3), or `'more'` (4+). Used to vary the visual density indicator in headers.
  - `getOccuppancy(events)` - counts distinct resource IDs present in the events array, then returns `(count / totalResources * 100).toFixed(0)`. Used to show the occupancy percentage in footers.
- Each instance uses a **header renderer** and a **footer renderer**. Both receive `args` with `args.events` (all events overlapping that column) and `args.date`. The week header additionally receives `args.startDate` and `args.endDate`. Use `formatDate` for date formatting:
  - Hour: `formatDate('h:mm A', args.date)`
  - Day: `formatDate('DDD', args.date)` (abbreviated day name) + `formatDate('DD', args.date)` (day number)
  - Week: `formatDate('MMM DD', args.startDate) + ' - ' + formatDate('MMM DD', args.endDate)`
  - Month: `formatDate('MMM', args.date)`
  - Quarter: compute as `args.date.getMonth() / 3 + 1` and render `'Quarter N'`
  - Year: `formatDate('YYYY', args.date)`
- All footer renderers are identical: render `getOccuppancy(args.events) + ' %'`.
- The renderer option names differ per resolution. React names → Angular template option → Vue slot:
  - Hour header: `renderHour` → `hourTemplate` → `#hour`
  - Hour footer: `renderHourFooter` → `hourFooterTemplate` → `#hourFooter`
  - Day header: `renderTimelineDay` → `timelineDayTemplate` → `#timelineDay`
  - Day footer: `renderDayFooter` → `dayFooterTemplate` → `#dayFooter`
  - Week header: `renderWeek` → `weekTemplate` → `#week`
  - Week footer: `renderWeekFooter` → `weekFooterTemplate` → `#weekFooter`
  - Month header: `renderMonth` → `monthTemplate` → `#month`
  - Month footer: `renderMonthFooter` → `monthFooterTemplate` → `#monthFooter`
  - Quarter header: `renderQuarter` → `quarterTemplate` → `#quarter`
  - Quarter footer: `renderQuarterFooter` → `quarterFooterTemplate` → `#quarterFooter`
  - Year header: `renderYear` → `yearTemplate` → `#year`
  - Year footer: `renderYearFooter` → `yearFooterTemplate` → `#yearFooter`

## Run it in your project

1. If not signed up yet, or don't own a license, you can [start a free trial](https://mobiscroll.com/starttrial).

2. Clone the repository:

   ```
   git clone https://github.com/acidb/mobiscroll-demos-angular.git
   ```

3. If not installed yet, install the Mobiscroll CLI from NPM:

   ```
   npm install -g @mobiscroll/cli
   ```

4. Config Mobiscroll - open a terminal window in the root directory and run:

   ```
   mobiscroll config angular
   ```

5. Install dependencies - open a terminal window in the root directory and run:

   ```
   npm install
   ```

6. Run the project:

   ```
   ng serve
   ```

On a trial, add `--trial` to the config command: `mobiscroll config angular --trial`.

Running a demo locally requires a Mobiscroll trial or a license; setup is per framework at https://mobiscroll.com/docs/angular/getting-started/installation

## Related demos

- Previous: [Custom Cell Content](https://demo.mobiscroll.com/angular/timeline/dynamic-cell-content-template.md) - Add workload badges, status icons, and a hover quick-add button to each Angular Timeline cell — and cap events per cell.
- Next: [Employee Shift Slots](https://demo.mobiscroll.com/angular/timeline/shift-template.md) - Build a Angular Timeline with custom shift slot headers — replace hourly intervals with Morning and Afternoon, each with its own time range and color.
