<!DOCTYPE html>
<html ng-app="myApp">

  <head>
    <link data-require="toastr@*" data-semver="1.3.0" rel="stylesheet" href="//www.johnpapa.net/scripts/toastr.min.css" />
    <link data-require="bootstrap@*" data-semver="3.3.2" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" />
    <link rel="stylesheet" href="style.css" />
    
    <script data-require="jquery@*" data-semver="2.1.3" src="http://code.jquery.com/jquery-2.1.3.min.js"></script>
    <script data-require="bootstrap@*" data-sever="3.3.2" src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
    <script data-require="toastr@*" data-semver="1.3.0" src="//www.johnpapa.net/scripts/toastr.min.js"></script>
    <script src="https://code.angularjs.org/1.3.15/angular.js" data-semver="1.3.15" data-require="angular.js@1.3.15"></script>
    
    <script src="app.js"></script>
    <script src="users-service.js"></script>
    <script src="db-config-service.js"></script>
    <script src="notification-service.js"></script>
  </head>

  <body>
    <div class="container" ng-controller="AppCtrl">
      <div class="alert alert-danger" ng-show="msg">{{msg}}</div>
      <div class="alert alert-info loading" ng-show="users.length==0">Loading...</div>
      <div class="panel panel-info">
        <div class="panel-heading">
          <strong>System Users</strong>
          <span class="badge pull-right">Total Users : {{users.length}}</span>
        </div>
        <div class="panel-body">
          <div class="row col-sm-12">
            <div class="pull-left col-sm-6">Ordered by : <span class="badge">{{sort | uppercase}}{{sort? (reverse?' - Descending':' - Ascending'):'None'}}</span>
            </div>
            <div class="pull-right">
              <span>Filtered by : </span>
              <span ng-show="search.firstName">First Name =<span class="badge">{{search.firstName}}</span>
              </span>
              <span ng-show="search.lastName">Last Name =<span class="badge">{{search.lastName}}</span>
              </span>
              <span ng-show="search.email">Email =<span class="badge">{{search.email}}</span>
              </span>
            </div>
          </div>
          <table class="table table-striped table-bordered" ng-hide="users.length==0">
            <thead>
              <tr>
                <th>
                  <input type="text" class="form-control" ng-model="search.firstName" placeholder="Filter by First Name" />
                </th>
                <th>
                  <input type="text" class="form-control" ng-model="search.lastName" placeholder="Filter by Last Name" />
                </th>
                <th>
                  <input type="text" class="form-control" ng-model="search.email" placeholder="Filter by Email" />
                </th>
                <th></th>
              </tr>
              <tr>
                <th ng-click="setSort('firstName')">First Name<span class="pull-right arrow-up" ng-show="sort==='firstName' && reverse"></span>
                  <span class="pull-right arrow-down" ng-show="sort==='firstName' && !reverse"></span>
                </th>
                <th ng-click="setSort('lastName')">Last Name<span class="pull-right arrow-up" ng-show="sort === 'lastName' && reverse"></span>
                  <span class="pull-right arrow-down" ng-show="sort === 'lastName' && !reverse"></span>
                </th>
                <th ng-click="setSort('email')">Email<span class="pull-right arrow-up" ng-show="sort==='email' && reverse"></span>
                  <span class="pull-right arrow-down" ng-show="sort==='email' && !reverse"></span>
                </th>
                <th>Action</th>
              </tr>
            </thead>
            <tbody>
              <tr ng-repeat="user in users | filter:search | orderBy:sort:reverse | startFrom:currentPage*selectedPageSize | limitTo:selectedPageSize track by $index">
                <td ng-click="editUser(user)">
                  <span ng-hide="user.editMode">{{user.firstName}}</span>
                  <input type="text" name="firstName" ng-show="user.editMode" class="form-control" ng-model="user.firstName" placeholder="First Name" required="" />
                </td>
                <td ng-click="editUser(user)">
                  <span ng-hide="user.editMode">{{user.lastName}}</span>
                  <input type="text" ng-show="user.editMode" name="lastName" class="form-control" ng-model="user.lastName" placeholder="Last Name" required="" />
                </td>
                <td ng-click="editUser(user)">
                  <span ng-hide="user.editMode">{{user.email}}</span>
                  <input type="text" ng-show="user.editMode" name="email" class="form-control" ng-model="user.email" placeholder="Email" required="" />
                </td>
                <td>
                  <a href="" ng-hide="user.editMode" ng-click="editUser(user)">Edit</a>
                  <a href="" ng-hide="user.editMode" ng-click="delete(user,$index)">Delete</a>
                  <a href="" ng-show="user.editMode" ng-click="saveUser(user,$index)">Update</a>
                  <a href="" ng-show="user.editMode" ng-click="cancel(user)">Cancel</a>
                </td>
              </tr>
            </tbody>
          </table>
          <div class="row">
            <div class="col-sm-2">
              <span class="col-sm-5">Pagesize:</span>
              <span class="col-sm-7">
                <select class="form-control" ng-options="pageSize for pageSize in pageSizes" ng-model="selectedPageSize"></select>
              </span>
            </div>
            <div class="col-sm-6">
              <button class="btn btn-small btn-info" ng-disabled="currentPage <1" ng-click="currentPage=currentPage-1">Prev Page</button>
              <!--span class="col-sm-3">Goto Page:</span>
              <span class="col-sm-2">
                <select class="form-control" ng-options="page for page in pages" ng-model="currentPage"></select>
              </span-->
              <button class="btn btn-small btn-info" ng-disabled="currentPage >= users.length/selectedPageSize - 1" ng-click="currentPage=currentPage+1">Next Page</button>
            </div>
            <div class="pull-right col-sm-4">
              <button class="btn btn-warning" ng-click="refreshUsers()">Refresh</button>
              <button class="btn btn-success" ng-click="bulkUpdateUsers()">Bulk Update</button>
              <button class="btn btn-danger" ng-click="bulkDeleteUsers()">Bulk Delete</button>
            </div>
          </div>
        </div>
      </div>
    </div>
  </body>

</html>
// Code goes here
(function() {
  //create application module
  var app = angular.module("myApp", []);

  //create main app controller
  app.controller("AppCtrl", function($scope, usersSvc, notificationService) {

    //maintain a user list (initialy blank)
    var defaultSort = 'firstName';
    $scope.users = [];
    $scope.msg = "";
    $scope.sort = defaultSort;
    $scope.reverse = false;
    $scope.selectedPageSize = 3;
    $scope.pageSizes = [1, 2, 3];
    $scope.currentPage = 1;
    $scope.pages = [1, 2, 3, 4];


    $scope.setSort = function(sort) {
      if ($scope.sort === sort) {
        $scope.reverse = !$scope.reverse;
      }
      if ($scope.sort !== undefined) {
        $scope.sort = sort;
      }
    }

    //define user CRUD operations

    //save user (create & update)
    $scope.saveUser = function(user, index) {
      if (user.editMode) {
        user.editMode = false;
        $scope.sort = defaultSort;
        usersSvc.updateUser(user, index)
          .then(function() {
            notificationService.success("User updated successfully.");
          }, function(response) {
            $scope.msg = response;
          });
      } else {
        usersSvc.createUser(user)
          .then(function() {
            notificationService.success("User created successfully.");
          }, function(response) {
            $scope.msg = response;
          });
      }
    }

    //bulk update users
    $scope.bulkUpdateUsers = function() {
      for (var i = 0; i < $scope.users.length; i++) {
        var user = $scope.users[i];
        if (user.editMode) {
          $scope.saveUser(user, i);
        }
      }
    }

    //var lastEditedUser={};
    //edit user details
    $scope.editUser = function(user) {
      //angular.extend(lastEditedUser,user);
      user.editMode = true;
      $scope.sort = undefined;
    }

    //cancel
    $scope.cancel = function(user) {
      getAllUsers();
      user.editMode = false;

      //angular.extend(user,lastEditedUser);
    }


    //delete user
    $scope.deleteUser = function(user, index) {
      $scope.users = [];
      usersSvc.deleteUser(user, index)
        .then(function(response) {
          user.editMode = false;
          refreshUsers();
          notificationService.success("User deleted successfully.");
        }, function(response) {
          $scope.msg = response;
        });
    }

    $scope.bulkDeleteUsers = function() {
      for (var i = 0; i < $scope.users.length; i++) {
        var user = $scope.users[i];
        if (user.editMode) {
          $scope.deleteUser(user, i);
        }
      }
    }

    //get all users
    function getAllUsers() {
      $scope.sort = defaultSort;
      usersSvc.retrieveAllUsers()
        .then(function(response) {
          $scope.users = angular.copy(response.data);
        }, function(response) {
          $scope.msg = response;
        });
    }


    function refreshUsers() {
      $scope.users = [];
      getAllUsers();
    }

    $scope.refreshUsers = refreshUsers;

    //fetch all users
    getAllUsers();
  });

  //start from filter
  app.filter('startFrom', function() {
    return function(input, start) {
      start = +start; //parse to int
      return input.slice(start);
    }
  });
}());
/* Styles go here */

.container {
  margin-top: 50px;
}
input.ng-invalid.ng-dirty {
  background-color: rgb(255, 200, 200);
  border-color: red;
}
.loading {
  position: absolute;
  left: 45%;
  top: 45%;
}
.arrow-up {
  width: 0;
  height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-bottom: 5px solid black;
  margin-top: 10px;
}
.arrow-down {
  width: 0;
  height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 5px solid black;
  margin-top: 10px;
}
Angular Table 
Sorting
Filtering
Inline-editing
Bulk-update
Online Mongolab Integration
(function(){
  
  //load main module
  var app=angular.module("myApp");
  
  app.value("notificationService",window.toastr);
}());
(function() {

  //load main module
  var app = angular.module("myApp");

  var debugMode = true;
  var offlineUsers = [];
  for (var i = 0; i < 5; i++) {
    offlineUsers.push({
      firstName: "debug" + i + "_firstName",
      lastName: "debug" + i + "_lastName",
      email: "debug" + i + "_email"
    });
  }
  app.factory("usersSvc", function($q, $http, dbConfigService) {

    //-----------------offline user CRUD operations-----------------------

    //createUser
    function createUserOffline(user) {
      var dfd = new $q.defer();
      offlineUsers = offlineUsers || [];
      offlineUsers.push(user);
      dfd.resolve("");
      return dfd.promise;
    }

    //retrieveAllUsers
    function retrieveAllUsersOffline() {
      var dfd = new $q.defer();
      offlineUsers = offlineUsers || [];
      dfd.resolve({
        data: offlineUsers
      });
      return dfd.promise;
    }

    //updateUser
    function updateUserOffline(user, index) {
      var dfd = new $q.defer();

      if (offlineUsers) {
        offlineUsers[index] = angular.copy(user);
      }
      dfd.resolve("");
      return dfd.promise;
    }

    //deleteUser
    function deleteUserOffline(user, index) {
      var dfd = new $q.defer();
      if (offlineUsers) {
        offlineUsers.splice(index, 1);
      }
      dfd.resolve("");
      return dfd.promise;
    }

    //----------------------User CRUD operations------------------------------------------
    //createUser
    function createUser(user) {
      return $http.post(dbConfigService.url, user, {
        params: {
          apiKey: dbConfigService.key
        }
      });
    }

    //retrieveAllUsers
    function retrieveAllUsers() {
      return $http.get(dbConfigService.url, {
        params: {
          apiKey: dbConfigService.key
        }
      });
    }

    //updateUser
    function updateUser(user, index) {
      return $http.put(dbConfigService.url + "/" + user._id.$oid, user, {
        params: {
          apiKey: dbConfigService.key
        }
      });
    }

    //deleteUser
    function deleteUser(user, index) {
      return $http.delete(dbConfigService.url + "/" + user._id.$oid, {
        params: {
          apiKey: dbConfigService.key
        }
      });
    }

    return {
      createUser: debugMode ? createUserOffline : createUser,
      retrieveAllUsers: debugMode ? retrieveAllUsersOffline : retrieveAllUsers,
      updateUser: debugMode ? updateUserOffline : updateUser,
      deleteUser: debugMode ? deleteUserOffline : deleteUser
    };
  });
}());
(function() {

  //get main app module
  var app = angular.module("myApp");

  app.value("dbConfigService", {
    url: "https://api.mongolab.com/api/1/databases/users/collections/users",
    key: "Wdc5sXvl48AUtNVVobU4rSbxrrUNOQIe"
  });
}());