Create a timeline flow component with Angular

timeline component with Angular

[UPDATE: April 14, 2020]: I initially wrote this article in 2018 but I can see a lot of people are still interested based on the traffic I am getting for this page. So, I decided to update this article and also improve the component a little bit. It’s now upgraded from Angular 6 all the way to Angular 9.1 which is the latest version. Although, the component itself is pretty simple so should work with any version of Angular (Angular 2+). For the component, I tried to simplify the template and also the styles to reduce some code

A step-by-step guide to creating custom Angular Timeline Component from scratch. Simple, configurable Angular Timeline component in easy steps.

  1. Create a new Angular CLI project
  2. Create the timeline component
  3. Timeline Component template
  4. Timeline Component logic
  5. Timeline Component styles
  6. Timeline Component interfaces and default values
  7. Invoke the timeline component from AppComponent

I work for a tech company in the Netherlands, currently working on assignments for a client in the Banking/Insurance domain. Recently we came across a scenario where we wanted to show the status of different processes to the customer. In the current situation, if a customer wants to know the status, they have to contact the customer care. We wanted to reduce those calls and hence decided to show the statuses of these processes in the customer’s account.

If you’re new to Angular, you might be interested in understanding the Angular Project Structure. If you’re just interested in Angular overall, take a look at our other Angular Related Articles.

We ended up creating something like this:

Timeline flow component with Angular

You can pass the list of items displayed and also required configuration as input, making the component completely customizable.

In this tutorial, I am going to create a new Angular CLI project and then create the component. The Source Code and Live Demo of this tutorial are available on Github.

So, let’s get started.

Step 1: Create a new Angular CLI project

Let’s create a new Angular project using the Angular CLI. Go to the Terminal/Command Prompt and type:

ng new timeline-demo --style scss

Step 2: Create the timeline component

Angular CLI will create the project and install the dependencies for you. Once done, navigate to the project directory and create a new component

cd timeline-demo 
ng generate component timeline-flow

At this point, you have a brand new angular CLI project created with 2 components in it – the AppComponent and TimelineFlowComponent.

Step 3: Add the template for timeline component

The template is not very complicated. On the left side, we have the pipe running through the length of the component. And on the right side, we show the title and subtitle (optional) for each step. the timeline-flow.component.html file should look something like this.

<div class="timeline-flow-wrapper">
  <div class="step edge-step" *ngIf="topEdge">
    <div class="pipe-wrapper">
      <span class="pipe"></span>
    </div>
    <div class="status-text"></div>
  </div>
  <div class="step" *ngFor="let step of data; let i=index" [ngClass]="{'edge-step': i === data.length-1 &amp;&amp; bottomEdge}">
    <div class="pipe-wrapper">
      <span class="pipe" [ngStyle]="getStyles(step, 'pipe-styles')"></span>
      <span class="status-icon" [ngClass]="getStyles(step, 'icon-class')"
        [ngStyle]="getStyles(step, 'icon-styles')"></span>
    </div>
    <div class="status-text" [ngStyle]="getStyles(step, 'text-styles')">
      <div class="title">{{step.step}}</div>
      <div class="sub-title" *ngIf="step.subtext">{{step.subtext}}</div>
    </div>
  </div>
</div>

Step 4: Add the logic for timeline component

This is very much a visual component, so the component logic is very simple. the component file contains only 1 function to process the configuration (received as input) to apply some dynamic styles to our component. The component also accepts two optional boolean inputs topEdge and bottomEdge that control whether or not the timeline should show edges. The timeline-flow.component.ts file should look like this:

import { Component, Input } from '@angular/core';
import { DEFAULT_STATUS_VALUES, StepsData, StatusConfig } from './timeline-flow.options';

@Component({
  selector: 'app-timeline-flow',
  templateUrl: './timeline-flow.component.html',
  styleUrls: ['./timeline-flow.component.scss']
})
export class TimelineFlowComponent {

  @Input() data: StepsData[] = [];
  @Input() statuses: StatusConfig[] = DEFAULT_STATUS_VALUES;
  @Input() topEdge = false;
  @Input() bottomEdge = false;

  constructor() {
  }

  getStyles(step, type) {
    const statusRec: StatusConfig = this.statuses.find(item => item.text === step.status);
    const styles = {};
    if (statusRec &amp;&amp; statusRec.styles) {
      switch (type) {
        case 'icon-styles':
          if (statusRec.styles.iconPath) {
            styles['content'] = `url(${statusRec.styles.iconPath})`;
          }
          if (statusRec.styles.textColor) {
            styles['color'] = statusRec.styles.textColor;
          }
          break;
        case 'icon-class':
          if (statusRec.styles.iconClass) {
            styles[statusRec.styles.iconClass] = true;
          }
          break;
        case 'text-styles':
          if (statusRec.styles.textColor) {
            styles['color'] = statusRec.styles.textColor;
          }
          break;
        case 'pipe-styles':
          if (statusRec.styles.pipeColor) {
            styles['background-color'] = statusRec.styles.pipeColor;
          }
          break;
      }
    }
    return styles;
  }
}

Step 5: Add the styles for timeline component

I have tried to simplify the styles with the recent update but it’s still not perfect. As I said this is a visual component and your visual requirements might be different than how this component looks. So feel free to update the styles as per your needs, I’ll leave plenty of comments in the code to help you understand. The timeline-flow.component.scss file should look something like this:

$contrastGrey: #c8c8c8;

.timeline-flow-wrapper {
  width: inherit;
  display: inline-block;
  padding-left: 20px;
  padding-top: 10px;

  .step {
    display: grid;
    grid-template-columns: auto 1fr;
    min-height: 70px;

    .pipe-wrapper {
      position: relative;
      padding-right: 15px;

      .pipe {
        position: absolute;
        left: 5px;
        top: 10px;
        width: 10px;
        border-radius: 3px;
        height: 100%;
        background-color: #ccc;
        z-index: 1;
      }

      .status-icon {
        position: relative;
        z-index: 2;
        top: 2px;
        background-color: #fff;
        border-radius: 50%;
        width: 22px;
        height: 22px;

        &::before {
          font-size: 22px;
          position: relative;
          left: 1px;
        }
      }
    }

    .status-text {
      padding-bottom: 25px;

      .title {
        font-size: 1.4rem;
      }

      .sub-title {
        font-size: 1rem;
        color: #999;
      }
    }


    &:last-child {
      .pipe-wrapper {
        .pipe {
          display: none;
        }
      }
    }

    &.edge-step {
      min-height: 30px;
    }

    &:last-child {
      min-height: auto;

      .status-text {
        padding-bottom: 10px;
      }

      &.edge-step {
        margin-bottom: 30px;

        .pipe-wrapper .pipe {
          display: inherit;
        }
      }
    }
  }
}

Step 6: The interfaces and default values

timeline-flow.options.ts file contains the interfaces used by the component and also default values for the list of Statuses. You can, of course, override the list of statuses by passing in your own list as input.

export const DEFAULT_STATUS_VALUES: StatusConfig[] = [{
  text: 'Not Started',
  styles: { pipeColor: '', textColor: '#A5A5A5', iconClass: 'fa fa-circle-o', iconPath: '' }
}, {
  text: 'In progress',
  styles: { pipeColor: '', textColor: '#0077C8', iconClass: 'fa fa-exclamation-circle', iconPath: '' }
}, {
  text: 'Completed',
  styles: { pipeColor: '#0077C8', textColor: '#49AF57', iconClass: 'fa fa-check-circle', iconPath: '' }
}];

export interface StatusConfig {
  text: string;
  styles?: StatusConfigStyles;
}

interface StatusConfigStyles {
  pipeColor?: string;
  textColor?: string;
  iconClass?: string;
  iconPath?: string;
}

export interface StepsData {
  step: string;
  subtext?: string;
  status: string;
}

As you can see here, you can completely customize the timeline component by passing the config/options as Input. In this case, I am using Font Awesome for the icons and hence you see font awesome classes in the config. You can use any other library or use your own font classes if you’ve any. You can also pass the font path as part of the config in case you don’t have a font class

Step 7: Invoke the timeline component from AppComponent

  • app.component.html
<h2>Angular Timeline Component</h2>
<div class="examples">
  <div>
    <h3>Default</h3>
    <app-timeline-flow [data]="statusData" style="width: 400px;display: inline-block;">
    </app-timeline-flow>
  </div>
  <div>
    <h3>With Edges</h3>
    <app-timeline-flow [data]="statusData" [statuses]="StatusValues" [topEdge]="true" [bottomEdge]="true"
      style="width: 400px; display: inline-block;">
    </app-timeline-flow>
  </div>
  <div>
    <h3>Custom Config</h3>
    <app-timeline-flow [data]="CustomStatusData" [statuses]="CustomStatusValues"
      style="width: 400px; display: inline-block;">
    </app-timeline-flow>
  </div>
</div>
  • app.component.ts
import { Component } from '@angular/core';
import { StepsData, StatusConfig } from './timeline-flow/timeline-flow.options';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  statusData: StepsData[] = [{
    step: 'This is Step 1',
    status: 'Completed'
  }, {
    step: 'This is Step 2',
    subtext: 'This is a dummy text for sub title for this step. This should be show below the title',
    status: 'Completed'
  }, {
    step: 'Step 3',
    status: 'Completed'
  }, {
    step: 'Step 4',
    status: 'In progress'
  }, {
    step: 'Step 5',
    status: 'Not Started'
  }, {
    step: 'Step 6',
    status: 'Not Started'
  }];

  StatusValues: StatusConfig[] = [{
    text: 'Not Started',
    styles: { pipeColor: '', textColor: '#A5A5A5', iconClass: 'fa fa-circle-o', iconPath: '' }
  }, {
    text: 'In progress',
    styles: { pipeColor: '', textColor: '#0077C8', iconClass: 'fa fa-exclamation-circle', iconPath: '' }
  }, {
    text: 'Completed',
    styles: { pipeColor: '#0077C8', textColor: '#49AF57', iconClass: 'fa fa-check-circle', iconPath: '' }
  }];

  CustomStatusValues: StatusConfig[] = [{
    text: 'Pending',
    styles: { pipeColor: '#F00', textColor: '#F00', iconClass: 'fa fa-circle-o', iconPath: '' }
  }, {
    text: 'Working on It',
    styles: { pipeColor: '', textColor: '#0077C8', iconClass: 'fa fa-exclamation-circle', iconPath: '' }
  }, {
    text: 'Done',
    styles: { pipeColor: '#0077C8', textColor: '#49AF57', iconClass: 'fa fa-check-circle', iconPath: '' }
  }];

  CustomStatusData: StepsData[] = [{
    step: 'This is Step 1',
    status: 'Done'
  }, {
    step: 'This is Step 2',
    subtext: 'This is a dummy text for sub title for this step. This should be show below the title',
    status: 'Done'
  }, {
    step: 'Step 3',
    status: 'Done'
  }, {
    step: 'Step 4',
    status: 'Working on It'
  }, {
    step: 'Step 5',
    status: 'Pending'
  }, {
    step: 'Step 6',
    status: 'Pending'
  }];
}

And here we have, our own timeline component. You can change the styling as per your requirements. As mentioned above, the source code is available on Github. You can see the live demo of this component by following the link below.

If you’re new to Angular, you might be interested in understanding the Angular Project Structure.

Thank you for your time. Please leave a comment and share this tutorial if you liked it. Also, leave a comment if you want something improved and I’ll try to make that happen.

Be sure to follow us on the social media to get notified about the latest posts as soon as they’re published

Leave a comment

Your email address will not be published. Required fields are marked *