0

I have a page with a Pie chart using Chart.js which gets some data from a database and visualises them. The user enters two dates (from/to) and then he presses the Update Chart button, and the partial is loaded dynamically into the page. Unfortunately the Chart works only after refresh.

I know the deal about turbolinks and jquery but I don't seem to go anywhere. Same thing happens with other jQueries in the website, If I go back then I need to refresh in order to load the js code.

application.js

//= require jquery
//= require jquery.turbolinks
//= require jquery_ujs
//= require moment
//= require bootstrap-sprockets
//= require Chart
//= require libs/bootstrap-lifestyle
//= require bootstrap-datepicker
//= require_tree .
//= require turbolinks

_visualise_chart.html.erb

<script>
  // Load jQuery data 

  $(document).on('page:load', ready);
  $(document).on('page:change', ready);
  $(document).ready(ready);

  var ready = function () {

  //
  // Setup Tickets per day of week chart
  //
  var data = [
    {
        value: $('#MyPieChart').data('removed'),
        color:"#F7464A",
        highlight: "#FF5A5E",
        label: "Removed"
    },
    {
        value: $('#MyPieChart').data('installed'),
        color: "#46BFBD",
        highlight: "#5AD3D1",
        label: "Installed"
    },
    {
        value: $('#MyPieChart').data('updated'),
        color: "#FDB45C",
        highlight: "#FFC870",
        label: "Updated"
    }
  ]

  if ($('#MyPieChart')) {
       myNewChart = new Chart($("#MyPieChart").get(0).getContext("2d")).Pie(data, {
          //Number - Amount of animation steps
          animationSteps : 200,
          responsive : true,
          maintainAspectRatio: true
       });
    }

};

</script>

<div class="panel panel-default" style="width: max-width:360px; float:right;">
  <div class="panel-body">
    <div class="label distleft" style="background-color: #FDB45C">
      Updated:
    </div> 
    <%= @logevents_updated %> 
    <div class="label distleft" style="background-color: #46BFBD">
      Installed:
    </div>  
    <%= @logevents_installed %> 
    <div class="label distleft" style="background-color: #F7464A">
      Removed:
    </div> 
    <%= @logevents_removed %> 
  </div>
</div>

<%= content_tag :canvas, nil,  id: "MyPieChart", width: 300, height: 300,
      data: { 
          installed: @logevents_installed,
          updated: @logevents_updated,
          removed: @logevents_removed 
          } %>

visualise.js.erb

$('#piechart').html('<%= escape_javascript(render("visualise_chart")) %>');

visualise.html.erb

<h1>Visualize data</h1>
<hr />

<div class="col-lg-12">
  <div style="max-width: 600px" class="center-block">
    <div class="panel panel-default">
      <div class="panel-body">
        <div id="piechart">
          <%= render "visualise_chart" %>
        </div>
      </div>
    </div>
  </div>
</div>

<div class="col-lg-12">
  <div style="max-width: 600px" class="center-block">
    <div class="panel panel-default">
      <div class="panel-heading">
        Please select from and to dates <br/>
      </div>
      <div class="panel-body">
        <%= form_tag visualise_path, class: 'form-inline',remote: true, :method => 'get', multipart: true do %>
          <div class="form-group">
            <%= text_field_tag :from_date, nil, class: 'datepicker form-control', type: 'text', placeholder: "From date"%>
          </div>
          <div class="form-group">
            <%= text_field_tag :to_date, nil, class: 'datepicker form-control', placeholder: "To date"%>
          </div>
        <%= submit_tag "Update chart", class: 'btn btn-default' %>
        <% end %>
      </div>
    </div>
  </div>
</div>

visualise.js

// Load jQuery data 
$(document).ready(ready);
$(document).on('page:load', ready);
$(document).on('page:change', ready);

var ready = function () {

  //
  // Setup Tickets per day of week chart
  //
  var data = [
    {
        value: $('#MyPieChart').data('removed'),
        color:"#F7464A",
        highlight: "#FF5A5E",
        label: "Removed"
    },
    {
        value: $('#MyPieChart').data('installed'),
        color: "#46BFBD",
        highlight: "#5AD3D1",
        label: "Installed"
    },
    {
        value: $('#MyPieChart').data('updated'),
        color: "#FDB45C",
        highlight: "#FFC870",
        label: "Updated"
    }
  ]

  if ($('#MyPieChart')) {
       myNewChart = new Chart($("#MyPieChart").get(0).getContext("2d")).Pie(data);
  }

};

any ideas?

Radolino
  • 1,834
  • 4
  • 26
  • 48
  • Try removing `//= require turbolinks` and see where that gets you. Take a look at [this](http://stackoverflow.com/questions/18770517/rails-4-how-to-use-document-ready-with-turbo-links). – Drops Jun 08 '15 at 17:46
  • Try replacing `var ready = function {` with `function ready() {` – fylooi Jun 08 '15 at 18:05
  • @fylooi, you obviously don't understand the code at all. Changing it into a function expression changes nothing. – max Jun 08 '15 at 18:07
  • @maxcal: I think I understand it well enough to suspect that the `var ready = function(){` declaration is not hoisted to the top, which means `ready` only gets set upon refresh. =) – fylooi Jun 08 '15 at 18:09
  • Nope. Variables are hoisted to the top as well. The issues is that the inline code is evaluated after `page:change` and `page:load` – max Jun 08 '15 at 18:11
  • @maxcal Variables and function declarations are, function expressions aren't. http://adripofjavascript.com/blog/drips/variable-and-function-hoisting – fylooi Jun 08 '15 at 18:13
  • True but still not the issue. – max Jun 08 '15 at 18:14

1 Answers1

1

Using inline javascript is a bad practice since you mix behaviour and content. Using inline javascript with Turbolinks is just plain stupid - Turbolinks essentially turns your app into a persistent one page app you get all kinds of weird timing issues do the fact that your scripts may not be run until turbolinks appends the new page to the DOM. By then the the page:change event may have already have been fired.

This is why it only runs when you refresh the page vs when Turbolinks loads the page.

The fault isn't Turbolinks - it's your own. Put on the big boy pants and move that javascript into external files.

Added;

There are basically two approaches to creating widgets such as graphs that need external data. This is a generic sketch and in no way related to your question and whatever graph framework you are using.

Data attributes:

function Graph(data, el){}

if ($("#graph").length) {
  
  var data = $('#bar>li').map(function(i,el){ 
    return $(el).data() }
  ).toArray();
  $("#test").text( JSON.stringify(data) );

  Graph(data, $("#graph"));
} 
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<ul id="bar">
  <li data-x="1" data-y="2">A</li>
  <li data-x="2" data-y="3">B</li>
  <li data-x="3" data-y="4">C</li>
</ul>

<pre id="test"></pre>

Ajax:

if ($("#graph").length) {
  var promise = $.getJSON('/foo.json');
  promise.then(function(data){
    return $.map(data, function(item){
      return {x: item.foo y: item.bar };
    });
  });

  promise.done(function(data){
    Graph($("#graph"), data);
  });
}
max
  • 96,212
  • 14
  • 104
  • 165
  • Can you give an example please ? How can I update the Chart If the .js runs on an external file ? – Radolino Jun 08 '15 at 18:22
  • What? Of putting your code inside `application.js`? – max Jun 08 '15 at 18:23
  • The code is already in the application.js but somehow it should be updated when I press the update button. – Radolino Jun 08 '15 at 18:25
  • I have updated with the visualise.js code. Also what do you think of those page:change and page:load ? Are they nonsense ? – Radolino Jun 08 '15 at 18:29
  • Added an example. `page:change` is not nonsense its part of the turbolinks API. https://github.com/rails/turbolinks – max Jun 08 '15 at 18:47
  • Both of these examples should be wrapped in `on('page:change'`. They are also not any way meant to be complete but just to demonstrate that separation of logic and data is not very hard. – max Jun 08 '15 at 18:47
  • Rails docs say that you need to add `page:change` and remove `.ready()`. Do you think it has to do with my problem ? Apart from using pointless inline javascript and not ujs. – Radolino Jun 09 '15 at 09:18
  • Yes, you should wrap your code in `$(document).on('page:change')` if you want it to be run when Turbolinks changes pages. Turbolinks also fires `page:change` on the initial page load. – max Jun 09 '15 at 09:34