Skip to content

Instantly share code, notes, and snippets.

Created March 31, 2022 23:11
Show Gist options
  • Save retsef/d6dffcc0b792bce66bd9038bd8646388 to your computer and use it in GitHub Desktop.
Save retsef/d6dffcc0b792bce66bd9038bd8646388 to your computer and use it in GitHub Desktop.
Chartkick with stimulus
import { Controller } from "@hotwired/stimulus"
import Chartkick from "chartkick";
export default class extends Controller {
static targets = [];
static values = {
type: String,
data: Array,
options: Object
chart = null;
connect() {
this.chart = new Chartkick[this.typeValue](,
disconnect() {
Chartkick::Helper.module_eval do
# don't break out options since need to merge with default options
def chartkick_chart(klass, data_source, **options)
options = chartkick_deep_merge(Chartkick.options, options)
@chartkick_chart_id ||= 0
element_id = options.delete(:id) || "chart-#{@chartkick_chart_id += 1}"
height = (options.delete(:height) || '300px').to_s
width = (options.delete(:width) || '100%').to_s
# html vars
html_vars = {
id: element_id,
height: height,
width: width,
# don't delete loading option since it needs to be passed to JS
loading: options[:loading] || 'Loading...'
%i[height width].each do |k|
# limit to alphanumeric and % for simplicity
# this prevents things like calc() but safety is the priority
# dot does not need escaped in square brackets
raise ArgumentError, "Invalid #{k}" unless /\A[a-zA-Z0-9%.]*\z/.match?(html_vars[k])
html_vars.each_key do |k|
# escape all variables
# we already limit height and width above, but escape for safety as fail-safe
# to prevent XSS injection in worse-case scenario
html_vars[k] = ERB::Util.html_escape(html_vars[k])
# js vars
js_vars = {
type: klass,
id: element_id,
data: data_source.respond_to?(:chart_json) ? data_source.chart_json : data_source.to_json,
options: options.to_json
html = content_tag(:div, id: html_vars[:id],
style: "height: #{html_vars[:height]}; width: #{html_vars[:width]}; line-height: #{html_vars[:height]}; text-align: center; color: #999; font-size: 14px; font-family: 'Lucida Grande', 'Lucida Sans Unicode', Verdana, Arial, Helvetica, sans-serif;",
'data-controller': 'chart',
'data-chart-type-value': js_vars[:type],
'data-chart-data-value': js_vars[:data],
'data-chart-options-value': js_vars[:options]) do
html.respond_to?(:html_safe) ? html.html_safe : html
Copy link

elalemanyo commented May 30, 2022

@retsef Thanks for your gist. Unfortunately it does not work in my case 😞
I am using Chartkick inside a turbo frame, and because I use turbo_action: "advance" to change the url the charts are not showing up, not sure why. Maybe you have an idea.

Copy link

retsef commented Jun 5, 2022

Hi @elalemanyo I'm using this patch in a similar environment. At our company we have a dashboard with many charts and with a range picker we update the charts. In our case we trigger the request from javascript and we ask for a turbo-stream response.
Like this:
import { FetchRequest } from '@rails/request.js'
const request = new FetchRequest("get", url.toString(), { responseKind: "turbo-stream" })
const response = await request.perform()

In your case try to check for the request to includes a turbo-stream mime. If not try to investigate further on the correct scope of the target with data-turbo-target

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment