GithubHelp home page GithubHelp logo

Comments (23)

sebastianiorga avatar sebastianiorga commented on May 2, 2024 5

Hacked something to make this work. No idea if it's the proper solution, but I'll leave it here in case anyone is just looking for a way to Make It Work.

My actual implementation involves some classes wrapping chartkick and jquery datatables, but I've stripped it down to the bare bones for this example. Modify as needed for your purposes.

In some javascript file available in your view
window.exampleFunction = function () { return 'function_name example' }
In your view
<%= chartkick_render_chart_hack %>

In ApplicationHelper

FUNCTION_NAME_DELIMITER = 'FNNAME'
FUNCTION_START = 'FNSTART'
FUNCTION_END = 'FNEND'

# Assuming you are using the Highcharts adapter
def chartkick_render_chart_hack(chart_type = :column_chart, options = nil)
  # Or generate the options elsewhere and pass them in. Just make sure to use the helpers.
  options ||= { 
    library: { 
      xAxis: { labels: { formatter: js_inline('function () { return "inline_example"; }') } },
      yAxis: { labels: { formatter: js_function_name('exampleFunction') } }
    }   
  }

  # chart_type might end up being :column_chart, :bar_chart, etc 
  # whatever chart method symbol from chartkick you want to use
  rendered = send chart_type, data, options

  # Strip out the delimiters from the generated html/js/json. 
  # The effect is it turns the JSON options into a plain javascript object. 
  # The function bodies/names are no longer strings. Magic.
  rendered
    .gsub(/"#{ChartTemplate::FUNCTION_NAME_DELIMITER}([^""]*)"/, '\1')
    .gsub(/"#{FUNCTION_START}(.*)#{FUNCTION_END}"/, '\1')
    .gsub('\"', '"')
end

# Example: onClick: js_function_name('someFunction').
# someFunction needs to be available as window.highchartsExtras.someFunction.
def js_function_name(fn)
  "#{FUNCTION_NAME_DELIMITER}highchartsExtras.#{fn}"
end

# Example: onClick: js_inline('function () { alert("wooo"); }')
def js_inline(js)
  "#{FUNCTION_START}#{js}#{FUNCTION_END}"
end

Of course, you need to make sure that you are using the helper functions to annotate your embedded 'js' code. You also need to make sure to always wrap the call to the actual chartkick method with the helper. So if you are rendering the chart in a controller in response to an AJAX call, it needs to use the helper method etc.

Haven't run this code, since it's extracted from a more complicated wrapper I'm building around chartkick, but it should work.

Disclaimer: this may be a mad way of implementing this, but it seems to work fine for my purposes.

Disclaimer 2: this can be implemented straight into the various chartkick methods: make js_inline and js_function_name(or equivalent) available in the view or as static methods, then have methods like column_chart innately try to parse out their output before they return it. However, I haven't actually looked into the chartkick internals to see what it would take.

from chartkick.

CyberDeck avatar CyberDeck commented on May 2, 2024 2

In line with the comment from @sebastianiorga, I developed the following solution:

require 'active_support/core_ext/module/aliasing'

module ChartkickMixinJavascriptFunctions
  FUNCTION_NAME_DELIMITER = 'FNNAME'
  FUNCTION_START = 'FNSTART'
  FUNCTION_END = 'FNEND'

  module Helper
    def js_function_name(fn)
      "#{ChartkickMixinJavascriptFunctions::FUNCTION_NAME_DELIMITER}#{fn}"
    end

    def js_inline(js)
      "#{ChartkickMixinJavascriptFunctions::FUNCTION_START}#{js}#{ChartkickMixinJavascriptFunctions::FUNCTION_END}"
    end
  
    private
    def chartkick_chart(klass, data_source, options)
      rendered = super(klass, data_source, options)
  
      rendered
        .gsub(/"#{ChartkickMixinJavascriptFunctions::FUNCTION_NAME_DELIMITER}([^""]*)"/, '\1')
        .gsub(/"#{ChartkickMixinJavascriptFunctions::FUNCTION_START}(.*)#{ChartkickMixinJavascriptFunctions::FUNCTION_END}"/, '\1')
        .gsub('\"', '"')
        .html_safe
    end 

  end
end
  
Chartkick::Helper.send(:prepend, ChartkickMixinJavascriptFunctions::Helper)

Just put the code into the Chartkick initializer file, e.g. config/initializers/chartkick.rb.

Afterwards, you can call e.g. line_chart the usual way and wrap options strings into the js_inline etc. helpers.

from chartkick.

ConfusedVorlon avatar ConfusedVorlon commented on May 2, 2024 1

just to add an example here

where I'm adding hs.tens_label_callback - and I don't want to clobber the options that are set through standard options like 'stacked'

<%= column_chart test_team_dashboard_path, download: "test", stacked: true, id: "test-chart" %>
<script>

	var chart = Chartkick.charts["test-chart"];
	var newOptions = {
		library: {
			animation: {
				easing: 'easeOutQuad',"duration": 200
			},
			scales:{
				yAxes: [{
					ticks: {
						beginAtZero:true,
						suggestedMax: 20,
						callback: hs.tens_label_callback
					}
				}]
			}
		}
}
var mergedOptions = jQuery.extend(chart.options,newOptions)
chart.setOptions(mergedOptions);

from chartkick.

ankane avatar ankane commented on May 2, 2024

Unfortunately, there's no way to pass a function at the moment since options are serialized to JSON.

from chartkick.

kakoni avatar kakoni commented on May 2, 2024

Pass the function in as a string and then parse it back?
(something like this http://ovaraksin.blogspot.fi/2013/10/pass-javascript-function-via-json.html)

from chartkick.

tfwright avatar tfwright commented on May 2, 2024

Ran into this issue as well. It is very difficult to do much customizing with Highcharts at all without the ability to pass a function.

from chartkick.

ankane avatar ankane commented on May 2, 2024

Adding a function method this way seems like the wrong approach. However, I definitely see why this would be useful. If anyone has other ideas about how to approach this, I'd love to hear them. I plan on starting work for Chartkick 2.0 soon, which will be all about customization.

from chartkick.

tfwright avatar tfwright commented on May 2, 2024

I haven't looked into the highcharts source very closely, but assuming the function can be named, could chartkick just require that you pass in that name instead of the actual javascript? That way you could keep things relatively unobtrusive but also simple.

If this sounds like a viable option to you, I could try to whip something up in a fork.

from chartkick.

ankane avatar ankane commented on May 2, 2024

I think events will be the best way to accomplish this.

$("#users-chart").on("chartkick:before", function (e) {
  // customize options and data
})

from chartkick.

kakoni avatar kakoni commented on May 2, 2024

Aah, events make perfect sense.

from chartkick.

khelal avatar khelal commented on May 2, 2024

+1 as this is blocking us from using chartkick at the moment :(

from chartkick.

khelal avatar khelal commented on May 2, 2024

Sorry for a probably stupid question, but could you show us how to do that to configure HighChart so that items with 0 value don't appear in the graphs (using something like this: http://stackoverflow.com/questions/15834687/how-to-hide-zero-values-in-column-chart)

from chartkick.

tfwright avatar tfwright commented on May 2, 2024

@khelal you can use series#update to add a formatter described in the answers to that SO question via JS.

from chartkick.

khelal avatar khelal commented on May 2, 2024

Tried this:

$("#chart-3").highcharts().series[0].update({options: {dataLabels: {formatter: function() { if (this.y > 0) return this.point.name; }}}}, true);

Doesn't seem to work as we can't seem to be able to update the options once the graph has been displayed...

from chartkick.

tfwright avatar tfwright commented on May 2, 2024

@khelal not sure what the problem is, it has worked fine for me. Are you getting any JS errors? Maybe try changing something simple like the title to make sure the function is working as expected. Updating the chart after it's loaded should work, that's what the function is meant for.

from chartkick.

summerville avatar summerville commented on May 2, 2024

@tfwright what's the best way to know when the chart is loaded, and series#update can be called?

from chartkick.

tfwright avatar tfwright commented on May 2, 2024

@summerville if you're using highcharts the chart object supports a load event. So the flow would be

  1. Create the chart object with chartkick
  2. Access the highchart object with js and set the load event to trigger a function
  3. That function sets a formatter (or whatever) on the chart using the update method

I warn you and anyone else though that if you have a lot of js this gets extremely bulky very fast. I ended up switching back to michelson/lazy_high_charts for that reason.

from chartkick.

summerville avatar summerville commented on May 2, 2024

Cool, thx @tfwright.

I love the async setup option of Chartkick, but not having an easy way to format the data on the client side and/or register click handlers is the biggest bummer.

I tried accessing the highchart object on domready like $('#some-chart').highchart() but it returned undefined because Chartkick hadn't initialized the chart yet (it's still waiting on the ajax callback).

Were you using the ajax load option when you were using Chartkick?

from chartkick.

tfwright avatar tfwright commented on May 2, 2024

@summerville ah, yeah I ran into that problem too. There's not a great solution to it, I ended up going with brandonaaron/livequery

from chartkick.

satheeshcsedct avatar satheeshcsedct commented on May 2, 2024

Is there any progress on this, I am looking for the proper fix to pass the js function to the labelformatter.

from chartkick.

austinarchibald avatar austinarchibald commented on May 2, 2024

@satheeshcsedct, yes I am wondering the same thing. All I want to do is add a measly '%' to the xAxis values. Why does it have to be so difficult? Is there a working workaround? I tried something like this, but I can't get it to work.

$(".highcharts-container").on("chartkick:before", function (e){
    yAxis: {
      labels: {
        formatter: function () {
          return this.value+'%';
        }
      }
    }
  });

from chartkick.

itkin avatar itkin commented on May 2, 2024

+1

from chartkick.

ankane avatar ankane commented on May 2, 2024

Just fyi, the preferred way to do this is to use the setOptions method in the JavaScript API. https://github.com/ankane/chartkick#javascript-api

from chartkick.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.