AngularJS + ASP.NET Web API: Building a simple grid in AngularJS with server-side paging, sorting, searching (Part 7)

In this post I’ll add the ability to search on first or last name, so should the search term occur in either the first or last name then only those records will be returned.

Add the highlighted code to the students.tpl.html file

<div class="row">
    <div class="col-sm-3 col-md-3">
        <div class="input-group">
            <input type="search" class="form-control" placeholder="Search for..." ng-model="searchfor">
            <div class="input-group-btn">
                <button class="btn btn-default" type="button" ng-click="search(searchfor)"><i class="glyphicon glyphicon-search"></i></button>
            </div>
        </div>
    </div>
</div>

<div class="row top-buffer">
  <table class="table table-bordered table-striped table-responsive">
    <thead>
      <tr>
        <th>
        </th>
        <th>
        </th>
        <th>
        </th>

        <th>
          <a href="#" ng-click="sort('lastName')" target="_self">Last Name</a>
          <i ng-class="{'glyphicon glyphicon-chevron-up':sortKeyOrder.order=='ASC' && sortKeyOrder.key=='lastName'}"></i>
          <i ng-class="{'glyphicon glyphicon-chevron-down':sortKeyOrder.order=='DESC' && sortKeyOrder.key=='lastName'}"></i>

        </th>
        <th>
          <a href="#" ng-click="sort('firstName')" target="_self">First Name</a>
          <i ng-class="{'glyphicon glyphicon-chevron-up':sortKeyOrder.order=='ASC' && sortKeyOrder.key=='firstName'}"></i>
          <i ng-class="{'glyphicon glyphicon-chevron-down':sortKeyOrder.order=='DESC' && sortKeyOrder.key=='firstName'}"></i>
        </th>
        <th>
          Date of Enrollment
        </th>

      </tr>
    </thead>
    <tbody data-ng-repeat="i in data">
      <tr>
        <td></td>
        <td></td>
        <td></td>
        <td>
          <textarea class="form-control" style="width: 300px;height: 65px" ng-model="i.lastName"></textarea>
        </td>
        <td>
          <textarea class="form-control" style="width: 300px;height: 65px" ng-model="i.firstMidName"></textarea>
        </td>
        <td>
          <input type="text" class="form-control" style="width: 150px;height: 65px" ng-model="i.enrollmentDate" />
        </td>
      </tr>

    </tbody>
  </table>

  <span data-pagination data-total-items="totalItems" data-ng-model="currentPage" data-max-size="numberOfPageButtons" class=" pagination-sm" data-boundary-links="true" data-rotate="false" data-ng-change="pageChanged()" data-items-per-page="recordsPerPage"></span>

</div>

In the student.js file add the highlighted lines of code to the studentCtrl, there is one subtle point to note with regards to line 9-11. Here the search term is being read from the local Storage if the search term exists in the local storage. The reason for this is that when we sort on the last or first name (see lines 24 and 30 above in the students.tpl.html), the ng-click causes a full page reload since it resides within an anchor tag with the href attribute set to “#” and since this causes the page to “flash” there is the target=”_self” attribute added to the anchor tag as you’ll see in line 24 and 30.

Now, due to a full page reload being performed the search term that is displayed in the search box gets “lost” and hence one has to reload the search term from the local storage. Remember, that in the getData helper function any subsequent reads from the server side webapi will always use the value stored in the local storage so lines 9-11 exist only to make sure that the search term continues to be displayed in the search box each time the user sorts on either the last or first name, this is necessary to give a visual clue to the user that the records she is viewing all have the search term occurring in the first or last name.

You’ll notice that when the user pages through the grid, the search term if it exists will continue to be displayed in the search box even if lines 9-11 are commented out, and, the reason for this is that a full page reload does not happen when the user pages through the records and hence the search term that is displayed in the search box is not “lost”.

 .controller("studentCtrl", ["$scope", "dataService", "localStorageService",
       function ($scope, dataService, localStorageService) {

           var sortKeyOrder = {
               key: '',
               order: '',
           };

           if (localStorageService.get("searchfor") !== undefined) {
               $scope.searchfor = localStorageService.get("searchfor");
           }

           $scope.totalItems = 0;
           $scope.currentPage = 1;
           $scope.maxSize = 5;
           $scope.recordsPerPage = 5;
           $scope.numberOfPageButtons = 5;

           getData($scope, dataService, localStorageService);

           $scope.sort = function (col) {

               sortKeyOrder = localStorageService.get('sortKeyOrder');

               if (sortKeyOrder !== null && sortKeyOrder.key === col) {

                   if (sortKeyOrder.order == 'ASC')
                       sortKeyOrder.order = 'DESC';
                   else
                       sortKeyOrder.order = 'ASC';

                   localStorageService.set('sortKeyOrder', sortKeyOrder);

               } else {

                   sortKeyOrder = {
                       key: col,
                       order: 'ASC',
                   };

                   localStorageService.set('sortKeyOrder', sortKeyOrder);

               }
           };

           $scope.pageChanged = function () {

               getData($scope, dataService, localStorageService);
           };

           $scope.search = function (searchfor) {

               if (searchfor === undefined) {
                   $scope.searchfor = "";
               }

               localStorageService.set("searchfor", searchfor);

               getData($scope, dataService, localStorageService);
           }

       }]);

The getData helper function should look as so

var getData = function ($scope, dataService, localStorageService) {

    $scope.data = dataService.students;
    var sortKeyOrder = localStorageService.get('sortKeyOrder');

    if (sortKeyOrder == null) {
        sortKeyOrder = {
            key: 'lastName',
            order: 'ASC',
        };
    }

    var searchfor = localStorageService.get('searchfor');

    $scope.sortKeyOrder = sortKeyOrder;

    var options = {

        currentPage: $scope.currentPage,
        recordsPerPage: $scope.recordsPerPage,
        sortKeyOrder: sortKeyOrder,
        searchfor: searchfor,

    };

    dataService.getStudents(options)
    .then(function (totalItems) {
        $scope.totalItems = totalItems;
    },
    function () {

        alert("an error occurred: unable to get data");
    });

};

Make the following changes to the dataService service as highlighted below


 .factory("dataService", ["$http", "$q", function ($http, $q) {

       var _students = [];

       var _getStudents = function (options) {

           var deferred = $q.defer();

           $http.get("api/StudentsApi?currentPage=" + options.currentPage + "&" +
               "recordsPerPage=" + options.recordsPerPage + "&" +
               "sortKey=" + options.sortKeyOrder.key + "&" + "sortOrder=" + options.sortKeyOrder.order + "&searchfor=" + options.searchfor)
               .then(function (result) {
                   angular.copy(result.data.students, _students);
                   deferred.resolve(result.data.recordCount);
               },
                   function () {
                       deferred.reject();
                   });

           return deferred.promise;
       };

       return {
           students: _students,
           getStudents: _getStudents,
       };
   }])

And the GetStudents method should look as so

public StudentsContainer GetStudents(int currentPage, int recordsPerPage, string sortKey, string sortOrder, string searchfor)
        {

            var pageNumber = currentPage;
            var pageSize = recordsPerPage;
            var begin = (pageNumber - 1) * pageSize;

            var totalNumberOfRecords = db.Students.Count(r => searchfor == "null" || r.LastName.Contains(searchfor) || r.FirstMidName.Contains(searchfor));
            List<Student> results = null;
            switch (sortOrder)
            {
                case "ASC":
                    switch (sortKey)
                    {
                        case "lastName":
                            results = db.Students.Where(r => searchfor == "null" || r.LastName.Contains(searchfor) || r.FirstMidName.Contains(searchfor)).OrderBy(r => r.LastName).Skip(begin).Take(pageSize).ToList();
                            break;
                        case "firstName":
                            results = db.Students.Where(r => searchfor == "null" || r.LastName.Contains(searchfor) || r.FirstMidName.Contains(searchfor)).OrderBy(r => r.FirstMidName).Skip(begin).Take(pageSize).ToList();
                            break;
                    }
                    break;
                case "DESC":
                    switch (sortKey)
                    {
                        case "lastName":
                            results = db.Students.Where(r => searchfor == "null" || r.LastName.Contains(searchfor) || r.FirstMidName.Contains(searchfor)).OrderByDescending(r => r.LastName).Skip(begin).Take(pageSize).ToList();
                            break;
                        case "firstName":
                            results = db.Students.Where(r => searchfor == "null" || r.LastName.Contains(searchfor) || r.FirstMidName.Contains(searchfor)).OrderByDescending(r => r.FirstMidName).Skip(begin).Take(pageSize).ToList();
                            break;
                    }
                    break;
            }

            var students =
                results.Select(
                    r =>
                        new Student
                        {
                            EnrollmentDate = r.EnrollmentDate,
                            FirstMidName = r.FirstMidName,
                            LastName = r.LastName,
                            ID = r.ID
                        }).ToList();

            var studentsContainer = new StudentsContainer { Students = students, RecordCount = totalNumberOfRecords };

            return studentsContainer;
        }

That’s it!, so now when a user enters a search term then only those records will be returned where the search term occurs either in the first or last name. The search term is being saved in the local storage and this allows the user to page through only those records where the search term occurs in the first or last name. It’s also possible to sort on the records where the search term occurs and again the local storage is queried to retrieve the search term.

As always, the changes I have made in this post can be viewed at SHA

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s