5

I've just started using ng-table and I'm trying to test it through jasmine unit tests (not sure if this is the best way to test my controllers) and I keep running into this error;

TypeError: Cannot set property '$data' of null
    at project/vendor/assets/javascripts/ng-table-0.3.1/ng-table.src.js:396:55
    at C (project/vendor/assets/javascripts/angular-min.js:92:375)
    at project/vendor/assets/javascripts/angular-min.js:94:28
    at h.$eval (project/vendor/assets/javascripts/angular-min.js:102:308)
    at h.$digest (project/vendor/assets/javascripts/angular-min.js:100:50)
    at h.$apply (project/vendor/assets/javascripts/angular-min.js:103:100)
    at f (project/vendor/assets/javascripts/angular-min.js:67:98)
    at handleResponse (project/vendor/assets/javascripts/angular-mocks.js:1160:9)
    at Function.$httpBackend.flush (project/vendor/assets/javascripts/angular-mocks.js:1491:26)
    at null.<anonymous> (project/spec/javascripts/unit/debtors_controller_spec.js.js:50:24)

Here is where I setup the ngTableParams in my controller

  # Setup ng-table with defaults and data
  $scope.tableParams = new ngTableParams({
    page: 1,
    count: 10,
    sorting: { code: 'asc'}
  }, {
    # data is created by ngTableParams
    total: 0,

    # ng-table will ask for updated data, this is where it gets it from.
    getData: ($defer, params) ->
      # Go get a list of debtors
      Debtor.query {}, (data) ->
        # Once successfully returned from the server with my data process it.
        params.total(data.length)

        # Filter
        filteredData = (if params.filter then $filter('filter')(data, params.filter()) else data)

        # Sort
        orderedData = (if params.sorting then $filter('orderBy')(filteredData, params.orderBy()) else data)

        # Paginate
        $defer.resolve(orderedData.slice((params.page() - 1) * params.count(), params.page() * params.count()))
  })

Here is the jasmine test

  # ================================================================================
  # Delete tests
  # ================================================================================
  describe "destroy", ->
    beforeEach(inject ( ($controller, $rootScope, $location, $state, $httpBackend) ->
      ctrl = $controller('DebtorsController', {
        $scope: @scope,
        $location: $location
      })
    ))

    it "gets new listing", ->
      @scope.debtors = [{id: 1, name: "Good guys"}, {id: 2, name: "John"}]
      @httpBackend.whenDELETE('/clients/1').respond(200)
      @httpBackend.whenGET('/debtors').respond(200,  [{name: "John"}])
      @scope.destroy(1)
      @httpBackend.flush()
      expect(@scope.debtors[0].name).toEqual('John')

Here is my code for deleting (which works)

  # --------------------------------------------------------------------------------
  # Delete
  # --------------------------------------------------------------------------------
  $scope.destroy = (id) ->
    Client.delete
      id: id
    , (response) ->             # Success
      flash("notice", "Client deleted", 2000)
      $scope.tableParams.reload() # Reload ng-table data, which will get a new listing from the server.
    , (response) ->             # Failure
map7
  • 5,096
  • 6
  • 65
  • 128

1 Answers1

6

Can you show the code in your controller where you initialize ngTableParams?

Problem is ng-table uses ngTableController to set the $scope for ngTableParams. Since you are unit testing you wont have a ngTableController so $scope of ngTableParams will be null. You should manually set $scope of ngTableParams in your unit test. Try something similar to this JS (really dislike coffeescript, so convert to your needs):

beforeEach(inject(function ($controller, $rootScope) {
            scope = $rootScope.$new();
            ctrl = $controller('DebtorsController', {
                $scope: scope,
                $location: $location
            });
            scope.tableParams.settings().$scope = scope;
        }));

Assuming you have something like the following in your controller where you initialize your ngTableParams:

$scope.tableParams = new ngTableParams({
    page: 1,            // show first page
    count: 10           // count per page
}, {
    total: data.length, // length of data
    getData: function($defer, params) {
        $defer.resolve(data.slice((params.page() - 1) * params.count(), params.page() * params.count()));
    }
});
Beyers
  • 8,968
  • 43
  • 56
  • Using ctrl.ngTableParams.settings().$scope = scope or ctrl.tableParams.settings().$scope = scope doesn't seem to work, I get the error cannot call method 'settings' of undefined – map7 Feb 23 '14 at 23:58
  • @map7 As per the first line in my answer: "Can you show the code in your controller where you initialize ngTableParams?" You need to reference your ngTableParams variable that you should expose on your controller. For instance if you have: ` $scope.tableParams = new ngTableParams(......)` inside your controller, then your unit test should have `scope.tableParams.settings().$scope = scope;` See updated answer. – Beyers Feb 25 '14 at 00:14
  • I've updated my question with my controller. The test is getting a little further. Now it's complaining about the expect line, instead of equaling 'John' it's still set to both 'good guys' and 'john'. If I test it manually through the browser it works. Should I be checking @scope.debtors[0].name or should I be checking something else for the end result? – map7 Feb 25 '14 at 00:47
  • Found that I should be doing my expect on the following: @scope.tableParams.data[0].name by using the debugger in chrome. Thanks for your detailed answer. – map7 Feb 25 '14 at 02:00
  • $scope.$watch('initialApiCall', function() { $scope.customerList.settings().$scope = $scope; $scope.customerList.reload(); }); InitialApiCall scope is table data that i assigned. CustomerList is a reference for ngTableParams. But it cannot load the correct json Table. Is anything wrong in my Code. Please help me. – kuppu May 08 '15 at 18:17