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
   |-testGrails 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 blankobj.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
- 700