Mike Pennington
Mike Pennington

Reputation: 43087

AngularJS ngTable delays display of ajax data

Background

I am struggling with the finer points of using ngTable. I went through the ngTable ajax demo and tried to follow the example as reasonably as possible; however, I need to deviate from the part where the ngTable ajax demo defines an ngResource inline with the Controller.

Problem

I successfully built an ngTable with ngTable filters; My webapp is here (also see the plunker). This is an ngTable which:

The json REST service loads correctly, and the filters work. However, my problem is that the table is blank until I type something in the Filter field (see the before and after screenshots below).

The problem exists with all browsers I have tried:

I know the webapp is broken in Internet Exploder 9... I don't know why, but I really don't care about IE; this webapp is not going to be a public web service.

Questions

Note: The source code is at the bottom of the page... I built a plunker for it.


End Notes

Note 1Sharp-eyed readers may notice that I'm using [[ and ]] in my AngularJS form instead of the default curly-brace delimiters. I did this because I'm using Flask with Jinja (which also needs double curly-braces for its templating engine)..


Screen shots

BEFORE: Page is blank when I load it:

blank ngTable

AFTER: However, if I type any character in the filters, the ngTable displays the data:

ngTable with filter


Update: Explicit Problem Resolution, thanks to lib3d's answer

<script type="text/javascript">

    "use strict";
    // Set up an ngResource service to make HTTP GET / POST / DELETE calls
    var Api = angular.module("api_main", ["ngResource"]);
    Api.factory("restDemo", function ($resource) {
        // http://www.masnun.com/2013/08/28/rest-access-in-angularjs-using-ngresource.html
        return $resource("http://demo.pennington.net/demo/api/v1/data01", {}, {});
    });

    // Loosely based on this ngTable demo...
    //       http://bazalt-cms.com/ng-table/example/6
    var App2 = angular.module('taskTable', ['ngRoute', 'api_main', 
        'ngTable']);
    // Need to change AngularJS symbols when using flask + Jinja
    App2.config(function($interpolateProvider) {
        $interpolateProvider.startSymbol('[[');
        $interpolateProvider.endSymbol(']]');
    });

    App2.controller('tableCntl', function($scope, $filter, restDemo, ngTableParams) {


        var data = restDemo.query(); // HTTP GET for REST service


        // Set up task table parameters
        /* Lib3d's fix (i.e. data.$promise) is below */
        data.$promise.then(function (data) {
            /* the data is here, work with it */
            $scope.tableParams = new ngTableParams({
                page: 1,            // show first page
                count: 10,          // count per page
                total: data.length,
                sorting: {
                    Column01: 'asc',
                    Column02: 'asc',
                    Column03: 'asc'
                },
                filter: {
                    Column01: "",
                    Column02: "",
                    Column03: "",
                }
            }, {
                getData: function($defer, params) {

                    // Filtering
                    var orderedData = params.filter() ?
                           $filter('filter')(data, params.filter()) :
                           data;

                    // Sorting
                    orderedData = params.sorting() ?
                        $filter('orderBy')(orderedData, params.orderBy()) :
                        orderedData;
                   $defer.resolve(orderedData.slice((params.page() - 1) * params.count(),  params.page() * params.count()));

                    /* set total for recalc pagination */
                    params.total(orderedData.length);

                    // This shouldn't be required, but keeping here in case
                    //if(!$scope.$$phase) {
                    //    $scope.$apply();
                    //}
                }
            });
        });
    });
</script>

The broken demo source code

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="stylesheet" href="http://yui.yahooapis.com/pure/0.4.2/pure-min.css">
    <link rel="stylesheet" href="//cdn.jsdelivr.net/angular.ngtable/0.3.1/ng-table.css">
    <!--
    <link rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/themes/smoothness/jquery-ui.css">
    <link rel="stylesheet" type="text/css" href="http://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/css/jquery.dataTables.css">
    -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
      body {
        padding-top: 30px; /* 30px pad at the top of screen */
      }
    </style>
    <!-- Fix up title -->
    <title>DATA DEMO</title>
  </head>
  <body>
    <div class="pure-g">
    </div>
    <!-- Static navbar -->
    <div class="pure-g">
      <div id="menu" class="pure-u">
        <div class="pure-menu pure-menu-horizontal pure-menu-open">
          <ul>
            <li><a href="#">Add Demo</a></li>
            <li class="active"><a href="#">List Demo</a></li>
            <li><a href="#">List Projects</a></li>
            <li>
                <a href="/login">Login</a>
          </ul>
        </div><!--/.nav-collapse -->
      </div>
    </div>
    <!--- insert stuff here -->
  <div id="Tasks" ng-app="taskTable" ng-controller="tableCntl">
    <p><strong>Filter:</strong> [[tableParams.filter()|json]]
    <table ng-table="tableParams" show-filter="true" class="table">
        <tbody>
          <tr ng-repeat="words in $data">
              <td data-title="'Column01'" sortable="Column01" filter="{'Column01': 'text'}">
                  [[words.Column01]]
              </td>
              <td data-title="'Column02'" sortable="Column02" filter="{'Column02': 'text'}">
                  [[words.Column02]]
              </td>
              <td data-title="'Column03'" sortable="Column03" filter="{'Column03': 'text'}">
                  [[words.Column03]]
              </td>
          </tr>
        </tbody>
    </table>
  </div>
  </body>
  <!-- insert JS at the bottom of the page -->
  <footer>
    <!-- all flash messages are processed here -->
    <script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
    <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.min.js"></script>
    <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular-route.min.js"></script>
    <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular-resource.min.js"></script>
    <script type="text/javascript" src="//cdn.jsdelivr.net/angular.ngtable/0.3.1/ng-table.js"></script>
    <!--
    <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.5.1/moment.min.js"></script>
    -->

    <script type="text/javascript">

        "use strict";
        // Set up an ngResource service to make HTTP GET / POST / DELETE calls
        var Api = angular.module("api_main", ["ngResource"]);
        Api.factory("restDemo", function ($resource) {
            // http://www.masnun.com/2013/08/28/rest-access-in-angularjs-using-ngresource.html
            return $resource("http://demo.pennington.net/demo/api/v1/data01", {}, {});
        });

        // Loosely based on this ngTable demo...
        //       http://bazalt-cms.com/ng-table/example/6
        var App2 = angular.module('taskTable', ['ngRoute', 'api_main', 
            'ngTable']);
        // Need to change AngularJS symbols when using flask + Jinja
        App2.config(function($interpolateProvider) {
            $interpolateProvider.startSymbol('[[');
            $interpolateProvider.endSymbol(']]');
        });

        App2.controller('tableCntl', function($scope, $filter, restDemo, ngTableParams) {


            var data = restDemo.query(); // HTTP GET for REST service
            // Set up task table parameters
            $scope.tableParams = new ngTableParams({
                page: 1,            // show first page
                count: 10,          // count per page
                total: data.length,
                sorting: {
                    Column01: 'asc',
                    Column02: 'asc',
                    Column03: 'asc'
                },
                filter: {
                    Column01: "",
                    Column02: "",
                    Column03: "",
                }
            }, {
                getData: function($defer, params) {
                    // use build-in angular filter
                    var orderedData = params.filter() ?
                           $filter('filter')(data, params.filter()) :
                           data;

                    //sorting
                    orderedData = params.sorting() ?
                        $filter('orderBy')(orderedData, params.orderBy()) :
                        orderedData;

                    // store filtered data as $scope.words
                    $scope.words = orderedData.slice((params.page() - 1) * params.count(), params.page() * params.count());
                    params.total(orderedData.length); // set total for recalc pagination
                    $defer.resolve($scope.words);
                    $defer.resolve(data.result);
                }
            });
        });
    </script>
    <!-- Custom footer content here -->

  </footer>
</html>


The data (I will delete the demo json REST service after the question is answered):

[{"Column02": "shines", "Column03": "paycheck", "Column01": "days"}, 
 {"Column02": "erg", "Column03": "gag", "Column01": "emotion's"}, 
 {"Column02": "Chris", "Column03": "Poznan's", "Column01": "treasure's"}, 
 {"Column02": "presentiments", "Column03": "Austerlitz's", "Column01": "suppression's"}, 
 {"Column02": "leopards", "Column03": "slosh's", "Column01": "upturned"}, 
 {"Column02": "uncaring", "Column03": "cosmetics", "Column01": "symmetry"}, 
 {"Column02": "guesser's", "Column03": "lapped", "Column01": "retrogressed"},
 {"Column02": "Kurd", "Column03": "wryest", "Column01": "cicadas"}, 
 {"Column02": "cantered", "Column03": "encrustation's", "Column01": "beyond"}, 
 {"Column02": "flybys", "Column03": "poesying", "Column01": "physician's"}, 
 {"Column02": "fun", "Column03": "Delaware's", "Column01": "destructiveness"}, 
 {"Column02": "scramblers", "Column03": "gestates", "Column01": "acoustics"}, 
 {"Column02": "redesigning", "Column03": "cubits", "Column01": "Enterprise"}, 
 {"Column02": "phonograph's", "Column03": "haloed", "Column01": "upsurge"}, 
 {"Column02": "Michelson", "Column03": "Pansy", "Column01": "McCoys"}, 
 {"Column02": "adieu", "Column03": "Dido", "Column01": "ligaturing"}, 
 {"Column02": "osprey's", "Column03": "expressiveness's", "Column01": "Starr"}, 
 {"Column02": "patent's", "Column03": "therapeutically", "Column01": "brasher"}, 
 {"Column02": "enfranchise", "Column03": "idolized", "Column01": "criticized"}, 
 {"Column02": "Angel", "Column03": "wryest", "Column01": "drum"}, 
 {"Column02": "overstaying", "Column03": "tranquillized", "Column01": "alacrity"}, 
 {"Column02": "underachievers", "Column03": "minority", "Column01": "Brigham's"}, 
 {"Column02": "lobotomy's", "Column03": "filament's", "Column01": "scoldings"}, 
 {"Column02": "original", "Column03": "muskmelon's", "Column01": "financially"}, 
 {"Column02": "flagon's", "Column03": "vapidness", "Column01": "Klaus's"}, 
 {"Column02": "dhotis", "Column03": "fleeter", "Column01": "jugulars"}, 
 {"Column02": "shanty", "Column03": "profiteer's", "Column01": "disbelief"}, 
 {"Column02": "bureaucracies", "Column03": "flashier", "Column01": "refrigerating"}, 
 {"Column02": "betrayal's", "Column03": "hindquarters", "Column01": "faze"}, 
 {"Column02": "Poland", "Column03": "cobbler", "Column01": "kidnaped"}]

I'm using...

What I already tried

None of these things seemed to have an effect on the problem:

If I inline the json data inside the <script> block instead of making an ajax json REST service call, the table populates correctly when the page loads, but that really doesn't help since the data changes from time to time.

Upvotes: 6

Views: 6654

Answers (1)

atondelier
atondelier

Reputation: 2434

Your data are asynchronously fetched but you use it synchronously. Then first getData call uses unresolved data, which means no data at all.

Typing a character seems to recall getData with, this time, resolved data. That would explain data showing on first key typed.

To wait for data being fetched, implement ngTable call in such a callback:

/* there, data are not fetched */
data.$promise.then(function (data) {
    /* there data are fetched, work with it */
});

Upvotes: 4

Related Questions