This is an example app showcasing some of the ways that I use Angular within a Grails project. This is a proof of concept but it should serve as a nice starting point for anyone wanting to use the two frameworks together.
This project also makes use of Twitter Bootstrap, Angular UI Bootstrap and Font Awesome.
This is a slightly modified version of the standard Grails RestfulController. It adds support for server side paging and can be used exactly the same way the RestfulController is used.
Here's how you would add a REST controller for the Book domain class:
class BookController extends AngularController {
BookController() {
super(Book)
}
}
This project makes use of the Asset Pipeline along with two AngularJs specfic asset pipeline plugins that I developed:
The Angular Template Asset Pipeline plugin works with the excellent Fields plugin, and you'll see that I've made heavy use of that.
For example, the create-edit template contains the following and provides customized rendering for each data type:
<f:with bean="${new Book()}">
<f:field property="title" input-ng-model="item.title" />
<f:field property="author" input-ng-model="item.author" input-ng-options="author as author.lastName for author in authors track by author.id" />
<f:field property="price" input-ng-model="item.price" />
<f:field property="publishDate" input-ng-model="item.publishDate" />
<f:field property="pageCount" input-ng-model="item.pageCount" />
</f:with>
This project includes an AngularJS module called angularGrails that you can include as a dependency in your own angular modules.
This is a generalized service used to make REST calls. The constants rootUrl and restUrl must be set for these methods to work correctly. This service is esentially a wrapper for Angular's own $resource module but returns a promise instead of a resource object.
Config:
// Modules should be previously defined
angular.module('angularGrails.constants').constant('rootUrl', '/');
angular.module('myApp').constant('restUrl', '/api/book');
Here's an example of how you might use each available method:
// CrudService.list
CrudService.list({page: 1}).then(function(response) {
$scope.items = response;
});
// CrudService.create
CrudService.create().then(function(response) {
$scope.newItem = response;
});
// CrudService.get
CrudService.get(1).then(function(response) {
$scope.currentItem = response;
});
// CrudService.update
var item = {id: 1, title: 'Foo Bar'};
CrudService.update(item);
// CrudService.delete
CrudService.delete(1);
Each of the above functions can also accept an optional success and error callback function:
var successFunction = function(response) {
console.log("It worked!");
};
var errorFunction = function(response) {
console.log("Uh oh!");
};
CrudService.delete(1, successFunction, errorFunction);
Used in conjunction with the flash-message directive below. This service allows you to easily set different messages in your app. Each time a flash message is set it overrides the previous one.
FlashService.success("Everything is fine");
FlashService.warning("Something bad is about to happen");
FlashService.error("Uh oh, something bad did happen");
FlashService.info("Something good or bad might happen");
FlashService.clear(); // Clear message
This directive allows you to add buttons that make use of the CrudService.
The click actions of these buttons are automatically set to make the appropriate crudService method call. For example, clicking the delete button will call the CrudService.delete method.
<button crud-button="delete" resource="item" ></button>
<button crud-button="edit" resource="item" ></button>
<button crud-button="save" resource="item" ></button>
<button crud-button="create" ></button>
<button crud-button="cancel" ></button>
You can also include an optional afterAction parameter to register a callback or isDisabled to disable a button.
<button crud-button="delete" resource="item" after-action="logDelete()"></button>
<button crud-button="save" resource="item" is-disabled="form.$invalid"></button>
The button templates are located at:
/grails-app/assets/templates/angular-grails/directives/buttons
This directive is used along with the FlashService above to display messages on the page.
<div flash-message></div>
The flash message template is located at:
/grails-app/assets/templates/angular-grails/directives/flash-message.tpl.html
This directive allows you to keep track of the current sort state of a table, and has an onSort callback to allow you to reload your data if need be.
<thead sort-header ng-model="sort" on-sort="reloadData()">
<th sortable-column title="Id" property="id"></th>
<th sortable-column title="Name" property="name"></th>
</thead>
The sortable column template is located at:
/grails-app/assets/templates/angular-grails/directives/sortable-column.tpl.html