One more gem to build nice charts for your Ruby on Rails application.
With it you can build various types of charts Apache eCharts library (v. 5.4.0). This gem simplifies interface and adding few helpers to start adding charts in your app with just a few lines of code.
What you can build with it:
- area chart
- line chart
- bar chart
- donut chart
- pie chart
- radar chart
- calendar chart
- candlestick chart
- funnel chart
- gauge chart
- parallel chart
- sankey chart
- scatter chart
- stacked bar chart
- custom chart
In most cases with one line of code you can have a nice chart. The idea of this gem was inspired by Chartkick gem which is great and allows you to build charts very quickly. It works best in cooperation with groupdate gem. Unfortunatelly it's missing many needed types of charts or other customization options.
This implementation have more options and similar "interface" for building charts.
Add gem to your application's Gemfile:
gem "rails_charts"
Then execute:
$ ./bin/bundle install
You can install ECharts with installation command
$ ./bin/rails rails_charts:install
or do it manualy
- add eCharts in main JS bundle, e.g.
app/assets/javascripts/application.js
//= require echarts.min.js
//= require echarts/theme/dark.js
- add your first chart e.g.
<%= line_chart User.group(:age).count %>
- customize charts if needed. See available options or official documentation.
- Run:
yarn add echarts
- In
app/javascript/application.js
add this:
import * as echarts from 'echarts';
import 'echarts/theme/dark';
window.echarts = echarts;
- In
app/javascript/packs/application.js
add this:
import * as echarts from 'echarts';
import 'echarts/theme/dark';
window.echarts = echarts;
- add your first chart e.g.
<%= line_chart User.group(:age).count %>
- customize charts if needed. See available options or official documentation.
- change
config/importmap.rb
pin "echarts", to: "echarts.min.js"
pin "echarts/theme/dark", to: "echarts/theme/dark.js"
- add eCharts in main JS
import "echarts"
import "echarts/theme/dark"
- add your first chart e.g.
<%= line_chart User.group(:age).count %>
- customize charts if needed. See available options or official documentation.
<%= line_chart data, {
width: '250px',
height: '250px',
theme: 'dark',
class: 'chart-container-class',
style: 'padding: 10px'
} %>
Available options:
width: specify width of the chart
height: specify height of the chart
theme: specify theme of the chart (available themes examples https://echarts.apache.org/en/download-theme.html)
class: specify container's CSS class
id: specify container's ID
style: add inline style
debug: for gem development useful if you want to pause somewhere in the code
vertical: applicable for some types of charts
code: to see output code what is generated to see the chart, useful for debugging
options: {...}, nested hash, specify additional eCharts options
Apache eCharts options - https://echarts.apache.org/en/option.html#title.
If you need to format tooltip (or other javascript function as an option) you can pass a JS function, but you need to wrap it like:
options: {
tooltip: {
valueFormatter: RailsCharts.js("(value) => '$' + Math.round(value)")
}
}
All examples available in https://github.com/railsjazz/rails_charts/tree/main/test/dummy/app/views/home. You can see more examples if you clone this repo and start a dummy app.
Every chart has a built in default configuration for tooltips, or other options (sample https://github.com/railsjazz/rails_charts/blob/main/lib/rails_charts/line_chart.rb#L64-L75). This is just to simplify usage of this gem. In the future the plan is to have it configured in initializer.
<%= area_chart User.distinct.pluck(:role).map{|e| {name: e, data: User.where(role: e).group_by_day(:created_at).count} } %>
<%= line_chart User.group(:age).count, class: 'box',
options: {
title: {
text: "People count by age",
left: 'center'
},
}
%>
<%= bar_chart User.group(:role).average(:age),
class: 'box',
theme: 'sakura',
options: {
series: {
barWidth: '50%'
},
tooltip: {
valueFormatter: RailsCharts.js("(value) => '$' + Math.round(value)")
}
}
%>
<%= calendar_chart Commit.for_calendar_chart,
class: 'box',
options: {
visualMap: {
show: true,
min: 0,
max: 40,
orient: 'horizontal'
},
calendar: [{
range: '2021',
},]
}
%>
<%= candlestick_chart({
'2017-10-24' => [20, 34, 10, 38],
'2017-10-25' => [40, 35, 30, 50],
'2017-10-26' => [31, 38, 33, 44],
'2017-10-27' => [38, 15, 5, 42]
},
class: 'box',
theme: 'roma',
options: {
xAxis: {
axisTick: {
alignWithLabel: true
}
}
})
%>
<%= funnel_chart User.get_funnel_sample_data,
class: 'box',
height: '400px',
options: {
title: {
text: 'Demo',
left: 'center'
}
}
%>
<%= gauge_chart User.get_gauge_sample_data,
class: 'box',
height: '400px',
options: {
title: {
text: 'Demo',
left: 'center'
}
}
%>
<div class="box">
<%= parallel_chart [
[1, 2, 1, "Ruby"],
[2, 3, 2, "JavaScript"],
[3, 1, 3, "C#"]
], {
options: {
parallelAxis: [
{ dim: 0, name: '2019', inverse: true, minInterval: 1, min: 1, nameTextStyle: { fontSize: 16 }, axisLabel: { fontSize: 16 } },
{ dim: 1, name: '2020', inverse: true, minInterval: 1, min: 1, nameTextStyle: { fontSize: 16 }, axisLabel: { fontSize: 16 } },
{ dim: 2, name: '2021', inverse: true, minInterval: 1, min: 1, nameTextStyle: { fontSize: 16 }, axisLabel: { fontSize: 16 } },
{ dim: 3, type: "category", name: 'Language', data: ["Ruby", "JavaScript", "C#"], inverse: true, nameTextStyle: { fontSize: 16 }, axisLabel: { fontSize: 14 } },
]
}
}
%>
</div>
<%= donut_chart User.group(:role).count,
class: 'box',
options: {
legend: {
bottom: '0'
},
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
%>
<%= pie_chart User.group(:role).count,
class: 'box',
options: {
legend: { orient: 'vertical', left: 'left' }
}
%>
<%= radar_chart User.get_data_for_radar_chart,
class: 'box',
options: {
legend: {
data: ['Average Salaries', 'Maximum Salary'],
orient: 'vertical',
left: '20%'
}
}
%>
<%= sankey_chart({
data: [
{name: 'Ruby'}, {name: 'HTML'}, {name: 'JS'}, {name: 'Good'}, {name: 'Bad'}, {name: 'CSS'}, {name: 'PHP'}, {name: 'Frontend'}, {name: 'Backend'}
],
links: [
{
source: 'Ruby',
target: 'Good',
value: 1
},
{
source: 'HTML',
target: 'Good',
value: 1
},
{
source: 'JS',
target: 'Good',
value: 1
},
{
source: 'CSS',
target: 'Good',
value: 1
},
{
source: 'PHP',
target: 'Bad',
value: 1
},
{
source: 'Good',
target: 'Backend',
value: 1
},
{
source: 'Good',
target: 'Frontend',
value: 3
},
{
source: 'Bad',
target: 'Backend',
value: 1
},
]
}, {
options: {
}
})
%>
<%= scatter_chart [
{ name: 'John', data: User.random_scatter_chart(500, 200) },
{ name: 'Bob', data: User.random_scatter_chart(500, 1000) },
],
{
class: 'box',
options: {
xAxis: {
name: 'Distance'
},
yAxis: {
name: 'Sales'
},
legend: {
data: [
{name: 'John'},
{name: 'Bob'},
]
},
},
}
%>
<%= stacked_bar_chart [
{ name: 'high priority', data: Account.high_priority.group_by_month(:created_at, format: "%b %Y").count },
{ name: 'low priority', data: Account.low_priority.group_by_month(:created_at, format: "%b %Y").count }
],
{
options: {
title: {
text: "Popular vs Unpopular"
},
},
class: 'box',
vertical: true
}
%>
<%= custom_chart {...raw JS options ...} %>
You are welcome to contributes. Some open tasks:
- support turbo streams?
- add more specs
- add more examples to the dummy app
- customization, options overides, default values?
- every "5sec" refresh
- remote data
- how to access chart from JS
- better documentation how to specify theme, locale, etc
- more examples with data structure
- add github actions
- add info about initializer and it's configuration
- specify info about default configs per chart
- add support for CSP similar to https://github.com/ankane/chartkick/blob/master/lib/chartkick/helper.rb#L55
- example of how to build multiple-chart charts
test/dummy/bin/rails s
- to start dummy app.
rspec
- to run specs.
The gem is available as open source under the terms of the MIT License.
Gem is using https://echarts.apache.org/ to build charts.