This post is trending. 6,032 Views

Beautiful Timeline Component in Ionic 3

In this tutorial we will be building a beautiful timeline component to display any suitable content from blog posts to comments or any other information. The sample of what we will be building is shown in the preview link.

GitHub repository for the complete code: https://github.com/lohanitech/ion-timeline

As you can see, the timeline component is responsive also. With just few media queries and small style overrides we will make our timeline component responsive. So let’s get started.

If you are first timer with Ionic and do not know how to setup and run a basic Ionic project, please go through our Getting started with ionic tutorial

Creating a project and generating our component

Ionic provides a easy way to generate our project and required components and services for our project. So we will be using Ionic CLI to generate our project and also generate our timeline component as follows
In our timeline we will change directory to our workspace and type the following commands

ionic start ion-timeline blank
ionic g component timeline
ionic serve

These commands will create blank ionic project, add a component named timeline and serve the project and open browser.

If you do not understand any CLI commands please go through our Ionic CLI 3 Cheatsheet. Understanding Ionic CLI commands is very important to progress through Ionic development as it is the best way to scaffold the project and other components required in the project. Also to serve for development and build the project, Ionic CLI is the best option.

Understanding Component in Ionic 2

Now let’s open the project in our favourite text editor. We at competenepal use Visual Studio Code.
To view our component we must go to src/components/timeline folder. There we can find three files, timeline.ts, timeline.scss and timeline.html which are our controller, style and template files respectively.

Lets get started building our timeline component

First let’s add the required classes in our controller file. Open src/components/timeline/timeline.ts and modify the code as follows

import { Component, Input } from '@angular/core';

@Component({
  selector: 'timeline',
  templateUrl: 'timeline.html'
})
export class TimelineComponent {
  @Input('endIcon') endIcon = "ionic";
  constructor() {

  }

}

@Component({
  selector: 'timeline-item',
  template: '<ng-content></ng-content>'
})
export class TimelineItemComponent{
  constructor(){

  }
}


@Component({
  selector:'timeline-time',
  template: '<span>{{time.subtitle}}</span> <span>{{time.title}}</span>'
})
export class TimelineTimeComponent{
  @Input('time') time = {};
  constructor(){

  }
}

Let’s us examine the code to understand what’s changed

import { Component, Input } from '@angular/core';

In the import line we have imported Input which is required to pass input to our component from where it renders.

@Component({
  selector: 'timeline',
  templateUrl: 'timeline.html'
})
export class TimelineComponent {
  @Input('endIcon') endIcon = "ionic";
  constructor() {

  }

}

The first component, which was already there, with the selector timeline is the component we generated. We have just added and input endIcon that user can used to supply the name of icon for the timeline end, default is set to ‘ionic’.

In the same file we have added two more components as follows

@Component({
  selector: 'timeline-item',
  template: '<ng-content></ng-content>'
})
export class TimelineItemComponent{
  constructor(){

  }
}


@Component({
  selector:'timeline-time',
  template: '<span>{{time.subtitle}}</span> <span>{{time.title}}</span>'
})
export class TimelineTimeComponent{
  @Input('time') time = {};
  constructor(){

  }
}

The timeline item is the component that displays our content for the timeline as shown in the right side of the image. And the component timeline-time is used to display our left side title, date etc for our timeline item. The template for both components is defined inline. TimelineItem just renders whatever is inside it’s body as it’s template only uses <ng-content></ng-content>. And timeline time component renders two spans that renders the title and subtitle for our timeline left header.

Now lets add the template for our main timeline component. Open src/components/timeline/timeline.html and modify as follows

<div class="timeline">
  <ng-content></ng-content>

  <timeline-item>
    <ion-icon class="" [name]="endIcon"></ion-icon>
  </timeline-item>

</div>

The selector of the component is used to render this component in our page. Before we can render the component we need to add it to our app modules. So open src/app/app.module.ts file and modify as follows

import { BrowserModule } from '@angular/platform-browser';
import { ErrorHandler, NgModule } from '@angular/core';
import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular';
import { SplashScreen } from '@ionic-native/splash-screen';
import { StatusBar } from '@ionic-native/status-bar';

import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
import { TimelineComponent } from '../components/timeline/timeline';
import { TimelineTimeComponent } from '../components/timeline/timeline';
import { TimelineItemComponent } from '../components/timeline/timeline';

@NgModule({
  declarations: [
    MyApp,
    HomePage,
    TimelineComponent,
    TimelineItemComponent,
    TimelineTimeComponent
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp)
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    HomePage
  ],
  providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler}
  ]
})
export class AppModule {}


In this code we just imported our three components and added it to the declarations array. This is required for ionic to know about the components and render it in other pages and components.

Render our timeline by adding items

Open src/pages/home/home.html and modify as follows:

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic Blank
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
  <timeline endIcon="call">
    <timeline-item *ngFor="let item of items">
      <timeline-time [time]="item.time"></timeline-time>
      <ion-icon [name]="item.icon"></ion-icon>
      <ion-card>
        <ion-card-header>
          {{item.title}}
        </ion-card-header>
        <ion-card-content>
          {{item.content}}
        </ion-card-content>
      </ion-card>
    </timeline-item>

  </timeline>
</ion-content>

This is the actual code that renders our timeline. Here we have a ngFor loop over a list of items. so lets define our list of items in the home page controller. Open src/pages/home/home.ts and change the code ans follows.

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {
  items = [
    {
      title: 'Courgette daikon',
      content: 'Parsley amaranth tigernut silver beet maize fennel spinach. Ricebean black-eyed pea maize scallion green bean spinach cabbage jícama bell pepper carrot onion corn plantain garbanzo. Sierra leone bologi komatsuna celery peanut swiss chard silver beet squash dandelion maize chicory burdock tatsoi dulse radish wakame beetroot.',
      icon: 'calendar',
      time: {subtitle: '4/16/2013', title: '21:30'}
    },
    {
      title: 'Courgette daikon',
      content: 'Parsley amaranth tigernut silver beet maize fennel spinach. Ricebean black-eyed pea maize scallion green bean spinach cabbage jícama bell pepper carrot onion corn plantain garbanzo. Sierra leone bologi komatsuna celery peanut swiss chard silver beet squash dandelion maize chicory burdock tatsoi dulse radish wakame beetroot.',
      icon: 'calendar',
      time: {subtitle: 'January', title: '29'}
    },
    {
      title: 'Courgette daikon',
      content: 'Parsley amaranth tigernut silver beet maize fennel spinach. Ricebean black-eyed pea maize scallion green bean spinach cabbage jícama bell pepper carrot onion corn plantain garbanzo. Sierra leone bologi komatsuna celery peanut swiss chard silver beet squash dandelion maize chicory burdock tatsoi dulse radish wakame beetroot.',
      icon: 'calendar',
      time: {title: 'Short Text'}
    }
  ]
  constructor(public navCtrl: NavController) {

  }

}

By now if you look at the browser, you should see your content rendered. But it looks nothing like the one we saw in the demo gif.
DeepinScreenshot_select-area_20170820090527.png
So what’s remaining. We have not yet added styles to our timeline component. So open src/components/timeline/timeline.scss and modify to add the following styles

  .timeline {
    margin: 30px 0 0 0;
    padding: 0;
    position: relative;

    &:before {
      content: '';
      position: absolute;
      top: 0;
      bottom: 0;
      width: 10px;
      background: #afdcf8;
      left: 20%;
      margin-left: -10px;
    }
    timeline-item {
      position: relative;
      display: inline-block;

      timeline-time {
        display: block;
        width: 20%;
        padding-right: 50px;
        position: absolute;
        span {
          display: block;
          text-align: right;
          &:last-child {
            font-size: 2.9em;
            color: #3594cb;
          }
        }
      }
      /* Right content */
      ion-card {
        margin-left: 25% !important;
        width: 75% !important;
        position: relative;
      }
      ion-icon {
        width: 40px;
        height: 40px;
        font-size: 1.4em;
        position: absolute;
        color: #fff;
        background: #46a4da;
        border-radius: 50%;
        box-shadow: 0 0 0 8px #afdcf8;
        text-align: center;
        left: 20%;
        top: 0;
        margin: 0 0 0 -25px;
        line-height: 40px;
        &:after {
            left: 46px;
            border: solid transparent;
            content: " ";
            height: 0;
            width: 0;
            position: absolute;
            pointer-events: none;
            border-left-color: #afdcf8;
            border-width: 10px;
            top: 10px;
          }
      }
        &:last-child{
          width: 100%;
          ion-icon{
            box-shadow: none;
            &:after{
              border-width: 0;
            }
          }
        }
    }
    @media screen and (max-width: map-get($grid-breakpoints,'lg')){
      timeline-item{
        timeline-time{
          font-size: 0.7em;
        }
      }
    }
    @media screen and (max-width: map-get($grid-breakpoints,'md')){
      &:before{
        left: 30px;
      }
      timeline-item{
        ion-card{
          margin-left: 80px !important;
        }
        ion-icon{
          left: 30px;
        }
        timeline-time{
          font-size: 0.8em;
          width: 100%;
          left: 80px;
          position: relative;
          span{
            text-align: left;
          }

        }
      }
    }
    @media screen and (max-width: map-get($grid-breakpoints, 'sm' )){
      &:before{
        display: none;
      }
      timeline-item{
        &:last-child{
          visibility: hidden;
        }
        ion-icon{
          position: relative;
          float: right;
          left: auto;
          margin: -55px 5px 0 0;
          &:after{
            border-left-color: transparent;
            border-top-color: #afdcf8;
            top: 46px;
            left: 10px;
          }
        }
        timeline-time{
          width: 100%;
          position: relative;
          padding: 0 0 20px 0;
          left: 20px;
          span{
            text-align: left;
          }
        }

        ion-card{
          width: calc(100% - 20px) !important;
          margin-left: 10px !important;
        }
      }
    }
  }

This should do it, you see the browser output as follows.
DeepinScreenshot_select-area_20170820090752.png
If you resize the browser window you can see the component style changes as we saw in the demo. This is achieved using the media queries and the grid breakpoints supplied by the ionic.

 

Related Posts

%d bloggers like this: