3

I am running a few tables that use the pipe/ajax section of the code with the controller/service setup. https://lorenzofox3.github.io/smart-table-website/#section-pipe

One of the issues I am coming across is when there are special characters in the values with a space in it and it is unable to filter it. For example, if there was a lastname of "last-name firstname" it is unable to filter the data but it is able to filter the name "last-name" and it is able to do "lastname firstname" just fine.

Could I get some help on figuring out why this might not be able to filter correctly?

Thank You!

Edit: I noticed I forgot to add the filter.

app.filter('propsFilter', function() {
  return function(items, props) {
    var out = [];

    if (angular.isArray(items)) {
      var keys = Object.keys(props);

      items.forEach(function(item) {
        var itemMatches = false;

        for (var i = 0; i < keys.length; i++) {
          var prop = keys[i];
          var text = props[prop].toLowerCase();
          if (item[prop].toString().toLowerCase().indexOf(text) !== -1) {
            itemMatches = true;
            break;
          }
        }

        if (itemMatches) {
          out.push(item);
        }
      });
    } else {
      // Let the output be the input untouched
      out = items;
    }

    return out;
  };
});

app.controller('mainCtrl', ['$scope', '$window', 'Resource', function($scope, $window, service) {

    var ctrl = this;

    this.displayed = [];

    $scope.itemsByPage = $window.datatableperPage;

    this.callServer = function callServer(tableState) {

        ctrl.isLoading = true;

        var pagination = tableState.pagination;

        var start = pagination.start || 0;
        var number = pagination.number || 10;

        service.getPage(start, number, tableState).then(function(result) {
            ctrl.displayed = result.data;
            tableState.pagination.numberOfPages = result.numberOfPages; //set the number of pages so the pagination can update
            ctrl.isLoading = false;
        });
    };

}]);
app.factory('Resource', ['$q', '$filter', '$window', '$http', '$timeout', function($q, $filter, $window, $http, $timeout) {

    var nameData = [];

    $http.get($window.datatableSource).success(function(response) {
        nameData = response;
    });

    function getPage(start, number, params) {

        var deferred = $q.defer();

        var filtered = params.search.predicateObject ? $filter('filter')(nameData, params.search.predicateObject) : nameData;

        if (params.sort.predicate) {
            filtered = $filter('orderBy')(filtered, params.sort.predicate, params.sort.reverse);
        }

        var result = filtered.slice(start, start + number);

        $timeout(function() {
            //note, the server passes the information about the data set size
            deferred.resolve({
                data: result,
                numberOfPages: Math.ceil(filtered.length / number),
            });
        }, $window.datatableTimeout);

        return deferred.promise;
    }

    return {
        getPage: getPage
    };


}]);

Update:

With Hardy's Help I was finally able to replicate the issue.

When setting the

$scope.itemsByPage = -1;

the filtered results seem to vanish after initial couple characters at the slice

result = filtered.slice(start, start + number); 

In this example, I added the word "Clinical - " to the beginning of a name and it is unable to search for the word when you start typing the word "Clinical" but other words work just fine.

https://plnkr.co/edit/7n68AKbwQGpVdFOpbUuP?p=preview

Robert Lee
  • 1,541
  • 1
  • 10
  • 20
  • Could simulate this on a fiddle? – lenilsondc Oct 26 '16 at 15:22
  • I updated the question since I forgot to add the filter code in. I cant seem to get the jsfiddle to show the list if names for some reason but I started it. https://jsfiddle.net/6zyuLzre/1/ – Robert Lee Oct 26 '16 at 16:21
  • 1
    Is this a filter you're actually using? I've tried to use it within your jsfiddle (just changed jsfiddle config) and it worked. https://jsfiddle.net/uthuLruz/7/ Maybe you're getting data from some external source? If so, can you provide http request you've sent and data received? – Hardy Rust Oct 27 '16 at 08:21
  • I dont use jsfiddle very much but I tossed a randomized json file online to see if I can replicate it but I cant seem to get it to load. https://jsfiddle.net/uthuLruz/8/ It is definitely odd that it is working on the fiddle. I wonder if it is some of the other angular extensions I have setup ['ui.bootstrap', 'ngSanitize', 'ui.select', 'angular-loading-bar', 'smart-table', 'ngCsv', 'angularMoment'] – Robert Lee Oct 27 '16 at 08:53
  • 1
    It won't load from external service because of CORS policy. I've rewrited it to plnk as it's more functiuonal for this purpose https://plnkr.co/edit/CbC7IzjJCT8TjNxizS6S?p=preview Looks like it loads data and sorts good. Can you check if your loader does the same? It doesn't seems to be related to external components unless you're using filters from them on result array. What is the source of real data you're receiving? Is it JSON like in example or something else? – Hardy Rust Oct 27 '16 at 09:13
  • Yes it is a JSON file that was put together by Python output. It is similar so I'm going to possibly guess it is possibly from either a library I'm using or the JSON file itself is not being read properly. I'm confused on what could possibly be the cause when it is working external to the website. Could it be one of the extensions for AngularJS or could it be a library? – Robert Lee Oct 27 '16 at 17:22
  • I attempted the plnkr and it does not look like it is loading the data for me. – Robert Lee Oct 27 '16 at 17:41
  • 1
    Tried to modify plnkr for you https://plnkr.co/edit/4WYNBLCIyLAr3SQUmAQs?p=preview This one loads via double promise (from http request and from search params). Unlike example from component library, it avoids unnecessary data generation and displays items. It this one won't work too, please copy everything you've got from developer console output (if any). And please, copy headers of server response. – Hardy Rust Oct 28 '16 at 07:30
  • Seems to be working for me. Would it be best practice to double promise like your example? I am still trying to wrap my head around the data search. I would imagine it has something to do with the json data formatting when outputting from Python. Is there a specific standard I should follow for the json file? – Robert Lee Oct 28 '16 at 18:07
  • Is it "special characters", or is it just dashes? It could be related to Angular's [normalization](https://docs.angularjs.org/guide/directive#normalization) (eg. ng-click -> ngClick) – adamdport Oct 29 '16 at 15:25
  • I can search via special characters (-, &, space, etc).. But when I type string text in the field it just goes blank with no results. After more research it looks like it is specific to the name field. So I am going to guess its the json source that is the problem. Is there a json standard I need to follow for angular? – Robert Lee Oct 29 '16 at 17:24
  • I was finally able to replicate the issue with this. https://plnkr.co/edit/7n68AKbwQGpVdFOpbUuP?p=preview The issue is with when I set the itemsByPage to -1 and it tries to slice the filtered result. If you attempt to search for Clinical on the first row it will fail to load. – Robert Lee Oct 31 '16 at 08:35

1 Answers1

1

Okay, I've traced all the functions and the problem in Plunker is in this very line. Try to change the value to any positive integer and you will see that your app works just fine.

$scope.itemsByPage = -1;

The reason you get strange filtering results in some cases is because this very value is used as number later in

var result = filtered.slice(start, start + number);

thus translating to

var result = filtered.slice(0, -1);

.slice(0, -1) returns a shallow copy of the filtered array excluding the last item. And when you only have one filtered result you will recieve an empty array.

Voilà!


Update

As it turns out, the whole reason of using $scope.itemsByPage = -1 was a delusion of the behaviour of the library in that case.
The desired behavior was to "always return all the rows" and it can be easily achieved by using Infinity

$scope.itemsByPage = Infinity

This way we will get

var result = filtered.slice(0, Infinity);

thus getting initial filtered array in the output.

Dennis Baizulin
  • 1,410
  • 8
  • 11
  • Thank you for the explanation. I understand that this was the case but I was wondering if you could possibly provide an example to avoid the "shallow copy" where it is excluding the last variable in the list. – Robert Lee Nov 01 '16 at 16:21
  • Hmm? If you set your `itemsByPage` to a normal value like 25 you will get `result = filtered.slice(0, 25)` and it will work just as you'd expect. I don't understand what kind of example you need. – Dennis Baizulin Nov 01 '16 at 16:24
  • Since this helped me get to my answer I will go ahead and mark it. Thanks. The point was to provide a solution while using itemsByPage = -1 but since your explanation got me to my answer, I will update your answer since some credit is due. https://plnkr.co/edit/TDvvPuTVdjt4JIN9tvN6?p=preview – Robert Lee Nov 01 '16 at 16:37
  • I am curious tho - why would you need to use `-1`? It is totally wrong and if there is some goal you are trying to achieve with that you should really rethink your approach. – Dennis Baizulin Nov 01 '16 at 16:40
  • From my understanding, if you do not want to paginate and always show a "full list" of ALL rows, the method I found to be able to do this is by setting to itemsByPage value to -1 which would always show all rows instead of paginating. Is there another method you can think of to do this? – Robert Lee Nov 01 '16 at 16:42
  • The edit needs to be peer reviewed but I went ahead and added the answer number = (number == -1) ? filtered.length : number; to it for others to view. – Robert Lee Nov 01 '16 at 16:46
  • 1
    I just explained to you in the answer that it will never show you all rows because it will **always** strip the last item. If you absolutely need to display all the rows, just set `$scope.itemsByPage = Infinity;` and it will do – Dennis Baizulin Nov 01 '16 at 16:46
  • That is interesting. Could you possibly provide documentation on this? – Robert Lee Nov 01 '16 at 16:50