Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / AngularJs

AngularJS + ASP.NET Web API: Building a Simple Grid in AngularJS with Server-side Paging, sorting, searching (Part 7)

5.00/5 (2 votes)
4 Apr 2015CPOL2 min read 11.3K  
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.

This is the 7th blog in the series, and recommend reading the previous posts if you have not done so already.

The students.tpl.html file should look as so, I've added a search box as can be seen in lines 1-11.

HTML
<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”.

JavaScript
.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:

JavaScript
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:

JavaScript
.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 like this:

C#
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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)