DEV Community

Cover image for Make Pretty / User-Friendly Charts with Angular 14 & ng2-charts v3.1.0
Ria Pacheco
Ria Pacheco

Posted on

Make Pretty / User-Friendly Charts with Angular 14 & ng2-charts v3.1.0

In this post, we'll create a line chart (in Angular 14 with the latest release of ng2-charts) in a style that's all the rage in cryptocurrency dashboard design (RIP) and optimized for user interaction.

Here's how it will behave:
End Result


Skip Ahead

*** Full code here ***


Create App and Dependencies

Generate new Angular App



# terminal
ng new chart-demo --skip-tests


Enter fullscreen mode Exit fullscreen mode

When prompted:



# terminal
? Would you like to add Angular routing? n
? Which stylesheet format would you like to use?
  CSS
> SCSS
  Sass
  Less


Enter fullscreen mode Exit fullscreen mode

Import CommonModule

This module allows us to access and configure directives



// app.module.ts
import { CommonModule } from '@angular/common';

imports: [
  ...
  CommonModule
]


Enter fullscreen mode Exit fullscreen mode

Install SCSS Utility and Charts Package

Note: To keep this post as high-level as we can (especially with a robust, enterprise-level framework like Angular) we'll install my @riapacheco/yutes SCSS package for its utility configurations and classes. However, we'll visually style the component from scratch.



# terminal 
npm install @riapacheco/yutes ng2-charts


Enter fullscreen mode Exit fullscreen mode

@riapacheco/yutes

  • Strips webkit styles
  • Adds antialiasing / smoothing
  • Can access secret stylesheet that has "seasonal" colors I like. View colors here (actual variables replace the -- with $): Click Me

ng2-charts

The package that gives us charts (from chart.js)

Imports

Import the scss package into the main styles.scss file and add the following global styles (since I clearly have a thing for dark mode):



// styles.scss
@import '~@riapacheco/yutes/main.scss'; // For stripping styles & antialiasing
@import '~@riapacheco/yutes/seasonal.scss'; // To access dark color palette

html, body {
  font-size: 17px;
  background-color: $midnight-dark;
  background-image: linear-gradient(to bottom right, $midnight-dark, $midnight-medium);
  background-attachment: fixed; // The background is fixed relative to the viewport
  color: $steel-regular; // Change text color to an _almost_ white
}


Enter fullscreen mode Exit fullscreen mode

Add NgChartsModule into app.module.ts



// app.module.ts
import { NgChartsModule } from 'ng2-charts';

imports: [
  ...
  NgChartsModule
]


Enter fullscreen mode Exit fullscreen mode

Create Line Chart Component

Create a line-chart component [Figure 1] and replace the default content inside your app.component.html file with its selector (and some other stuff) [Figure 2]:
Figure 1



# terminal
ng g c components/line-chart


Enter fullscreen mode Exit fullscreen mode

Figure 2



<!--app.component.html-->

<div class="mx-auto-320px pt-3">
  <h6>
    Financial Highlights
  </h6>
  <h1>
    BWX Technologies
  </h1>

  <hr class="mb-2">
  <app-line-chart></app-line-chart>
</div>


Enter fullscreen mode Exit fullscreen mode

Yutes' Shorthand SCSS Classes

  • mx-auto-320px sets the width of the div to 320px and centers it horizontally (you can replace the 320 value with any # from 1 to 3000)
  • pt-3 adds 3rem of padding to the top of the div
  • mb-2 adds 2rem of margin to the bottom of the hr element

Now your locally served app [ng serve in your terminal] should look like this (exciting):
Image description

(“Inter” is one of the best fonts of all time)


Add a Chart Wrapper

In the line-chart component, we're going to create a wrapper that will:

  1. Bound the chart to a container that has a restricted width and height
  2. Add a colorful background

Add this to the template:



<!-- line-chart.component.html -->
<div class="chart-wrapper">
  <canvas></canvas>
</div>


Enter fullscreen mode Exit fullscreen mode

Add the following to its stylesheet:



// line-chart.component.scss
@import '~@riapacheco/yutes/seasonal.scss'; // get those colors again

$width: 320px;
$height: 250px;

.chart-wrapper {
  width: $width;
  min-width: $width;
  max-width: $width;
  height: $height;
  min-height: $height;
  max-height: $height;
  color: $midnight-dark;
  border-radius: 10px;
  background-image: linear-gradient(to bottom right, $bondi-dark, $bondi-light);
  background-attachment: fixed;
}


Enter fullscreen mode Exit fullscreen mode

Image description


Add and Bind the Chart

Now we can add the ng2-chart element to the app by importing a few types and adding their corresponding properties:



// line-chart.component.ts

// ⤵️ import these
import { ChartDataset, ChartOptions } from 'chart.js';
import { Component, OnInit } from '@angular/core';
@Component({
  selector: 'app-line-chart',
  templateUrl: './line-chart.component.html',
  styleUrls: ['./line-chart.component.scss']
})
export class LineChartComponent implements OnInit {
  // ⤵️ add them here
  chartData: ChartDataset[] = [];
  chartLabels: string[] = [];
  chartOptions: ChartOptions = {};

  constructor() { }

  ngOnInit(): void {
  }
}


Enter fullscreen mode Exit fullscreen mode

And the following to the line-chart.component.html file:



<div class="chart-wrapper">
  <canvas baseChart
          [type]="'line'"
    [datasets]="chartData"
    [labels]="chartLabels"
    [options]="chartOptions"
    >
  </canvas>
</div>


Enter fullscreen mode Exit fullscreen mode
  • baseChart and [type]="'line'" recognizes the package and specifies the chart type
  • The remaining directives (applied to the selector) are how we bind the data from the component. Our component's data being the properties on the right (without []) bound to the properties from the package on the left.

Feed and Strip Default Chart Styles

For our example (which is taken from publicly accessible financials), the chart will display how much revenue BWX Technologies generated each year from 2016 to 2021.

Add the Data

First, we'll add a label to describe what the chart's values represent ($ in millions) followed by a data array containing those revenues for each year [6 total].

Add the following to the component:



// line-chart.component.ts
// more code
export class LineChartComponent implements OnInit {
  chartData: ChartDataset[] = [
    {
      // ⤵️ Add these
      label: '$ in millions',
      data: [ 1551, 1688, 1800, 1895, 2124, 2124 ]
    }
  ];
  chartLabels: string[] = [];
  chartOptions: ChartOptions = {};

  // other code
}


Enter fullscreen mode Exit fullscreen mode

The app should look like this:
Image description

Strip the Chart

We'll remove the main label we created and the graph's Y-axis labels (as well as any grid lines making up the chart). Users will be able to find these when hovering over the line graph later.

To remove the above styles, we access the chartOptions object and set values for the following properties:

  • responsive Enables the chart to grow to fill any container it's enclosed in
  • scales Allows us to remove the lines and hidden ticks in the chart
  • plugins To hide the main label we created

Add the following to the chartOptions object:



// line-chart.component.ts

// more code

export class LineChartComponent implements OnInit {
  chartData: ChartDataset[] = [
        // code we added earlier
  ];
  chartLabels: string[] = [];

  chartOptions: ChartOptions = {

    // ⤵️ Fill the wrapper
    responsive: true,
    maintainAspectRatio: false,

    // ⤵️ Remove the grids
    scales: {
      xAxis: {
        display: false,
        grid: {
          drawBorder: false // removes random border at bottom
        }
      },
      yAxis: {
        display: false
      }
    },

    // ⤵️ Remove the main legend
    plugins: {
      legend: {
        display: false
      }
    }
  };


Enter fullscreen mode Exit fullscreen mode

Now the chart should look empty like this:
Image description

Show Data with Labels

To reveal the actual line, add labels like this:



// line-chart.component.ts

// more code

export class LineChartComponent implements OnInit {
    // stuff we added earlier

  // Add this ⤵️ 
  chartLabels: string[] = [ '2016 Revenue', '2017 Revenue', '2018 Revenue', '2019 Revenue', '2020 Revenue', '2021 Revenue' ];

  // more code
}


Enter fullscreen mode Exit fullscreen mode

Now it should look like this:
Image description


Style the Chart for Optimized User Interaction

If you hover your cursor over any of the points on the line, it'll reveal a tooltip which includes:

  • The year that each point represents
  • The main label we removed from the top of the chart earlier; and
  • A color label (which isn't really relevant with a single line'd line chart)

hovering

Notice how the cursor has to tightly hover over each point's 1 or 2 pixel radius. For an improved user experience, we can expand the radius that detects the hover event and increase the width of the targeted point during the event so the user understands which data point is in focus.

To do this, add the following to the chartData array:



// line-chart.component.ts

// more code 

export class LineChartComponent implements OnInit {
  chartData: ChartDataset[] = [
    {
      label: '$ in millions',
      data: [1551, 1688, 1800, 1895, 2124, 2124],

      // ⤵️ Add these
      pointHitRadius: 15, // expands the hover 'detection' area
      pointHoverRadius: 8, // grows the point when hovered
    }
  ];

  // other code
}


Enter fullscreen mode Exit fullscreen mode

Now it's much easier to navigate and understand:
bigger hits


Make it Pretty

Line and Point Styling

To configure colors, add the following to the chartData array. Read the comments to understand how each value impacts style:



// line-chart.component.ts

// more code

export class LineChartComponent implements OnInit {
  chartData: ChartDataset[] = [
    {
      label: '$ in millions',
      data: [1551, 1688, 1800, 1895, 2124, 2124],

      pointHitRadius: 15, // expands the hover 'detection' area
      pointHoverRadius: 8, // grows the point when hovered

      // ⤵️ Add these
      pointRadius: 2,
      borderColor: '#2D2F33', // main line color aka $midnight-medium from @riapacheco/yutes/seasonal.scss
      pointBackgroundColor: '#2D2F33',
      pointHoverBackgroundColor: '#2D2F33',
      borderWidth: 2, // main line width
      hoverBorderWidth: 0, // borders on points
      pointBorderWidth: 0, // removes POINT borders
      tension: 0.3, // makes line more squiggly
    }
  ];

  // more code
}


Enter fullscreen mode Exit fullscreen mode

Tooltip Styling

To configure the actual tooltip, add the following to the chartOptions.plugins object:



// line-chart.component.ts

// more code

export class LineChartComponent implements OnInit {

  // more code

  chartOptions: ChartOptions = {

    // other code

    plugins: {
      legend: {
        display: false
      },

      tooltip: {
        // ⤵️ tooltip main styles
        backgroundColor: 'white',
        displayColors: false, // removes unnecessary legend
        padding: 10,

        // ⤵️ title
        titleColor: '#2D2F33',
        titleFont: {
          size: 18
        },

        // ⤵️ body
        bodyColor: '#2D2F33',
        bodyFont: {
          size: 13
        }
      }
    }
  };
}


Enter fullscreen mode Exit fullscreen mode

Result

And here you have it! Though crypto is collapsing, lots of related dashboard UI designs love mini top-line indicator charts like this (and still look great despite such poor market performance).
result

*** Full code here ***

Top comments (3)

Collapse
 
tobiastotz profile image
Tobias Totz

Hey Ria, this is incredibly helpful!
I just had a recent project and the charts were a big issue... I wasn't aware how much we can customize with ng2-charts :)
Time to update some code :D

Collapse
 
riapacheco profile image
Ria Pacheco • Edited

Yeah, it was a big issue for me too! Also, you might find this helpful too: stackblitz.com/edit/ng2-chartjs-cu...

At the time (~9 mo ago) it was really difficult finding an answer on how to customize the legend -- found out it required just accessing the package's baseChart's update() method! So the link shows how I was able to programmatically detect what values changed in the data (using that method and some vanilla JS).

Might be out-of-date as I created this dev.to post to help spread the word on what both the ng2 package and original package's (chart.js) haven't yet updated in terms of their documented options syntax -- but if the problem still exists, it'll give you a solid head start anyway.

w00t!

Collapse
 
tobiastotz profile image
Tobias Totz

Very interesting! I will look into it :)
Thanks a lot!