AngularJS CRUD Grid v1: How to build a CRUD Grid with AngularJS, WebAPI, Bootstrap, Font Awesome and Toastr

UPDATE 7/30/2013: I modified the code to use $resource instead of $http. Go to my new post “V2 of my AngularJS, WebAPI CRUD Grid - Now using $resource instead of $http and deployed a LIVE DEMO to Azure!” for all the details. You can still access the $http code here: https://github.com/jonbgallant/AngularJS-WebApi-EF/tree/6b9df7c3b52caac64df800a4c8dcf8e45e0bb2d1

Here’s how I built a quick single entity CRUD grid with AngularJS, WebAPI, Entity Framework, Bootstrap, Font Awesome & Toastr. I spent a more time than I should have getting this all wired up, so I thought I’d share the code to save you some time. It’s not perfect – but should be a good starting point for you.

Here’s what the grid looks like.

THE CODE

All the code is on GitHub here: https://github.com/jonbgallant/AngularJS-WebApi-EF/

The $http methods that call WebAPI

You could also do this with $resource or Restangular, but I just stuck with $http for this example because it is easier to grok.

Each of the methods below perform $http actions and return a promise.

var url = 'api/person/';
app.factory('personFactory', function ($http) {
return {
getPeople: function () {
return $http.get(url);
},
addPerson: function (person) {
return $http.post(url, person);
},
deletePerson: function (person) {
return $http.delete(url + person.Id);
},
updatePerson: function (person) {
return $http.put(url + person.Id, person);
}
};
});

The personFactory is injected into the IndexCtrl and called like so. Provide “success” and “error” callbacks to process the promise.

app.controller('IndexCtrl', function ($scope, personFactory, notificationFactory) {
$scope.people = [];
var successCallback = function (data, status, headers, config) {
notificationFactory.success();
return personFactory.getPeople().success(getPeopleSuccessCallback).error(errorCallback);
};
var successPostCallback = function (data, status, headers, config) {
successCallback(data, status, headers, config).success(function () {
$scope.toggleAddMode();
$scope.person = {};
});
};
var errorCallback = function (data, status, headers, config) {
notificationFactory.error(data.ExceptionMessage);
};
$scope.addPerson = function () {
personFactory.addPerson($scope.person).success(successPostCallback).error(errorCallback);
};
});

HTML/JavaScript

<!doctype html>
<html ng-app="app">
<head>
<title>AngularJS-WebApi-EF</title>
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/angular")
@Scripts.Render("~/bundles/toastr")
@Scripts.Render("~/bundles/bootstrap")
@Styles.Render("~/content/bootstrap")
@Styles.Render("~/content/toastr")
<style>
i {
cursor: pointer;
}
</style>
</head>
<body>
<div ng-controller="IndexCtrl" ng-cloak>
<div style="width: 500px;">
<table class="table table-striped table-bordered table-condensed table-hover">
<tr>
<th style="width: 100px;">
<div class="btn-toolbar"><i class="btn icon-plus" ng-click="toggleAddMode()"></i></div>
</th>
<th style="width: 50px;">Id</th>
<th>Name</th>
</tr>
<tr ng-show="addMode">
<td>
<div class="btn-toolbar">
<div class="btn-group">
<i class="btn icon-save" ng-click="addPerson()"></i>
<i class="btn icon-remove" ng-click="toggleAddMode()"></i>
</div>
</div>
</td>
<td></td>
<td>
<input ng-model="person.Name" /></td>
</tr>
<tr ng-repeat="person in people | orderBy:'Id':true">
<td>
<div class="btn-toolbar" ng-show="person.editMode == null || person.editMode == false">
<div class="btn-group">
<i class="btn icon-edit" ng-click="toggleEditMode(person)"></i>
<i class="btn icon-trash" ng-click="deletePerson(person)"></i>
</div>
</div>
<div class="btn-toolbar" ng-show="person.editMode == true">
<div class="btn-group">
<i class="btn icon-save" ng-click="updatePerson(person)"></i>
<i class="btn icon-remove" ng-click="toggleEditMode(person)"></i>
</div>
</div>
</td>
<td></td>
<td>
<span ng-show="person.editMode == null || person.editMode == false"></span>
<input ng-model="person.Name" ng-show="person.editMode == true" />
</td>
</tr>
</table>
</div>
</div>
<script>
var app = angular.module('app', []);
var url = 'api/person/';
app.factory('personFactory', function ($http) {
return {
getPeople: function () {
return $http.get(url);
},
addPerson: function (person) {
return $http.post(url, person);
},
deletePerson: function (person) {
return $http.delete(url + person.Id);
},
updatePerson: function (person) {
return $http.put(url + person.Id, person);
}
};
});
app.factory('notificationFactory', function () {
return {
success: function () {
toastr.success("Success");
},
error: function (text) {
toastr.error(text, "Error!");
}
};
});
app.controller('IndexCtrl', function ($scope, personFactory, notificationFactory) {
$scope.people = [];
$scope.addMode = false;
$scope.toggleAddMode = function () {
$scope.addMode = !$scope.addMode;
};
$scope.toggleEditMode = function (person) {
person.editMode = !person.editMode;
};
var getPeopleSuccessCallback = function (data, status) {
$scope.people = data;
};
var successCallback = function (data, status, headers, config) {
notificationFactory.success();
return personFactory.getPeople().success(getPeopleSuccessCallback).error(errorCallback);
};
var successPostCallback = function (data, status, headers, config) {
successCallback(data, status, headers, config).success(function () {
$scope.toggleAddMode();
$scope.person = {};
});
};
var errorCallback = function (data, status, headers, config) {
notificationFactory.error(data.ExceptionMessage);
};
personFactory.getPeople().success(getPeopleSuccessCallback).error(errorCallback);
$scope.addPerson = function () {
personFactory.addPerson($scope.person).success(successPostCallback).error(errorCallback);
};
$scope.deletePerson = function (person) {
personFactory.deletePerson(person).success(successCallback).error(errorCallback);
};
$scope.updatePerson = function (person) {
personFactory.updatePerson(person).success(successCallback).error(errorCallback);
};
});
</script>
</body>
</html>

WebAPI

This is just the scaffolding code generated by VS

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;
namespace AngularJS_WebApi_EF.Models
{
public class PersonController : ApiController
{
private PersonContext db = new PersonContext();
// GET api/Person
public IEnumerable<Person> GetPeople()
{
return db.People.AsEnumerable();
}
// GET api/Person/5
public Person GetPerson(int id)
{
Person person = db.People.Find(id);
if (person == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
return person;
}
// PUT api/Person/5
public HttpResponseMessage PutPerson(int id, Person person)
{
if (!ModelState.IsValid)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
if (id != person.Id)
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
db.Entry(person).State = EntityState.Modified;
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex);
}
return Request.CreateResponse(HttpStatusCode.OK);
}
// POST api/Person
public HttpResponseMessage PostPerson(Person person)
{
if (ModelState.IsValid)
{
db.People.Add(person);
db.SaveChanges();
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, person);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = person.Id }));
return response;
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
}
// DELETE api/Person/5
public HttpResponseMessage DeletePerson(int id)
{
Person person = db.People.Find(id);
if (person == null)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
db.People.Remove(person);
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex);
}
return Request.CreateResponse(HttpStatusCode.OK, person);
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}
}
Share