1

I'm trying to build an AngularJS app, which outputs an HTML table that I populate with json (the table's HTML is at the bottom of this question). I'm using application/json data that I retrieve from my server.

When I do a simple curl http://myurl.local/todo/api/v1/tasks, I get json with no problems. If I put a console.log(); inside this block, I'm clearly getting the json from the server.

        getJson: function() {
            var url = 'http://myurl.local/todo/api/v1/tasks';
            var promise = $http.get(url);
            return promise.then(function(result) {
                console.log("Got data ->" + result.data);
                return result.data;
            });
        }

I'm using Chrome to develop the app; when I run the app in Chrome, Chrome's javascript console throws this error: TypeError: Cannot read property 'length' of undefined before I get to that console.log("Got data ->" + result.data); line. It's a race condition.

The error is very clearly right here:

        $scope.tableParams = new ngTableParams({
            page: 1,            // show first page
            count: 10,          // count per page
        }, {
            total: data.length, // length of data   <--- Broken, needs a promise?

The problem is that javascript isn't my primary language (it's Python); based on the googling I've done about this problem, I think I could fix this with a promise / .then() in the right place. However I'm having difficulty understanding exactly how I should implement this in my javascript. I would like to find the way to fix the async json GET, and understand why it needs to be done that way.

Could someone explain how I should fix this, and why I should do it that way?


The JavaScript:

    var App2 = angular.module('taskTable', ['ngRoute', 'ngTable']);
    // Need to change AngularJS symbols when using flask + Jinja
    App2.config(function($interpolateProvider) {
        $interpolateProvider.startSymbol('[[');
        $interpolateProvider.endSymbol(']]');
    });

    // Thank you PeteBD
    //     http://stackoverflow.com/a/12513509/667301
    // Set up a controller to get json tasks...
    App2.factory('getTasks', function($http) {
        return {
            getJson: function() {
                var url = 'http://myurl.local/todo/api/v1/tasks';
                var promise = $http.get(url);
                return promise.then(function(result) {
                    return result.data;
                });
            }
        }
    });
    App2.controller('tableCntl', function($scope, getTasks, $filter, 
        ngTableParams) {

        var data = [];
        getTasks.getJson().then(function(data) {
            $scope.data = data;
        });
        data = $scope.data;
        // Set up task table parameters
        $scope.tableParams = new ngTableParams({
            page: 1,            // show first page
            count: 10,          // count per page
        }, {
            total: data.length, // length of data
            getData: function($defer, params) {
                // use build-in angular filter
                var orderedData = params.filter() ?
                       $filter('filter')(data, params.filter()) :
                       data;
                // store filtered data as $scope.tasks
                var pageCount = params.page();
                var paramCount = params.count();
                $scope.tasks = orderedData.slice((pageCount-1)*paramCount, 
                    pageCount*paramCount);
                // set total for recalc pagination
                params.total(orderedData.length);
                $defer.resolve($scope.tasks);
            }
        });
    });
    // Props... angular.bootstrap() is required if two apps on the same page
    // http://stackoverflow.com/a/18583329/667301
    angular.bootstrap(document.getElementById("Tasks"),["taskTable"]);

The HTML table via ngTable:

  <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="task in $data">
              <td data-title="'Description'" sortable="description" 
                  filter="{'description': 'text'}">
                  [[task.description]]
              </td>
              <td data-title="'Priority'" sortable="priority" 
                  filter="{'priority': 'text'}">
                  [[task.priority]]
              </td>
              <td data-title="'Entered'" sortable="entry">
                  [[task.entry]]
              </td>
              <td data-title="'Status'" sortable="status">
                  [[task.status]]
              </td>
          </tr>
        </tbody>
    </table>
  </div>
2
  • hi, is this syntax correct: [[task.description]], you should use {{ task.description}} rite, If possible, post this in jsfiddle.net Commented Mar 12, 2014 at 11:26
  • Hi @MikePennington The code that you show here, with all the solutions works well?. I get data as Undefined, but if I log data inside getTasks.getJson().then() I get all the data that I expect. Is there another code related with this approach about asynchronously populating an AngularJS ngTable with JSON data? Thanks in advance. Commented Apr 28, 2016 at 16:35

1 Answer 1

3

Definitely worth looking at ngResource - this will simply take out all the heavy lifting for you.

On another note, I suspect you don't need to do this:

App2.factory('getTasks', function($http) {
    return {
        getJson: function() {
            var url = 'http://myurl.local/todo/api/v1/tasks';
            var promise = $http.get(url);
            return promise.then(function(result) {
                return result.data;
            });
        }
    }
});

i would change it to :

 App2.factory('getTasks', function($http) {
    return {
        getJson: function() {
            var url = 'http://myurl.local/todo/api/v1/tasks';
            return $http.get(url);
        }
    }
});

As $http returns the promise. That means this bit still works:

    getTasks.getJson().then(function(data) {
        $scope.data = data;
    });
Sign up to request clarification or add additional context in comments.

2 Comments

Finally found the rest of the problem... I had to change $defer.resolve($scope.tasks); to $defer.resolve(data);. ngResource does simplify the code.
Cool! Glad you got it sorted!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.