Thursday, August 1, 2013

AngularJS CRUD Grid v3: Now a Directive and Multiple Grids per page

Today I took my V2 Crud Grid and moved it into an AngularJS directive. That allows me to reuse the crud grid functionality and have many of them on the same page.  Here’s what it looks like now:

image

 

CODE: https://github.com/jonbgallant/AngularJS-WebApi-EF

DEMO: http://angularjs-webapi-crud-grid.azurewebsites.net/

 

 

Here’s what I did in a nutshell:

1. Moved all the angular code to the /Scripts folder:

 

image

 

2. app.js only contains this one line of code to initialize the app:

var app = angular.module('app', ['ngResource']);

3. I put the notification factory under /Services

4. I created the crud-grid-directive.js file and put the directive code. I renamed person to object so that we can reuse the directive for multiple tables.



scope: true – tells angular to create a new scope for each instance of the directive


$attrs.tableName is set in the view…see how below.


 

app.directive('crudGrid', function () {
return {
restrict: 'A',
replace: false,
scope: true,
templateUrl: '/Scripts/App/Directives/Templates/crud-grid-directive-template.html',
controller: ['$scope', '$element', '$attrs', 'crudGridDataFactory', 'notificationFactory',
function ($scope, $element, $attrs, crudGridDataFactory, notificationFactory) {
$scope.objects = [];
$scope.addMode = false;

$scope.toggleAddMode = function () {
$scope.addMode = !$scope.addMode;
};

$scope.toggleEditMode = function (object) {
object.editMode = !object.editMode;
};

var successCallback = function (e, cb) {
notificationFactory.success();
$scope.getData(cb);
};

var successPostCallback = function (e) {
successCallback(e, function () {
$scope.toggleAddMode();
$scope.object = {};
});
};

var errorCallback = function (e) {
notificationFactory.error(e.object.Message);
};

$scope.addObject = function () {
crudGridDataFactory($attrs.tableName).save($scope.object, successPostCallback, errorCallback);
};

$scope.deleteObject = function (object) {
crudGridDataFactory($attrs.tableName).delete({ id: object.Id }, successCallback, errorCallback);
};

$scope.updateObject = function (object) {
crudGridDataFactory($attrs.tableName).update({ id: object.Id }, object, successCallback, errorCallback);
};

$scope.getData = function (cb) {
crudGridDataFactory($attrs.tableName).query(function (data) {
$scope.objects = data;
if (cb) cb();
});
};

$scope.getData();
}]
}
});

 


5. Moved the factory code to /Directives/Services. It now accepts a type and builds the URL from that so we can reuse for multiple tables.

app.factory('crudGridDataFactory', ['$http', '$resource', function ($http, $resource) {
return function (type) {
return $resource('api/' + type + '/:id', { id: '@id' }, { 'update': { method: 'PUT' } }, { 'query': { method: 'GET', isArray: false } });
};
}]);

6. As you can see in the code above I created a new template for the directive in Directives/Templates called crud-grid-directive-template.html, which contains the HTML markup that the grid will render.


 


 

<table class="crud-grid 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="addObject()"></i>
<i class="btn icon-remove" ng-click="toggleAddMode()"></i>
</div>
</div>
</td>
<td></td>
<td>
<input ng-model="object.Name" />
</td>
</tr>
<tr ng-repeat="object in objects | orderBy:'Id':true">
<td>
<div class="btn-toolbar" ng-show="object.editMode == null || object.editMode == false">
<div class="btn-group">
<i class="btn icon-edit" ng-click="toggleEditMode(object)"></i>
<i class="btn icon-trash" ng-click="deleteObject(object)"></i>
</div>
</div>
<div class="btn-toolbar" ng-show="object.editMode == true">
<div class="btn-group">
<i class="btn icon-save" ng-click="updateObject(object)"></i>
<i class="btn icon-remove" ng-click="toggleEditMode(object)"></i>
</div>
</div>
</td>
<td>{{object.Id}}</td>
<td>
<span ng-show="object.editMode == null || object.editMode == false">{{object.Name}}</span>
<input ng-model="object.Name" ng-show="object.editMode == true" />
</td>
</tr>
</table>

 


7. Now all that is left in the view is this, which defines two crud grids and assigns the corresponding tables.


 

<h1>People</h1>
<div crud-grid table-name='person'></div>
<h1>Places</h1>
<div crud-grid table-name='place'></div>

 


(I also created a new “Place” model and controller)


 


Hope this helps you ramp up on AngularJS directives.


Jon