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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
<!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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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