├── .gitignore ├── README.md ├── angular2-springboot-engine.iml ├── package.json ├── pom.xml └── src ├── main ├── java │ └── io │ │ └── angular │ │ └── universal │ │ ├── MainController.java │ │ ├── Universal.java │ │ ├── UniversalApplication.java │ │ └── io │ │ └── angular │ │ └── universal │ │ └── engine │ │ ├── Engine.java │ │ └── RequireModule.java └── resources │ ├── application.properties │ └── static │ └── todo │ ├── app.js │ ├── app.js.map │ ├── app.ts │ ├── browser.js │ ├── browser.js.map │ ├── browser.ts │ ├── css │ ├── base.css │ ├── bg.png │ └── main.css │ ├── index.ng2.html │ ├── services │ ├── TodoStore.js │ ├── TodoStore.js.map │ └── TodoStore.ts │ └── todo.html └── test └── java └── io └── angular └── universal ├── ApplicationTests.java └── UniversalTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | .idea 5 | .vscode 6 | .DS_Store 7 | **/.DS_Store 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | /lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | /coverage 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # Dependency directory 24 | # Commenting this out is preferred by some people, see 25 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 26 | node_modules 27 | 28 | # used for karma unit test coverage 29 | test/coverage 30 | 31 | # Users Environment Variables 32 | .lock-wscript 33 | 34 | # Server 35 | /dist/ 36 | 37 | # Static files 38 | 39 | /bower_components/ 40 | /web_modules/ 41 | 42 | # Typings 43 | /typings 44 | /tsd_typings 45 | 46 | /target/ 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # angular2-springboot-engine 2 | This is an experiment to add Angular2 Universal support to Spring Boot 3 | 4 | ## WIP...stay tuned 5 | -------------------------------------------------------------------------------- /angular2-springboot-engine.iml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular2-springboot-engine", 3 | "main": "dist/index.js", 4 | "typings": "angular2-springboot-engine.d.ts", 5 | "version": "0.0.1", 6 | "description": "Universal (isomorphic) javascript support for Angular2", 7 | "homepage": "https://github.com/angular/universal", 8 | "license": "MIT", 9 | "contributors": [ 10 | "Tobias Bosch ", 11 | "PatrickJS ", 12 | "Jeff Whelpley " 13 | ], 14 | "scripts": {}, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/angular/universal" 18 | }, 19 | "bugs": { 20 | "url": "https://github.com/angular/universal/issues" 21 | }, 22 | "devDependencies": { 23 | "tsd": "^0.6.4", 24 | "typescript": "^1.7.3" 25 | }, 26 | "dependencies": { 27 | "angular2": "2.0.0-beta.3", 28 | "zone.js": "0.5.11", 29 | "rxjs": "5.0.0-beta.0", 30 | "systemjs": "^0.19.16", 31 | "reflect-metadata": "0.1.2", 32 | "es6-shim": "^0.33.8", 33 | "angular2-universal-preview": "*", 34 | "parse5": "^1.5.0", 35 | "css": "^2.2.1", 36 | "preboot": "*" 37 | }, 38 | "peerDependencies": {} 39 | } 40 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | io.angular.universal 7 | angular2-springboot-engine 8 | 0.0.1-SNAPSHOT 9 | war 10 | 11 | angular2-springboot-engine 12 | Spring Boot Angular 2 Universal Example 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.2.3.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | io.angular.universal.UniversalApplication 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-tomcat 35 | 36 | 37 | org.apache.tomcat.embed 38 | tomcat-embed-jasper 39 | 40 | 41 | javax.servlet 42 | jstl 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-test 47 | test 48 | 49 | 50 | org.jsoup 51 | jsoup 52 | 1.7.3 53 | test 54 | 55 | 56 | 57 | 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-maven-plugin 62 | 63 | 64 | org.springframework 65 | springloaded 66 | 1.2.4.RELEASE 67 | 68 | 69 | 70 | 71 | -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /src/main/java/io/angular/universal/MainController.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wchegham on 07/02/16. 3 | */ 4 | package io.angular.universal; 5 | 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Controller; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | 10 | @Controller 11 | public class MainController { 12 | 13 | public MainController() {} 14 | 15 | @RequestMapping("/") 16 | public String index() throws Exception { 17 | return "index"; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/angular/universal/Universal.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wchegham on 07/02/16. 3 | */ 4 | package io.angular.universal; 5 | 6 | 7 | import io.angular.universal.io.angular.universal.engine.RequireModule; 8 | import jdk.nashorn.api.scripting.NashornScriptEngine; 9 | 10 | public class Universal { 11 | 12 | NashornScriptEngine engine; 13 | 14 | Universal() { 15 | this.engine = (new RequireModule()).angular(); 16 | } 17 | 18 | public String render() { 19 | try { 20 | String html = ":)"; 21 | return String.valueOf(html); 22 | } catch (Exception e) { 23 | throw new IllegalStateException("failed to render angular component", e); 24 | } 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /src/main/java/io/angular/universal/UniversalApplication.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by wchegham on 07/02/16. 3 | */ 4 | package io.angular.universal; 5 | 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.autoconfigure.SpringBootApplication; 8 | import org.springframework.boot.builder.SpringApplicationBuilder; 9 | import org.springframework.boot.context.web.SpringBootServletInitializer; 10 | 11 | @SpringBootApplication 12 | public class UniversalApplication extends SpringBootServletInitializer { 13 | 14 | @Override 15 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { 16 | return application.sources(UniversalApplication.class); 17 | } 18 | 19 | public static void main(String[] args) { 20 | SpringApplication.run(UniversalApplication.class, args); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/io/angular/universal/io/angular/universal/engine/Engine.java: -------------------------------------------------------------------------------- 1 | package io.angular.universal.io.angular.universal.engine; 2 | 3 | import com.sun.xml.internal.xsom.impl.scd.Iterators; 4 | import jdk.nashorn.api.scripting.NashornScriptEngine; 5 | import jdk.nashorn.internal.objects.annotations.Function; 6 | 7 | import javax.script.ScriptException; 8 | import java.io.InputStream; 9 | import java.io.InputStreamReader; 10 | import java.io.Reader; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | * Created by wchegham on 07/02/16. 16 | */ 17 | public class Engine { 18 | 19 | private interface IEngineOptions { 20 | Function App = null; 21 | Iterators.Array providers = null; 22 | Object preboot = null; 23 | String selector = null; 24 | String serializedCmp = null; 25 | boolean server = false; 26 | boolean client = false; 27 | } 28 | 29 | private interface IQueryParams { 30 | boolean preboot = false; 31 | boolean angular = false; 32 | boolean bootstrap = false; 33 | boolean client = false; 34 | boolean server = false; 35 | String componentUrl = ""; 36 | } 37 | 38 | private NashornScriptEngine engine; 39 | 40 | static String prebootScript = " " + 41 | " " + 42 | " " + 43 | " " + 44 | " "; 45 | 46 | static String angularScript = " " + 47 | " " + 48 | " " + 49 | " " + 50 | " " + 51 | " " + 52 | " " + 53 | " " + 54 | " " + 55 | " " + 56 | " " + 57 | " "; 63 | static String bootstrapButton = "
" + 64 | " " + 74 | " " + 77 | "
"; 78 | static String bootstrapApp = " "; 83 | 84 | 85 | Engine() { 86 | engine = (new RequireModule()).universal(); 87 | } 88 | 89 | public String bootstrapFunction(String appUrl) { 90 | return ""; 105 | } 106 | 107 | /* 108 | * TODO: find better ways to configure the App initial state 109 | * to pay off this technical debt 110 | * currently checking for explicit values 111 | * */ 112 | public String buildClientScripts(String html, IQueryParams options) { 113 | return html 114 | .replace( 115 | this.call("selectorRegExpFactory", "preboot"), 116 | ((options.preboot == false) ? "" : this.prebootScript) 117 | ) 118 | .replace( 119 | this.call("selectorRegExpFactory", "angular"), 120 | ((options.angular == false) ? "" : this.angularScript) 121 | ) 122 | .replace( 123 | this.call("selectorRegExpFactory", "bootstrap"), 124 | ((options.bootstrap == false) ? ( 125 | this.bootstrapButton + 126 | bootstrapFunction(options.componentUrl) 127 | ) : ( 128 | ( 129 | (options.client == false) ? "" : this.bootstrapButton 130 | ) + 131 | bootstrapFunction(options.componentUrl) + 132 | ((options.client == false) ? "" : this.bootstrapApp) 133 | )) 134 | ); 135 | } 136 | 137 | private String call(String funcName, Object params) { 138 | try { 139 | return (String) this.engine.invokeFunction(funcName, params); 140 | } catch (ScriptException e) { 141 | e.printStackTrace(); 142 | } catch (NoSuchMethodException e) { 143 | e.printStackTrace(); 144 | } 145 | return ""; 146 | } 147 | 148 | protected Reader read(String path) { 149 | InputStream in = getClass().getClassLoader().getResourceAsStream(path); 150 | return new InputStreamReader(in); 151 | } 152 | 153 | public String ng2engine(String filePath, IQueryParams queryOptions, IEngineOptions engineOption, Function done) { 154 | 155 | String clientHtml = read(filePath).toString(); 156 | if (queryOptions.server == false && queryOptions.client == false) { 157 | return clientHtml; 158 | } 159 | if (queryOptions.server == false && queryOptions.client != false) { 160 | return buildClientScripts(clientHtml, queryOptions); 161 | } 162 | 163 | // bootstrap and render component to string 164 | String renderPromise = this.call("renderToString", null); 165 | List args = new ArrayList<>(); 166 | args.add(engineOption.App); 167 | args.add(engineOption.providers); 168 | 169 | if (queryOptions.preboot) { 170 | renderPromise = this.call("renderToStringWithPreboot", null); 171 | args.add(queryOptions.preboot); 172 | } 173 | 174 | return this.call("renderPromise", args); 175 | 176 | /* 177 | // defaults 178 | options = options || {}; 179 | options.providers = options.providers || undefined; 180 | options.preboot = options.preboot || undefined; 181 | 182 | // read file on disk 183 | try { 184 | fs.readFile(filePath, (err, content) => { 185 | 186 | if (err) { return done(err); } 187 | 188 | // convert to string 189 | const clientHtml: string = content.toString(); 190 | 191 | // TODO: better build scripts abstraction 192 | if (options.server === false && options.client === false) { 193 | return done(null, clientHtml); 194 | } 195 | if (options.server === false && options.client !== false) { 196 | return done(null, buildClientScripts(clientHtml, options)); 197 | } 198 | // bootstrap and render component to string 199 | var renderPromise: any = renderToString; 200 | const args = [options.App, options.providers]; 201 | if (options.preboot) { 202 | renderPromise = renderToStringWithPreboot; 203 | args.push(options.preboot); 204 | } 205 | 206 | renderPromise(...args) 207 | .then(serializedCmp => { 208 | 209 | const selector: string = selectorResolver(options.App); 210 | 211 | // selector replacer explained here 212 | // https://gist.github.com/gdi2290/c74afd9898d2279fef9f 213 | // replace our component with serialized version 214 | const rendered: string = clientHtml.replace( 215 | // 216 | selectorRegExpFactory(selector), 217 | // {{ serializedCmp }} 218 | serializedCmp 219 | // TODO: serializedData 220 | ); 221 | 222 | done(null, buildClientScripts(rendered, options)); 223 | }) 224 | .catch(e => { 225 | console.log(e.stack); 226 | // if server fail then return client html 227 | done(null, buildClientScripts(clientHtml, options)); 228 | }); 229 | }); 230 | } catch (e) { 231 | done(e); 232 | } 233 | */ 234 | }; 235 | 236 | 237 | 238 | } 239 | -------------------------------------------------------------------------------- /src/main/java/io/angular/universal/io/angular/universal/engine/RequireModule.java: -------------------------------------------------------------------------------- 1 | package io.angular.universal.io.angular.universal.engine; 2 | 3 | import jdk.nashorn.api.scripting.NashornScriptEngine; 4 | import org.springframework.web.context.request.async.NoSupportAsyncWebRequest; 5 | 6 | import javax.script.ScriptEngineManager; 7 | import javax.script.ScriptException; 8 | import java.io.InputStream; 9 | import java.io.InputStreamReader; 10 | import java.io.Reader; 11 | 12 | /** 13 | * Created by wchegham on 07/02/16. 14 | */ 15 | public class RequireModule { 16 | 17 | private NashornScriptEngine nashornScriptEngine; 18 | 19 | //https://github.com/facebook/react/issues/3037 20 | private String polyfill = "" + 21 | "var global = this; \n" + 22 | "var console = {}; \n" + 23 | "console.debug = print; \n" + 24 | "console.warn = print; \n" + 25 | "console.log = print;"; 26 | 27 | public RequireModule(){ 28 | nashornScriptEngine = (NashornScriptEngine) new ScriptEngineManager().getEngineByName("nashorn"); 29 | } 30 | 31 | public NashornScriptEngine universal() { 32 | try { 33 | nashornScriptEngine.eval(polyfill); 34 | nashornScriptEngine.eval(read("static/node_modules/angular2-universal-preview/dist/server/index.js")); 35 | } catch (ScriptException e) { 36 | throw new RuntimeException(e); 37 | } 38 | return nashornScriptEngine; 39 | } 40 | 41 | 42 | public NashornScriptEngine angular() { 43 | try { 44 | nashornScriptEngine.eval(polyfill); 45 | nashornScriptEngine.eval(read("static/node_modules/es6-shim/es6-shim.min.js")); 46 | nashornScriptEngine.eval(read("static/node_modules/systemjs/dist/system-polyfills.js")); 47 | nashornScriptEngine.eval(read("static/node_modules/angular2/bundles/angular2-polyfills.min.js")); 48 | nashornScriptEngine.eval(read("static/node_modules/systemjs/dist/system.js")); 49 | nashornScriptEngine.eval(read("static/node_modules/rxjs/bundles/Rx.js")); 50 | nashornScriptEngine.eval(read("static/node_modules/angular2/bundles/angular2.dev.js")); 51 | nashornScriptEngine.eval(read("static/node_modules/angular2/bundles/router.dev.js")); 52 | nashornScriptEngine.eval(read("static/node_modules/angular2/bundles/http.dev.js")); 53 | nashornScriptEngine.eval("System.config({'baseURL':'/','defaultJSExtensions':true});"); 54 | } catch (ScriptException e) { 55 | throw new RuntimeException(e); 56 | } 57 | return nashornScriptEngine; 58 | } 59 | 60 | protected Reader read(String path) { 61 | InputStream in = getClass().getClassLoader().getResourceAsStream(path); 62 | return new InputStreamReader(in); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.view.prefix: /todo/ 2 | spring.view.suffix: .ng2.html -------------------------------------------------------------------------------- /src/main/resources/static/todo/app.js: -------------------------------------------------------------------------------- 1 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 2 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 3 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 4 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 5 | return c > 3 && r && Object.defineProperty(target, key, r), r; 6 | }; 7 | var core_d_1 = require('../node_modules/angular2/core.d'); 8 | var router_d_1 = require('../node_modules/angular2/router.d'); 9 | var TodoStore_1 = require('./services/TodoStore'); 10 | var TodoApp = (function () { 11 | function TodoApp(todoStore, factory) { 12 | this.todoStore = todoStore; 13 | this.factory = factory; 14 | this.todoEdit = null; 15 | this.selected = 0; 16 | } 17 | TodoApp.prototype.ngOnInit = function () { 18 | this.addTodo('Universal JavaScript'); 19 | this.addTodo('Run Angular 2 in Web Workers'); 20 | this.addTodo('Upgrade the web'); 21 | this.addTodo('Release Angular 2'); 22 | }; 23 | TodoApp.prototype.enterTodo = function ($event, inputElement) { 24 | if (!inputElement.value) { 25 | return; 26 | } 27 | if ($event.which !== 13) { 28 | return; 29 | } 30 | this.addTodo(inputElement.value); 31 | inputElement.value = ''; 32 | }; 33 | TodoApp.prototype.editTodo = function (todo) { 34 | this.todoEdit = todo; 35 | }; 36 | TodoApp.prototype.doneEditing = function ($event, todo) { 37 | var which = $event.which; 38 | var target = $event.target; 39 | if (which === 13) { 40 | todo.title = target.value; 41 | this.todoEdit = null; 42 | } 43 | else if (which === 27) { 44 | this.todoEdit = null; 45 | target.value = todo.title; 46 | } 47 | }; 48 | TodoApp.prototype.addTodo = function (newTitle) { 49 | this.todoStore.add(this.factory.create(newTitle, false)); 50 | }; 51 | TodoApp.prototype.completeMe = function (todo) { 52 | todo.completed = !todo.completed; 53 | }; 54 | TodoApp.prototype.deleteMe = function (todo) { 55 | this.todoStore.remove(todo); 56 | }; 57 | TodoApp.prototype.toggleAll = function ($event) { 58 | var isComplete = $event.target.checked; 59 | this.todoStore.list.forEach(function (todo) { return todo.completed = isComplete; }); 60 | }; 61 | TodoApp.prototype.clearCompleted = function () { 62 | this.todoStore.removeBy(function (todo) { return todo.completed; }); 63 | }; 64 | TodoApp.prototype.pluralize = function (count, word) { 65 | return "word" + (count === 1 ? '' : 's'); 66 | }; 67 | TodoApp.prototype.remainingCount = function () { 68 | return this.todoStore.list.filter(function (todo) { return !todo.completed; }).length; 69 | }; 70 | TodoApp = __decorate([ 71 | core_d_1.Component({ 72 | selector: 'app', 73 | providers: [TodoStore_1.Store, TodoStore_1.TodoFactory], 74 | encapsulation: core_d_1.ViewEncapsulation.None, 75 | directives: [router_d_1.ROUTER_DIRECTIVES], 76 | styles: [], 77 | template: "\n
\n\n
\n

todos

\n \n
\n\n
\n \n \n\n
    \n\n \n\n
    \n\n \n\n \n \n\n
    \n\n
    \n\n \n\n
    \n\n \n
\n
\n\n \n\n
\n " 78 | }) 79 | ], TodoApp); 80 | return TodoApp; 81 | })(); 82 | exports.TodoApp = TodoApp; 83 | //# sourceMappingURL=app.js.map -------------------------------------------------------------------------------- /src/main/resources/static/todo/app.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"app.js","sourceRoot":"","sources":["app.ts"],"names":["TodoApp","TodoApp.constructor","TodoApp.ngOnInit","TodoApp.enterTodo","TodoApp.editTodo","TodoApp.doneEditing","TodoApp.addTodo","TodoApp.completeMe","TodoApp.deleteMe","TodoApp.toggleAll","TodoApp.clearCompleted","TodoApp.pluralize","TodoApp.remainingCount"],"mappings":"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"} -------------------------------------------------------------------------------- /src/main/resources/static/todo/app.ts: -------------------------------------------------------------------------------- 1 | import {ViewEncapsulation, Component} from '../node_modules/angular2/core.d'; 2 | 3 | import {ROUTER_DIRECTIVES} from '../node_modules/angular2/router.d'; 4 | 5 | import {Http} from '../node_modules/angular2/http.d'; 6 | 7 | 8 | import {Store, Todo, TodoFactory} from './services/TodoStore'; 9 | 10 | @Component({ 11 | selector: 'app', 12 | providers: [ Store, TodoFactory ], 13 | encapsulation: ViewEncapsulation.None, 14 | directives: [ROUTER_DIRECTIVES], 15 | styles: [], 16 | template: ` 17 |
18 | 19 | 29 | 30 |
31 | 36 | 37 | 38 |
    39 | 40 |
  • 44 | 45 |
    47 | 48 | 52 | 53 | 54 | 55 | 56 |
    57 | 58 |
    59 | 60 | 65 | 66 |
    67 | 68 |
  • 69 |
70 |
71 | 72 | 102 | 103 |
104 | ` 105 | }) 106 | export class TodoApp { 107 | todoEdit: Todo = null; 108 | selected: number = 0; 109 | constructor(public todoStore: Store, public factory: TodoFactory) { 110 | } 111 | 112 | ngOnInit() { 113 | this.addTodo('Universal JavaScript'); 114 | this.addTodo('Run Angular 2 in Web Workers'); 115 | this.addTodo('Upgrade the web'); 116 | this.addTodo('Release Angular 2'); 117 | } 118 | 119 | enterTodo($event, inputElement) { 120 | if (!inputElement.value) { return; } 121 | if ($event.which !== 13) { return; } 122 | this.addTodo(inputElement.value); 123 | inputElement.value = ''; 124 | } 125 | 126 | editTodo(todo: Todo) { 127 | this.todoEdit = todo; 128 | } 129 | 130 | doneEditing($event, todo: Todo) { 131 | var which = $event.which; 132 | var target = $event.target; 133 | 134 | if (which === 13) { 135 | todo.title = target.value; 136 | this.todoEdit = null; 137 | } else if (which === 27) { 138 | this.todoEdit = null; 139 | target.value = todo.title; 140 | } 141 | 142 | } 143 | 144 | addTodo(newTitle: string) { 145 | this.todoStore.add(this.factory.create(newTitle, false)); 146 | } 147 | 148 | completeMe(todo: Todo) { 149 | todo.completed = !todo.completed; 150 | } 151 | 152 | deleteMe(todo: Todo) { 153 | this.todoStore.remove(todo); 154 | } 155 | 156 | toggleAll($event) { 157 | var isComplete = $event.target.checked; 158 | this.todoStore.list.forEach((todo: Todo) => todo.completed = isComplete); 159 | } 160 | 161 | clearCompleted() { 162 | this.todoStore.removeBy(todo => todo.completed); 163 | } 164 | 165 | pluralize(count, word) { 166 | return `word${count === 1 ? '' : 's'}`; 167 | } 168 | 169 | remainingCount() { 170 | return this.todoStore.list.filter((todo: Todo) => !todo.completed).length; 171 | } 172 | } 173 | 174 | -------------------------------------------------------------------------------- /src/main/resources/static/todo/browser.js: -------------------------------------------------------------------------------- 1 | var browser_d_1 = require('../node_modules/angular2/platform/browser.d'); 2 | var router_d_1 = require('../node_modules/angular2/router.d'); 3 | var http_d_1 = require('../node_modules/angular2/http.d'); 4 | // import { 5 | // NG_PRELOAD_CACHE_PROVIDERS, 6 | // PRIME_CACHE 7 | // } from '../../../../modules/universal/client/client'; 8 | var app_1 = require('./app'); 9 | function main() { 10 | return browser_d_1.bootstrap(app_1.TodoApp, [ 11 | router_d_1.ROUTER_PROVIDERS, 12 | http_d_1.HTTP_PROVIDERS, 13 | ]); 14 | } 15 | exports.main = main; 16 | //# sourceMappingURL=browser.js.map -------------------------------------------------------------------------------- /src/main/resources/static/todo/browser.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"browser.js","sourceRoot":"","sources":["browser.ts"],"names":["main"],"mappings":"AAEA,0BAAwB,6CAA6C,CAAC,CAAA;AAGtE,yBAA+B,mCAAmC,CAAC,CAAA;AAEnE,uBAA6B,iCAAiC,CAAC,CAAA;AAE/D,WAAW;AACX,gCAAgC;AAChC,gBAAgB;AAChB,wDAAwD;AAGxD,oBAAsB,OAAO,CAAC,CAAA;AAE9B;IACEA,MAAMA,CAACA,qBAASA,CAACA,aAAOA,EAAEA;QACxBA,2BAAgBA;QAChBA,uBAAcA;KAGfA,CAACA,CAACA;AACLA,CAACA;AAPe,YAAI,OAOnB,CAAA"} -------------------------------------------------------------------------------- /src/main/resources/static/todo/browser.ts: -------------------------------------------------------------------------------- 1 | import {provide} from '../node_modules/angular2/core.d'; 2 | 3 | import {bootstrap} from '../node_modules/angular2/platform/browser.d'; 4 | 5 | 6 | import {ROUTER_PROVIDERS} from '../node_modules/angular2/router.d'; 7 | 8 | import {HTTP_PROVIDERS} from '../node_modules/angular2/http.d'; 9 | 10 | // import { 11 | // NG_PRELOAD_CACHE_PROVIDERS, 12 | // PRIME_CACHE 13 | // } from '../../../../modules/universal/client/client'; 14 | 15 | 16 | import {TodoApp} from './app'; 17 | 18 | export function main() { 19 | return bootstrap(TodoApp, [ 20 | ROUTER_PROVIDERS, 21 | HTTP_PROVIDERS, 22 | // NG_PRELOAD_CACHE_PROVIDERS, 23 | // provide(PRIME_CACHE, {useValue: true}) 24 | ]); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/resources/static/todo/css/base.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | button { 4 | margin: 0; 5 | padding: 0; 6 | border: 0; 7 | background: none; 8 | font-size: 100%; 9 | vertical-align: baseline; 10 | font-family: inherit; 11 | font-weight: inherit; 12 | color: inherit; 13 | -webkit-appearance: none; 14 | -ms-appearance: none; 15 | appearance: none; 16 | -webkit-font-smoothing: antialiased; 17 | -moz-font-smoothing: antialiased; 18 | -ms-font-smoothing: antialiased; 19 | font-smoothing: antialiased; 20 | } 21 | 22 | button, 23 | input[type="checkbox"] { 24 | outline: none; 25 | } 26 | 27 | .hidden { 28 | display: none; 29 | } 30 | 31 | .visible { 32 | display: block !important; 33 | } 34 | 35 | #todoapp { 36 | background: #fff; 37 | margin: 130px 0 40px 0; 38 | position: relative; 39 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 40 | 0 25px 50px 0 rgba(0, 0, 0, 0.1); 41 | } 42 | 43 | #todoapp input::-webkit-input-placeholder { 44 | font-style: italic; 45 | font-weight: 300; 46 | color: #e6e6e6; 47 | } 48 | 49 | #todoapp input::-moz-placeholder { 50 | font-style: italic; 51 | font-weight: 300; 52 | color: #e6e6e6; 53 | } 54 | 55 | #todoapp input::input-placeholder { 56 | font-style: italic; 57 | font-weight: 300; 58 | color: #e6e6e6; 59 | } 60 | 61 | #todoapp h1 { 62 | position: absolute; 63 | top: -155px; 64 | width: 100%; 65 | font-size: 100px; 66 | font-weight: 100; 67 | text-align: center; 68 | color: rgba(175, 47, 47, 0.15); 69 | -webkit-text-rendering: optimizeLegibility; 70 | -moz-text-rendering: optimizeLegibility; 71 | -ms-text-rendering: optimizeLegibility; 72 | text-rendering: optimizeLegibility; 73 | } 74 | 75 | #new-todo, 76 | .edit { 77 | position: relative; 78 | margin: 0; 79 | width: 100%; 80 | font-size: 24px; 81 | font-family: inherit; 82 | font-weight: inherit; 83 | line-height: 1.4em; 84 | border: 0; 85 | outline: none; 86 | color: inherit; 87 | padding: 6px; 88 | border: 1px solid #999; 89 | box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); 90 | -ms-box-sizing: border-box; 91 | box-sizing: border-box; 92 | -webkit-font-smoothing: antialiased; 93 | -moz-font-smoothing: antialiased; 94 | -ms-font-smoothing: antialiased; 95 | font-smoothing: antialiased; 96 | } 97 | 98 | #new-todo { 99 | padding: 16px 16px 16px 60px; 100 | border: none; 101 | background: rgba(0, 0, 0, 0.003); 102 | box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); 103 | } 104 | 105 | #main { 106 | position: relative; 107 | z-index: 2; 108 | border-top: 1px solid #e6e6e6; 109 | } 110 | 111 | label[for='toggle-all'] { 112 | display: none; 113 | } 114 | 115 | #toggle-all { 116 | position: absolute; 117 | top: -55px; 118 | left: -12px; 119 | width: 60px; 120 | height: 34px; 121 | text-align: center; 122 | border: none; /* Mobile Safari */ 123 | } 124 | 125 | #toggle-all:before { 126 | content: '❯'; 127 | font-size: 22px; 128 | color: #e6e6e6; 129 | padding: 10px 27px 10px 27px; 130 | } 131 | 132 | #toggle-all:checked:before { 133 | color: #737373; 134 | } 135 | 136 | #todo-list { 137 | margin: 0; 138 | padding: 0; 139 | list-style: none; 140 | } 141 | 142 | #todo-list li { 143 | position: relative; 144 | font-size: 24px; 145 | border-bottom: 1px solid #ededed; 146 | } 147 | 148 | #todo-list li:last-child { 149 | border-bottom: none; 150 | } 151 | 152 | #todo-list li.editing { 153 | border-bottom: none; 154 | padding: 0; 155 | } 156 | 157 | #todo-list li.editing .edit { 158 | display: block; 159 | width: 506px; 160 | padding: 13px 17px 12px 17px; 161 | margin: 0 0 0 43px; 162 | } 163 | 164 | #todo-list li.editing .view { 165 | display: none; 166 | } 167 | 168 | #todo-list li .toggle { 169 | text-align: center; 170 | width: 40px; 171 | /* auto, since non-WebKit browsers doesn't support input styling */ 172 | height: auto; 173 | position: absolute; 174 | top: 0; 175 | bottom: 0; 176 | margin: auto 0; 177 | border: none; /* Mobile Safari */ 178 | -webkit-appearance: none; 179 | -ms-appearance: none; 180 | appearance: none; 181 | } 182 | 183 | #todo-list li .toggle:after { 184 | content: url('data:image/svg+xml;utf8,'); 185 | } 186 | 187 | #todo-list li .toggle:checked:after { 188 | content: url('data:image/svg+xml;utf8,'); 189 | } 190 | 191 | #todo-list li label { 192 | white-space: pre; 193 | word-break: break-word; 194 | padding: 15px 60px 15px 15px; 195 | margin-left: 45px; 196 | display: block; 197 | line-height: 1.2; 198 | transition: color 0.4s; 199 | } 200 | 201 | #todo-list li.completed label { 202 | color: #d9d9d9; 203 | text-decoration: line-through; 204 | } 205 | 206 | #todo-list li .destroy { 207 | display: none; 208 | position: absolute; 209 | top: 0; 210 | right: 10px; 211 | bottom: 0; 212 | width: 40px; 213 | height: 40px; 214 | margin: auto 0; 215 | font-size: 30px; 216 | color: #cc9a9a; 217 | margin-bottom: 11px; 218 | transition: color 0.2s ease-out; 219 | } 220 | 221 | #todo-list li .destroy:hover { 222 | color: #af5b5e; 223 | } 224 | 225 | #todo-list li .destroy:after { 226 | content: '×'; 227 | } 228 | 229 | #todo-list li:hover .destroy { 230 | display: block; 231 | } 232 | 233 | #todo-list li .edit { 234 | display: none; 235 | } 236 | 237 | #todo-list li.editing:last-child { 238 | margin-bottom: -1px; 239 | } 240 | 241 | #footer { 242 | color: #777; 243 | padding: 10px 15px; 244 | height: 20px; 245 | text-align: center; 246 | border-top: 1px solid #e6e6e6; 247 | } 248 | 249 | #footer:before { 250 | content: ''; 251 | position: absolute; 252 | right: 0; 253 | bottom: 0; 254 | left: 0; 255 | height: 50px; 256 | overflow: hidden; 257 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 258 | 0 8px 0 -3px #f6f6f6, 259 | 0 9px 1px -3px rgba(0, 0, 0, 0.2), 260 | 0 16px 0 -6px #f6f6f6, 261 | 0 17px 2px -6px rgba(0, 0, 0, 0.2); 262 | } 263 | 264 | #todo-count { 265 | float: left; 266 | text-align: left; 267 | } 268 | 269 | #todo-count strong { 270 | font-weight: 300; 271 | } 272 | 273 | #filters { 274 | margin: 0; 275 | padding: 0; 276 | list-style: none; 277 | position: absolute; 278 | right: 0; 279 | left: 0; 280 | } 281 | 282 | #filters li { 283 | display: inline; 284 | } 285 | 286 | #filters li a { 287 | color: inherit; 288 | margin: 3px; 289 | padding: 3px 7px; 290 | text-decoration: none; 291 | border: 1px solid transparent; 292 | border-radius: 3px; 293 | } 294 | 295 | #filters li a.selected, 296 | #filters li a:hover { 297 | border-color: rgba(175, 47, 47, 0.1); 298 | } 299 | 300 | #filters li a.selected { 301 | border-color: rgba(175, 47, 47, 0.2); 302 | } 303 | 304 | #clear-completed, 305 | html #clear-completed:active { 306 | float: right; 307 | position: relative; 308 | line-height: 20px; 309 | text-decoration: none; 310 | cursor: pointer; 311 | visibility: hidden; 312 | position: relative; 313 | } 314 | 315 | #clear-completed::after { 316 | visibility: visible; 317 | content: 'Clear completed'; 318 | position: absolute; 319 | right: 0; 320 | white-space: nowrap; 321 | } 322 | 323 | #clear-completed:hover::after { 324 | text-decoration: underline; 325 | } 326 | 327 | #info { 328 | margin: 65px auto 0; 329 | color: #bfbfbf; 330 | font-size: 10px; 331 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); 332 | text-align: center; 333 | } 334 | 335 | #info p { 336 | line-height: 1; 337 | } 338 | 339 | #info a { 340 | color: inherit; 341 | text-decoration: none; 342 | font-weight: 400; 343 | } 344 | 345 | #info a:hover { 346 | text-decoration: underline; 347 | } 348 | 349 | /* 350 | Hack to remove background from Mobile Safari. 351 | Can't use it globally since it destroys checkboxes in Firefox 352 | */ 353 | @media screen and (-webkit-min-device-pixel-ratio:0) { 354 | #toggle-all, 355 | #todo-list li .toggle { 356 | background: none; 357 | } 358 | 359 | #todo-list li .toggle { 360 | height: 40px; 361 | } 362 | 363 | #toggle-all { 364 | -webkit-transform: rotate(90deg); 365 | transform: rotate(90deg); 366 | -webkit-appearance: none; 367 | appearance: none; 368 | } 369 | } 370 | 371 | @media (max-width: 430px) { 372 | #footer { 373 | height: 50px; 374 | } 375 | 376 | #filters { 377 | bottom: 10px; 378 | } 379 | } 380 | -------------------------------------------------------------------------------- /src/main/resources/static/todo/css/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manekinekko/angular2-springboot-engine/b1bdce0c362873af633a34d954d6673e980da28b/src/main/resources/static/todo/css/bg.png -------------------------------------------------------------------------------- /src/main/resources/static/todo/css/main.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | body { 8 | font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; 9 | line-height: 1.4em; 10 | background: #eaeaea; 11 | color: #4d4d4d; 12 | width: 550px; 13 | margin: 0 auto; 14 | -webkit-font-smoothing: antialiased; 15 | -moz-font-smoothing: antialiased; 16 | -ms-font-smoothing: antialiased; 17 | -o-font-smoothing: antialiased; 18 | font-smoothing: antialiased; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/resources/static/todo/index.ng2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Todo Angular 2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | Loading... 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/main/resources/static/todo/services/TodoStore.js: -------------------------------------------------------------------------------- 1 | var __extends = (this && this.__extends) || function (d, b) { 2 | for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; 3 | function __() { this.constructor = d; } 4 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 5 | }; 6 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 7 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 8 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 9 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 10 | return c > 3 && r && Object.defineProperty(target, key, r), r; 11 | }; 12 | var core_d_1 = require('../../node_modules/angular2/core.d'); 13 | // base model for RecordStore 14 | var KeyModel = (function () { 15 | function KeyModel(key) { 16 | this.key = key; 17 | } 18 | return KeyModel; 19 | })(); 20 | exports.KeyModel = KeyModel; 21 | var Todo = (function (_super) { 22 | __extends(Todo, _super); 23 | function Todo(key, title, completed) { 24 | _super.call(this, key); 25 | this.title = title; 26 | this.completed = completed; 27 | } 28 | return Todo; 29 | })(KeyModel); 30 | exports.Todo = Todo; 31 | var TodoFactory = (function () { 32 | function TodoFactory() { 33 | this._uid = 0; 34 | } 35 | TodoFactory.prototype.nextUid = function () { return ++this._uid; }; 36 | TodoFactory.prototype.create = function (title, isCompleted) { 37 | return new Todo(this.nextUid(), title, isCompleted); 38 | }; 39 | TodoFactory = __decorate([ 40 | core_d_1.Injectable() 41 | ], TodoFactory); 42 | return TodoFactory; 43 | })(); 44 | exports.TodoFactory = TodoFactory; 45 | // store manages any generic item that inherits from KeyModel 46 | var Store = (function () { 47 | function Store() { 48 | this.list = []; 49 | } 50 | Store.prototype.add = function (record) { this.list.push(record); }; 51 | Store.prototype.remove = function (record) { this._spliceOut(record); }; 52 | Store.prototype.removeBy = function (callback) { 53 | var records = this.list.filter(callback); 54 | for (var i = 0; i < records.length; ++i) { 55 | var index = this.list.indexOf(records[i]); 56 | this.list.splice(index, 1); 57 | } 58 | }; 59 | Store.prototype._spliceOut = function (record) { 60 | var i = this._indexFor(record); 61 | if (i > -1) { 62 | return this.list.splice(i, 1)[0]; 63 | } 64 | return null; 65 | }; 66 | Store.prototype._indexFor = function (record) { return this.list.indexOf(record); }; 67 | Store = __decorate([ 68 | core_d_1.Injectable() 69 | ], Store); 70 | return Store; 71 | })(); 72 | exports.Store = Store; 73 | //# sourceMappingURL=TodoStore.js.map -------------------------------------------------------------------------------- /src/main/resources/static/todo/services/TodoStore.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"TodoStore.js","sourceRoot":"","sources":["TodoStore.ts"],"names":["KeyModel","KeyModel.constructor","Todo","Todo.constructor","TodoFactory","TodoFactory.constructor","TodoFactory.nextUid","TodoFactory.create","Store","Store.constructor","Store.add","Store.remove","Store.removeBy","Store._spliceOut","Store._indexFor"],"mappings":"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"} -------------------------------------------------------------------------------- /src/main/resources/static/todo/services/TodoStore.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '../../node_modules/angular2/core.d'; 2 | 3 | // base model for RecordStore 4 | export class KeyModel { 5 | constructor(public key: number) {} 6 | } 7 | 8 | export class Todo extends KeyModel { 9 | constructor(key: number, public title: string, public completed: boolean) { super(key); } 10 | } 11 | 12 | @Injectable() 13 | export class TodoFactory { 14 | _uid: number = 0; 15 | 16 | nextUid(): number { return ++this._uid; } 17 | 18 | create(title: string, isCompleted: boolean): Todo { 19 | return new Todo(this.nextUid(), title, isCompleted); 20 | } 21 | } 22 | 23 | // store manages any generic item that inherits from KeyModel 24 | @Injectable() 25 | export class Store { 26 | list: Array = []; 27 | 28 | constructor() { 29 | 30 | } 31 | 32 | add(record: KeyModel): void { this.list.push(record); } 33 | 34 | remove(record: KeyModel): void { this._spliceOut(record); } 35 | 36 | removeBy(callback: any): void { 37 | var records = this.list.filter(callback); 38 | 39 | for (let i = 0; i < records.length; ++i) { 40 | let index = this.list.indexOf(records[i]); 41 | this.list.splice(index, 1); 42 | } 43 | } 44 | 45 | private _spliceOut(record: KeyModel) { 46 | var i = this._indexFor(record); 47 | if (i > -1) { 48 | return this.list.splice(i, 1)[0]; 49 | } 50 | return null; 51 | } 52 | 53 | private _indexFor(record: KeyModel) { return this.list.indexOf(record); } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/resources/static/todo/todo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 15 | 16 |
17 | 18 | 19 | 20 |
    21 | 22 |
  • 23 | 24 |
    26 | 27 | 30 | 31 | 32 | 33 | 34 |
    35 | 36 |
    37 | 38 | 42 | 43 |
    44 | 45 |
  • 46 |
47 | 48 |
49 | 50 |
51 | 52 |
53 | 64 | 65 |
66 | 67 |
68 | 69 | 73 | -------------------------------------------------------------------------------- /src/test/java/io/angular/universal/ApplicationTests.java: -------------------------------------------------------------------------------- 1 | package io.angular.universal; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.test.context.web.WebAppConfiguration; 6 | import org.springframework.boot.test.SpringApplicationConfiguration; 7 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 8 | 9 | @RunWith(SpringJUnit4ClassRunner.class) 10 | @SpringApplicationConfiguration(classes = UniversalApplication.class) 11 | @WebAppConfiguration 12 | public class ApplicationTests { 13 | 14 | @Test 15 | public void contextLoads() { 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/io/angular/universal/UniversalTest.java: -------------------------------------------------------------------------------- 1 | package io.angular.universal; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import static org.hamcrest.Matchers.is; 9 | import static org.hamcrest.Matchers.startsWith; 10 | import static org.junit.Assert.assertThat; 11 | 12 | public class UniversalTest { 13 | 14 | @Test 15 | public void testRenderCommentBox() throws Exception { 16 | 17 | Universal universal = new Universal(); 18 | String html = universal.render(); 19 | 20 | assertThat(html, startsWith(":)")); 21 | 22 | //Document doc = Jsoup.parse(html); 23 | //assertThat(doc.select("div.comment").size(), is(2)); 24 | } 25 | 26 | } --------------------------------------------------------------------------------