0

I have a scatted chart (chart.js) and a form-control for user to change the time period of the chart. When user selects different option in form-control, an ajax script is invoked to get a new set of data from controller. And it works fine at a first glimpse. The chart is dynamically reloaded and new data is showed. But when I roll a mouse pointer over a chart, it shows, that there are in fact two charts in the same place. And browser tries to show once a new one and once an old one. It looks strange. What do I do wrong? I wish to have only a new chart after refresh. Here's my html and script code:

@model LivtronDb.Device
@{
    ViewData["Title"] = "Chart";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<br />
<h1>@ViewData["Title"] @Html.DisplayFor(model => model.Name)</h1>
<hr />

<div class="row">
    <div class="col-md-4">
        <div class="form-group">
            <label class="control-label"></label>
            <select class="form-control" value=1 id="chartParameterDays">
                <option value="1">1 day</option>
                <option value="2">2 days</option>
                <option value="3">3 days</option>
                <option value="7">7 days</option>
            </select>
        </div>
    </div>
</div>
<div class="col-md-12">
    <div>
        <div scope="row" colspan="2" class="col-8">
            <div id="chartContainerT" class="containerT">
                <canvas id="myChartT"> </canvas>
            </div>
        </div>
    </div>
</div>

@section Scripts
{
    <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.21.0/moment.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.js"></script>

  <script>
    function GenerateRuntimeGraph(id, days) {
        $.ajax({
            type: "POST",
            url: `/Devices/ChartData?id=${id}&days=${days}`,
            success: function (chData) {
                let chDataObj = JSON.parse(chData);

                var options = {
                    type: 'scatter',
                    data: {
                        datasets: [{
                            label: "Temperatura",
                            backgroundColor: "rgba(196, 93, 105, 0.3)",
                            showLine: true,
                            data: chDataObj.dataListT
                        }]
                    },
                    options: {
                        scales: {
                            xAxes: [{
                                type: 'time',
                                time: { unit: 'hour' },
                                gridLines: { display: false },
                                display: true,
                                scaleLabel: { display: false, labelString: '' }
                            }],
                            yAxes: [{
                                ticks: { min: chDataObj.dataChartBoudaries.minT, max: chDataObj.dataChartBoudaries.maxT }
                            }]
                        }
                    }
                }
                var ctx = document.getElementById('myChartT').getContext('2d');
                var chartTHolder = new Chart(ctx, options);
            },
            "error": function (data) {
                alert("Sorry, error!");
            }
        });
    }

    //run when list changed
    $("#chartParameterDays").change(function () {
        GenerateRuntimeGraph('@Model.Id', $('#chartParameterDays').val()); 
    });
  </script>
  <script>
        //loaded on open
        GenerateRuntimeGraph('@Model.Id', $('#chartParameterDays').val()); 
  </script>
}
wazz
  • 4,057
  • 5
  • 18
  • 32
  • It looks like `var ctx` is the context where the chart gets inserted into the DOM, so why not just clear out that context before attempting to insert a chart? https://stackoverflow.com/questions/2142535/how-to-clear-the-canvas-for-redrawing – kmoser Sep 12 '20 at 21:44
  • I tried `ctx.clearRect(0, 0, canvas.width, canvas.height);` but it does not work. – Krzysztof Patra Sep 14 '20 at 20:50
  • Where exactly did you add that code? – kmoser Sep 14 '20 at 21:27
  • After ctx is declared. When I try to put id earlier is says that ctx is undefined. `var ctx = document.getElementById('myChartT').getContext('2d'); ctx.clearRect(0, 0, ctx.width, ctx.height); var chartTHolder = new Chart(ctx, options);` – Krzysztof Patra Sep 16 '20 at 19:54
  • Only thing I can suggest trying is to clear the innerHTML property first: `document.getElementById('myChartT').innerHTML = ''`. – kmoser Sep 16 '20 at 21:47

1 Answers1

0

The problem is that the old chart is still present in the canvas when you create the new chart. Instead of creating a new chart each time new data is available, you should simply assign the new values to the existing chart configuration and then update the existing chart.

chartTHolder.data.datasets[0].data = chDataObj.dataListT; 
chartTHolder.options.scales.yAxes[0].ticks.min = chDataObj.dataChartBoudaries.minT;
chartTHolder.options.scales.yAxes[0].ticks.max = chDataObj.dataChartBoudaries.maxT;
chartTHolder.update();

Your code would have to be changed as follows. Note that chartTHolder is defined outside the function GenerateRuntimeGraph.

let chartTHolder = null;
function GenerateRuntimeGraph(id, days) {
  $.ajax({
    type: "POST",
    url: `/Devices/ChartData?id=${id}&days=${days}`,
    success: function(chData) {
      let chDataObj = JSON.parse(chData);
      if (chartTHolder) {
        chartTHolder.data.datasets[0].data = chDataObj.dataListT; 
        chartTHolder.options.scales.yAxes[0].ticks.min = chDataObj.dataChartBoudaries.minT;
        chartTHolder.options.scales.yAxes[0].ticks.max = chDataObj.dataChartBoudaries.maxT;
        chartTHolder.update();
      } else {    
        var options = { 
          ... 
        };
        var ctx = document.getElementById('myChartT').getContext('2d');
        chartTHolder = new Chart(ctx, options);
      }
    }
  });
}
uminder
  • 14,658
  • 3
  • 20
  • 45
  • It seems very reasonable. But code never enters the "update" section, because chartTHolder is undefinied at this point of script. – Krzysztof Patra Sep 14 '20 at 20:56
  • Thanks uminder. It helped. I had to move the declaration of chartTHolder up to the first page load. So I did this: `` – Krzysztof Patra Oct 04 '20 at 09:51