Grails And Angular
Bridging The Gap
What I do
Explore
Create
TEST
What is Grails?
- JVM based solution for web applications
- Built on proven technologies like Spring and Groovy
- Mature and Established in many businesses
WhY Grails?
-
JAVA!!!!!
- Large pool of developers
- Groovy/Spring
Resonable Rates
Source Business Insider
Flourishing Community
Source Redmonk | X: # Github Y: # Stackoverflow
Typical Layout
Grails V1
-
One page per action
- Offload complexity to services
Grails V2
-
Start using ajax with special GSP tags
- Provide respond in addition to render
Next Gen 2.4/3.0
- Gradle over GANT
- Spring Web Components
- Java 8
- Multiple Sever Environments
|-grails-app
|---assets
|-----images
|-----javascripts
|-----stylesheets
|---conf
|-----hibernate
|-----spring
|---controllers
|-----org
|-------gleason
|---------test
|---domain
|---i18n
|---migrations
|---services
|-----org
|-------gleason
|---------test
|---taglib
|---utils
|---views
|-----layouts
|-----socket
|-----thefirm
|-scripts
|-src
|---groovy
|-----org
|-------gleason
|---------test
|-----------command
|---java
|-test
Grails Structure
class QuestionController {
def questionService
def index(){
}
def get() {
render questionService.get(params.id) as JSON
}
def update() {
def obj = JSON.parse(request.reader.text);
Question question = new Question();
question.key = obj.key
question.text = obj.text
question.desc = obj.desc
question.voteCount = obj.voteCount
render questionService.update(question) as JSON
}
def create(Question q){
render questionService.create(q) as JSON
}
def delete(){
def text = request.reader.text;
def slurper = new JsonSlurper();
def result = slurper.parseText(text)
render questionService.delete(result.key)
}
}
Grails Controller
class UrlMappings {
static mappings = {
"/Note/$id?"(controller: "question", parseRequest: true) {
action = [GET: "get", POST: "create", PUT: "update", DELETE: "delete"]
}
"/socket"(controller:"socket"){
action=[GET:"index"]
}
"/"(view:"/app")
"500"(view:'/error')
}
}
URL Mapping
class SocketController {
def index(){
}
@MessageMapping("/hello")
@SendTo("/topic/hello")
protected String hello(String world) {
return "hello from controller, ${world}!"+new String(UUID.randomUUID().getMostSignificantBits());
}
}
Grails STomp
import grails.validation.Validateable
@Validateable
class Question {
Integer key
String text
String desc
Integer voteCount
List<String> errorMessages
static constraints = {
key unique: true
text blank: false
desc blank: false
errorMessages nullable: true
}
}
Command Object And Validation
question.desc.nullable=Description cannot be null
question.desc.blank=Description cannot be blank
obj.validate()
if(!obj.hasErrors()){
notes.put(obj.key.toString(), obj)
}
else{
obj.errorMessages = obj?.errors?.allErrors?.collect{messageSource.getMessage(it,null)
}
window.appContext = '${request.contextPath}';
Setting Up the Context Root
What is ANGular?
- Javascript
- MVC platform for the client
- Allows for efficient 2-way Data Binding
- Supported by Google
Why Angular
- Smaller Requests == Less Bandwidth == $$
- Realtime Data-Binding
- Single Page Paradigm
module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-html2js');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-ng-annotate');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-cucumber');
grunt.initConfig({
pkg : grunt.file.readJSON('package.json'),
html2js : {
build : {
src : [ 'src/main/partials/**/*.html',
'src/main/partials/**/*.jade' ],
dest : 'build/ng-grails-templates.js'
}
},
concat : {
core : {
src : [ 'src/main/angular/**/app.js',
'src/main/angular/**/*.js'],
dest : 'build/ng-grails-core.js',
},
combine: {
src:['build/ng-grails-templates.js',
'build/ng-grails-core.js'],
dest : 'build/ng-grails.js',
},
jasmine: {
src:['src/test/jasmine/**/*.js'],
dest:'build/ng-grails-test.js'
}
},
clean: {
build: ["build",'web-app/js/ng-grails.js','web-app/js/templates.js']
},
watch : {
html2js : {
files : [ 'src/main/partials/**/*.html',
'src/main/partials/**/*.jade' ],
tasks : [ 'html2js' ]
},
concat : {
files : ['src/main/angular/**/app.js',
'src/main/angular/**/*.js',
'build/ng-grails-templates.js'],
tasks : [ 'concat' ]
}
},
ngAnnotate: {
options: {
remove: true
},
release: {
files: {
'build/ng-grails-annotated.js': ['build/ng-grails.js']
}
},
},
uglify: {
release: {
files: {
'build/ng-grails-min.js': ['build/ng-grails-annotated.js']
}
}
},
copy: {
release: {
files: [
{src: ['build/ng-grails-min.js'], dest: 'web-app/js/ng-grails.js'},
],
},
build: {
files: [
{src: ['build/ng-grails.js'], dest: 'web-app/js/ng-grails.js'},
],
},
jasmine: {
files: [
{src: ['build/ng-grails-test.js'], dest: 'web-app/js/ng-grails-test.js'}
]
}
},
cucumberjs: {
src: 'src/test/features',
options: {
steps: "src/test/features/step_definitions"
}
}
});
grunt.registerTask('default', [ 'build' ]);
grunt.registerTask('build', [ 'clean', 'html2js', 'concat:core', 'concat:combine', 'copy:build']);
grunt.registerTask('release', [ 'clean', 'html2js', 'concat:core', 'concat:combine', 'ngAnnotate', 'uglify', 'copy:release' ]);
grunt.registerTask('test', ['build','concat:jasmine', 'copy:jasmine']);
return grunt;
}
Grunt File
Copy of deck
By Jackie Gleason
Copy of deck
- 594