skip to Main Content

I have a Django template html page that includes a form for users to upload a data file in JSON format. This data file is then processed, and the data is sent to my PostgreSQL database. The page displays a DataTables table populated by data from the database and three D3 charts.

Currently, when I upload a file, I can see the datatable in its raw form for several seconds before the table is fully created. As the table is large, it obscures my D3 chart’s initial animations. Ideally, I would like the initial datatable creation to be obscured by a ‘loading.gif’. When the datatable creation is complete, I want the user to see the fully formed datatable, and only then do I want the D3 charts to be created/rendered on the page.

I have included a loading.gif in my static folder, which correctly displays after file upload. However, after several seconds, the loading.gif disappears, and then, as before, I can briefly see the data in its raw form before the full datatable displays. I am unsure how to completely obscure the datatable creation (with the loading.gif) until the table is fully formed.

Here is my DataTables script (below). As you can see, I have a ‘loading’ indicator at the top of the script set to ‘block,’ and I set it to ‘none’ when the datatable is complete. I thought this would obscure the table creation until it was complete, but this is not the case.

<!-- DATATABLES -->
      <script type="text/javascript">
        $(document).ready(function () {
          $('form').submit(function () {
            // Show the loading indicator
            $('#loading-indicator').css('display', 'block');
          });

          var table = $('#example').DataTable({
            colReorder: true,
            fixedColumns: false,
            responsive: false,
            paging: true,
            scrollY: '40vh',
            scrollX: true,
            autoWidth: true,
            pageLength: 15,
            dom: 'Bfrtip',
            buttons: [
              {
                extend: 'colvis',
                postfixButtons: ['colvisRestore'],
                text: 'Column Selection'
              },
              {
                text: 'Download CSV',
                extend: 'csv',
                exportOptions: {
                  columns: ':visible'
                }
              }
            ],
            columnDefs: [
              { width: '60px', targets: 0, className: 'text-left' },
              { width: '200px', targets: -1, className: 'text-left' }
            ],
            initComplete: function () {
              $('#loading-indicator').css('display', 'none');
              $('#example_wrapper').css('visibility', 'visible');
            }
          });
        });
      </script>

Here is my full html template page:

{% block content %}
{% load static %}
<!DOCTYPE html>
<html>
<head>
  <title>Test</title>
  <link rel="shortcut icon" type="image/x-icon" href="{% static 'favicon.ico' %}">

  <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.2.0/css/bootstrap.min.css" rel="stylesheet"/>

  <script type="text/javascript" src="https://code.jquery.com/jquery-3.5.1.js"></script>

  <link href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" rel="stylesheet"/>
  <script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>

  <link href="https://cdn.datatables.net/v/bs5/dt-1.13.4/b-2.3.6/b-colvis-2.3.6/b-html5-2.3.6/cr-1.6.2/r-2.4.1/sb-1.4.1/sp-2.1.2/sl-1.6.2/datatables.min.css" rel="stylesheet"/>
  <script src="https://cdn.datatables.net/v/bs5/dt-1.13.4/b-2.3.6/b-colvis-2.3.6/b-html5-2.3.6/cr-1.6.2/r-2.4.1/sb-1.4.1/sp-2.1.2/sl-1.6.2/datatables.min.js"></script>

  <script src="{% static 'd3_dots.js' %}"></script>
  <script src="https://d3js.org/d3.v7.js"></script>
  <script src="{% static 'd3_bar.js' %}"></script>
  <style>
    #example_wrapper {
      visibility: hidden;
    }
    table {
      font-size: 12px;
    }
    .dataTables_wrapper .dataTables_paginate .paginate_button {
      padding: 1px;
      background: white;
      color: white;
    }
    .dataTables_wrapper .dataTables_paginate .paginate_button:hover {
      background-color: red;
    }
    .dataTables_wrapper .dt-button-collection .buttons-columnVisibility .dt-button.active {
      background-color: red;
    }
    .text-left {
      text-align: left !important;
      white-space: nowrap;
    }
    .buttons-collection .dt-button:first-child {
      background-color: red !important;
      color: white !important;
    }
    .buttons-csv {
      background-color: red !important;
      color: white !important;
    }
    .upload-form button {
      border-radius: 5px;
      background: #E9ECEF;
      border: 1px solid #CED4DA;
    }
    .bar {
      fill: #0c6cfc;
      stroke:  #FFA500;
      stroke-width: 1px;
    }
    .bar:hover {
      fill: #FFA500;
      stroke: #000000;
      stroke-width: 2px;
    }
    #chart-wrapper {
      display: flex;
      flex-direction: row;
    }
    #chart-level-assign,
    #chart-edu-setting
    #chart-smd-scatter {
      width: 33.33%;
    }
    #loading-indicator {
      display: none;
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background-color: rgba(255, 255, 255, 0.9);
      z-index: 9999;
      text-align: center;
      padding-top: 20%;
      background-image: url('/static/loading.gif');
      background-repeat: no-repeat;
      background-position: center;
    }
  #loading-indicator img {
      width: 100px;
      height: 100px;
  }
  </style>
</head>
<body>
<div id="loading-indicator">
</div>
<div class="container mt-5">
  <div class="row">
    <div class="col-md-12">
      <form method="post" enctype="multipart/form-data" class="upload-form">
        {% csrf_token %}
        <div class="row mb-3">
          <div class="col-md-6">
            {{ form.json_file }}
          </div>
              <div class="col-md-6 d-flex align-items-left justify-content-end">
                <button type="submit" name="upload_button" class="btn-primary">Upload</button>
              </div>
        </div>
      </form>
        {% if has_data %}
        <br>
          <table id="example" class="display hover">
            <thead>
              <tr>
                <th>#</th>
                <th>EPPI ID</th>
                <th>Admin Strand</th>
                <th>Educational Setting</th>
              </tr>
            </thead>
              <tbody>
                {% for data_object in data %}
                <tr>
                  <th>{{ forloop.counter }}</th>
                  <td>{{ data_object.eppi_id }}</td>
                  <td>{{ data_object.admin_strand_data }}</td>
                  <td>{{ data_object.edu_setting_data }}</td>
                </tr>
                {% empty %}
                <tr>
                  <td colspan="3">No data to display.</td>
                </tr>
                  {% endfor %}
              </tbody>
            </table>
        </div>
        {% endif %}
        <br>
        <br>
        <div id="chart-wrapper">
        <div id="chart-level-assign"></div>
        <div id="chart-edu-setting"></div>
        <div id="chart-smd-scatter"></div>
      </div>

      <!-- DATATABLES -->
      <script type="text/javascript">
        $(document).ready(function () {
          $('form').submit(function () {
            // Show the loading indicator
            $('#loading-indicator').css('display', 'block');
          });
        
          var table = $('#example').DataTable({
            colReorder: true,
            fixedColumns: false,
            responsive: false,
            paging: true,
            scrollY: '40vh',
            scrollX: true,
            autoWidth: true,
            pageLength: 15,
            dom: 'Bfrtip',
            buttons: [
              {
                extend: 'colvis',
                postfixButtons: ['colvisRestore'],
                text: 'Column Selection'
              },
              {
                text: 'Download CSV',
                extend: 'csv',
                exportOptions: {
                  columns: ':visible'
                }
              }
            ],
            columnDefs: [
              { width: '60px', targets: 0, className: 'text-left' },
              { width: '200px', targets: -1, className: 'text-left' }
            ],
            initComplete: function () {
              $('#loading-indicator').css('display', 'none');
              $('#example_wrapper').css('visibility', 'visible');
            }
          });
        });
      </script>
      
      
      
      <!-- D3 CHARTS -->
      <script type="text/javascript">
        // First chart
        const assign_level_rawData = JSON.parse('{{ assign_levels|safe }}');
        console.log(assign_level_rawData)
        for (let i = 0; i < assign_level_rawData.length; i++) {
          if (assign_level_rawData[i][0] === "N") {
            assign_level_rawData[i][0] = "NA";
          }
        }
        const data1 = assign_level_rawData.map(d => ({level_assign: d[0], count: d[1]}));
        createChart(data1, "chart-level-assign", "Level of Assignment", "level_assign");
        
        // Second chart
        const edu_setting_rawData = JSON.parse('{{ edu_setting_levels|safe }}');
        for (let i = 0; i < edu_setting_rawData.length; i++) {
          if (edu_setting_rawData[i][0] === "N") {
            edu_setting_rawData[i][0] = "NA";
          }
        }
        const data2 = edu_setting_rawData.map(d => ({edu_setting: d[0], count: d[1]}));
        createChart(data2, "chart-edu-setting", "Educational Setting", "edu_setting");

        // Third chart
        const data3 = "{{ smd_combined_csv|escapejs }}";
        const smd = d3.csvParse(data3).filter(d => d.smd_value !== "NA");
        drawDotChart(smd);
      </script>
        

</body>
</html>
{% endblock %}

2

Answers


  1. You can use “draw” event delegate instead of “initComplete”.

    <script type="text/javascript">
            $(document).ready(function () {
              $('form').submit(function () {
                // Show the loading indicator
                $('#loading-indicator').css('display', 'block');
              });
    
              var table = $('#example').DataTable({
                colReorder: true,
                fixedColumns: false,
                responsive: false,
                paging: true,
                scrollY: '40vh',
                scrollX: true,
                autoWidth: true,
                pageLength: 15,
                dom: 'Bfrtip',
                buttons: [
                  {
                    extend: 'colvis',
                    postfixButtons: ['colvisRestore'],
                    text: 'Column Selection'
                  },
                  {
                    text: 'Download CSV',
                    extend: 'csv',
                    exportOptions: {
                      columns: ':visible'
                    }
                  }
                ],
                columnDefs: [
                  { width: '60px', targets: 0, className: 'text-left' },
                  { width: '200px', targets: -1, className: 'text-left' }
                ]
              });
              table.on('draw', function () {
                 $('#loading-indicator').css('display', 'none');
                 $('#example_wrapper').css('visibility', 'visible');
               });
            });
    </script>
    
    Login or Signup to reply.
  2. While the DataTable is loading, you can also use the processing option to display a loading message or animation instead of initComplete and draw.

    $('#datatable').DataTable({
      // Some other code
      processing: true,
      language: {
        processing: '<i class="fa fa-spinner fa-spin"></i> Data Loading...'
      }
    });
    

    Here we define processing as true and display a loading message using the language option. The custom message will be automatically deleted after the DataTable is loaded.

    You can personalize the loading message. In this snippet, a spinner animation is displayed using an icon from the Font Awesome icon set.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search