enguerranws
enguerranws

Reputation: 8233

Angular filter Ajax loaded data (JSON)

Here's my problem folks,

I'm pretty new to AngularJS, and I made a simple list of items. I can search into this items and I even putted a pagination in there.

This was working great, and I want to put my list outside my controller, as a json file.

This is what I've done :

HTML

<!DOCTYPE html>
<!--[if lt IE 7]>      <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]>         <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]>         <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" > <!--<![endif]-->
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>Test angular</title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <script type="text/javascript" src="js/angular.min.js"></script>
        <script type="text/javascript" src="js/controllers.js"></script>
        <!-- Place favicon.ico and apple-touch-icon.png in the root directory -->
    </head>
    <body >

      <section class="app" ng-app="myApp" data-scope="$scope = myApp" > 

         <div ng-controller="myCtrl" data-scope="$scope = myApp.myCtrl">
            <input type="text" ng-model="search" data-scope="$scope = myApp.myCtrl.items(repeater scope)">
            Recherche = {{ search }}
            <div class="item" ng-repeat="item in newItems | filter:search | startFrom:currentPage*pageSize | limitTo:pageSize">
              {{ item.name }}
            </div>
             <button ng-disabled="currentPage == 0" ng-click="currentPage=currentPage-1">
              Previous
          </button>
          {{currentPage+1}}/{{numberOfPages()}}
          <button ng-disabled="currentPage >= items.length/pageSize - 1" ng-click="currentPage=currentPage+1">
              Next
          </button>
          </div>

      </section>



    </body>
</html>

Note : as I'm learning, the data-scope attributes are only there to help me see what is the current scope

controllers.js

var myApp = angular.module('myApp', []); // creating the module myApp;

// registering startFrom filter
myApp.filter('startFrom', function() {
    return function(input, start) {
        start = +start; //parse to int
        return input.slice(start);
    }
});

// configure the module myApp with MyCtrl controller.
myApp.controller('myCtrl', function($scope, $interval, $filter, $http){
    $scope.currentPage = 0;
    $scope.pageSize = 10;
    $scope.numberOfPages=function(){
        return Math.ceil($scope.items.length/$scope.pageSize);                
    }
    $scope.$watch('search', function () {
        $scope.currentPage = 0;
        $scope.newItems = $filter('filter')($scope.items, $scope.search);
        $scope.numberOfPages=function(){
            return Math.ceil($scope.items.length/$scope.pageSize);             
        }
    });   
    $scope.newItems = $scope.items;
    $http.get('js/items.json') // go get my json file
       .then(function(res){
          $scope.items = res.data; // set this data to be my items             
        });


});

The json loads great (in the network panel, it's ok), but I guess the startFrom filter I'm using is making some errors in the JSconsole :

TypeError: Cannot call method 'slice' of undefined

My hypothesis is : the filter try to slice something that is not defined right now. Just as if $scope.items is defined after the filter does his job.

EDIT :

No more errors since I declared $scope.items = []; as VtoCorleone said, but now, there is the real problem : the first page of the list doesn't appear, it's just empty. But the pagination works.

My suggestion : I set two properties for items (items, newItems), items is the original json, newItems is the filtered results. With items, I can keep all my items, and restore them if needed.

With Angular inspector, on page load, I see items is filled with my JSON, but newItems is empty. (and yeah, the ng-repeat is on newItems :) ) Why is it empty ?

Upvotes: 0

Views: 3383

Answers (3)

Gwivv
Gwivv

Reputation: 1

I had same error,

Just add a line to your filter : "input = input || '';"

.filter('startFrom', function() {
    return function(input, start) {
        input = input || '';
        start = parseInt(start,10);
        return input.slice(start);
    }
});

During loading of page, filter is called but the value is not loaded at this time "input" is undefined.

++

Upvotes: 0

enguerranws
enguerranws

Reputation: 8233

I found it.

myApp.controller('myCtrl', function($http, $scope, $interval, $filter){



    $scope.items = [];
     $http.get('js/items.json')
       .then(function(res){
          $scope.items = angular.fromJson(res.data);   
          $scope.newItems = angular.fromJson(res.data); // I set $scope.newItems directly in the Ajax response.
        });
    //$scope.newItems = $scope.items; // this doesn't work, don't know why.
    $scope.currentPage = 0;
    $scope.pageSize = 10;
    $scope.numberOfPages=function(){
        return Math.ceil($scope.newItems.length/$scope.pageSize);                
    }
    $scope.$watch('search', function () {
        $scope.currentPage = 0;
        $scope.newItems = $filter('filter')($scope.items, $scope.search);
        $scope.numberOfPages=function(){
            return Math.ceil($scope.newItems.length/$scope.pageSize);             
        }
    });  
    $scope.$watch('pageSize', function () {
        $scope.currentPage = 0;
        $scope.numberOfPages=function(){
            return Math.ceil($scope.newItems.length/$scope.pageSize);             
        }
    });       

});

Finally, I set $scope.newItems directly in the Ajax response. On page load, newItems is already filled with all the elements. Then I updated numberOfPages(); to update the number of pages on the newItems.

$scope.newItems = $scope.items;

If someone can tell me why this line doesn't work as expected (newItems is empty on page load), please tell me :)

Upvotes: 0

Chi Row
Chi Row

Reputation: 1106

As for why the page is not displaying:

You only have $watch on 'search'. So when items get updated from ajax callback, your numberOfPages is still remains at 0. As soon as you start searching, watch kicks in and your numberOfPages get updated.

You need to include the updating of the pages in your ajax callback function.

$http.get('js/items.json') // go get my json file
   .then(function(res){
      $scope.items = res.data; // set this data to be my items
      $scope.numberOfPages=function(){
        return Math.ceil($scope.items.length/$scope.pageSize);             
   }             
});

Upvotes: 0

Related Questions