2

I implemented an advance search with 15 input fields in AngularJS.

In the page load itself the result set is return from database in JSON format and i need to do the filter in client side only.

The input criteria's equivalent column is available in the result set and i need to check in its respective column only.

I am converting each column by JSON.stringify() and check with the search params like the below :

$scope.filteredData = $scope.actualData.filter(function(item) {
    return JSON.stringify(item.FirstName).toLowerCase().indexOf(lowerFirstName) != -1 &&
    JSON.stringify(item.LastName).toLowerCase().indexOf(lowerLastName) != -1 &&
    JSON.stringify(item.EmailAddress).toLowerCase().indexOf(lowerEmailAddress) != -1 &&
    JSON.stringify(item.Address1).toLowerCase().indexOf(lowerAddress1) != -1 &&
    JSON.stringify(item.Address2).toLowerCase().indexOf(lowerAddress2) != -1;
    ...... etc // upto 15 fields
});

Since i have the 15 input fields and the actual result set contains a minimum of 50,000 records.

So converting each record's each column by JSON.stringify() and check with search params will surely cause the performance issue.

Is there any other way to achieve the filtering in client side with other approach.

I posted a sample code in Plunker with 5 input fields only : http://plnkr.co/edit/nUWZEbGvz7HG6gb91YZP

1
  • 2
    Why are you even calling JSON.stringify()? Your result data is already an object with string properties. Just do item.FirstName.toLowerCase().indexOf(..) Commented Mar 11, 2015 at 15:30

2 Answers 2

2

sylwester's answer is the normal way you'd filter things. Your code looks like you want to filter down to only the object that matches every input field. You code attempts to find an object where every property matches the searchParams object. At that point, I don't see what benefit there is to finding that object, because the user already created the object again! Nonetheless, here's a proper version of your code:

Live demo here.

<div ng-repeat="data in actualData | filter:searchData()">
 $scope.searchData = function() {
    return function(item) {
      return Object.keys(item).every(function(key) {
        // skip the $$hashKey property Angular adds to objects
        if (key === '$$hashKey') { return true; }
        var searchKey = key.charAt(0).toLowerCase()+key.slice(1);
        return item[key].toLowerCase() === $scope.searchParams[searchKey].toLowerCase();
      });
    };
  };

You really need to limit the data coming from the server for the browser's sake and for the server's sake. It's easy to implement a LIMIT, OFFSET system. It sounds like, overall, you just need to be able to query the server for a certain record.

From your comments, it seems you definitely want Angular's built in filter filter:searchParams, and just capitalize your searchParams models to match your data. For fun, I'll include more options for finer tuning.

This one almost mimics filter:searchParams. You can change > 1 to adjust when the partial matching kicks in, or have it return true only when both items are strictly equal === to disable partial matching. The difference here is that all items are hidden until matched, whereas filter:searchParams will show all items and then remove what doesn't match.

Live demo here.

  $scope.searchData = function() {
    return function(item) {
      return Object.keys(item).some(function(key) {
        if (key === '$$hashKey') { return false; }
        var searchKey = key.charAt(0).toLowerCase()+key.slice(1);
        var currentVal = $scope.searchParams[searchKey].toLowerCase();
        var match = item[key].toLowerCase().match(currentVal);
        return currentVal.length > 1 && match;
      });
    };
  };

Lastly, to perfectly mimic filter:searchParams, you'd just put in a check to NOT filter the items until there is user input and the input is long enough to start the partial match.

Live demo here.

  $scope.searchData = function() {
    var partialMatchLength = 2;
    return function(item) {
      var shouldFilter = Object.keys($scope.searchParams).some(function(key) {
        return $scope.searchParams[key] && $scope.searchParams[key].length >= partialMatchLength;
      });
      if (!shouldFilter) { return true; }
      return Object.keys(item).some(function(key) {
        if (key === '$$hashKey') { return false; }
        var searchKey = key.charAt(0).toLowerCase()+key.slice(1);
        var currentVal = $scope.searchParams[searchKey].toLowerCase();
        var match = item[key].toLowerCase().match(currentVal);
        return currentVal.length >= partialMatchLength && match;
      });
    };
  };
Sign up to request clarification or add additional context in comments.

5 Comments

The plunker is not return any filtered data for any valid input.
@Arulkumar It does. My code does what your code tried to do, which may not be at all what you want (like I said in my answer). Do you get what I'm saying? I did what you did, but right. My guess is that you don't want the result that your code tries to produce, especially because that result doesn't make any sense. Anyway, just like your code, with all the &&, my example will display the object if ALL of the inputs match. I really think you want sylwester's answer, or maybe my answer using .some instead, which will be similar to sylwester's anwer, but slightly different behavior.
@Arulkumar Here's the same thing using .some so object's are shown where any field matches exactly i.e. they enter the full name "Arul", then the "Arul" object is shown. plnkr.co/edit/xmAfXA0BKalpjhy0LDJy?p=preview Perhaps that's what you wanted.
First of all thanks for your effort, time and detailed reply. I can understand the point what you're tried. sylwester's answer is searching the entered field with all fields of the result set. My requirement is, if i type 'Arul' in the First name input field then i need to search 'Arul' only in the firstname field of the json and not on other fields. Your result is working as expected, except the partial keyword search, meant even if i type 'Ar' in the input field of First name, i want to get the result. So instead of '.some' can we use any other option to achieve this?
@Arulkumar I added a lot to my answer. I think you've now realized that filter:searchParams does exactly what you wanted all along, but I recreated it's behavior so that you can better understand what it's doing and customize the exact behavior if you desire to.
1

First of all you ng-repeter with 50.000 records more likely is going to kill your browser, so you should thing about pagination. Secondly you can easy filter your data using angular filter please see that demo

http://plnkr.co/edit/R8b8G4xCMSQmX1144UJG?p=preview

<div ng-controller="ListCtrl">
    <br />
    First Name:
    <input type="text" id="txtFirstname" ng-model="searchParams.FirstName">
    <br/>Last Name:
    <input type="text" id="txtLastname" ng-model="searchParams.LastName">
    <br/>Email Address:
    <input type="text" id="txtEmailAddress" ng-model="searchParams.EmailAddress">
    <br/>Address 1:
    <input type="text" id="txtAddress1" ng-model="searchParams.Address1">
    <br/>Address 2:
    <input type="text" id="txtAddress2" ng-model="searchParams.Address2">
    <br/>
    <button class="btn btn-primary" ng-click="searchData()">Search</button>
    <br />
    <hr />
    <b>Filtered Data(s):</b>
    <div ng-repeat="data in actualData | filter:searchParams ">
      <span ng-bind="data.FirstName"></span>
      <span ng-bind="data.LastName"></span> |
      Address : {{data.Address1}}
    </div>
    <hr />

  </div>

3 Comments

My requirement is to search the input field with its respective column of the json. So if i enter 'Ar' in the First name of input field, the filtered result need to fetch the matches with FirstName field of the json. Your code is returning the all matches with other columns also.
@Arulkumar please see here plnkr.co/edit/R8b8G4xCMSQmX1144UJG?p=preview second user lastName is Arul but if you type 'ar' in first name as result you get first user only
Oh, i missed to check that. Thanks for notify it.

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.