Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

  • Loads data from a json REST service
  • Builds the ngTable from the json data
  • Sorts
  • Filters

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:

  • Firefox 27.0.1 (Windows Vista)
  • Chrome Version 33.0.1750.146 (Windows Vista)
  • Chrome Version 32.0.1700.123 (Linux - Debian 7.0 Wheezy)

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

  • How do I make the table populate the first time I load my webapp (without typing anything in the filter boxes)?
  • Why is this broken now?

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...

  • Yahoo PureCSS version 0.4.2
  • jQuery version 2.0.3
  • AngularJS version 1.2.14Note 1

What I already tried

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

  • Remove jQuery
  • Remove Yahoo PureCSS
  • Change default values on the ngTable Filters
  • Remove all other javascript from the page (this actually had multiple AngularJS controllers on the same page, but removed all others in hopes that it was just a simple script conflict.
  • Several experimental what-if-I-modify-this (read: grasping at straws) in the AngularJS controller which builds the ngTable.
  • Tried using null instead of empty strings in my filter, as suggested by Sander Elias

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.

like image 402
Mike Pennington Avatar asked Mar 15 '14 20:03

Mike Pennington


1 Answers

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 */
});
like image 120
lib3d Avatar answered Sep 21 '22 20:09

lib3d