Rails 4.2 jQuery loads only after refresh

831 views Asked by At

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?

1

There are 1 answers

8
max On

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);
  });
}