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.
Skip Ahead
- Create App and Dependencies
- Create Line Chart Component
- Add a Chart Wrapper
- Feed and Strip Default Chart Styles
- Style the Chart for Optimized User Interaction
- Make it Pretty
- Result
*** Full code here ***
Create App and Dependencies
Generate new Angular App
# terminal
ng new chart-demo --skip-tests
When prompted:
# terminal
? Would you like to add Angular routing? n
? Which stylesheet format would you like to use?
CSS
> SCSS
Sass
Less
Import CommonModule
This module allows us to access and configure directives
// app.module.ts
import { CommonModule } from '@angular/common';
imports: [
...
CommonModule
]
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
@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
}
Add NgChartsModule
into app.module.ts
// app.module.ts
import { NgChartsModule } from 'ng2-charts';
imports: [
...
NgChartsModule
]
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
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>
Yutes' Shorthand SCSS Classes
-
mx-auto-320px
sets the width of the div to 320px and centers it horizontally (you can replace the320
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 thehr
element
Now your locally served app [ng serve
in your terminal] should look like this (exciting):
(“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:
- Bound the chart to a container that has a restricted width and height
- Add a colorful background
Add this to the template:
<!-- line-chart.component.html -->
<div class="chart-wrapper">
<canvas></canvas>
</div>
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;
}
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 {
}
}
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>
-
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
}
The app should look like this:
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
}
}
};
Now the chart should look empty like this:
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
}
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)
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
}
Now it's much easier to navigate and understand:
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
}
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
}
}
}
};
}
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).
*** Full code here ***
Top comments (3)
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
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
'supdate()
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!
Very interesting! I will look into it :)
Thanks a lot!