From abfffced87cb13554ed07ae69f5898f697d317f7 Mon Sep 17 00:00:00 2001 From: Isammoc OFF Date: Wed, 16 Nov 2016 21:16:24 +0100 Subject: [PATCH 01/22] feat: Change sample by Tour of Heroes --- app/assets/app/app-routing.module.ts | 26 ++ app/assets/app/app.component.css | 35 ++ app/assets/app/app.component.ts | 25 ++ app/assets/app/app.html | 23 -- app/assets/app/app.module.ts | 57 +++- app/assets/app/app.ts | 58 ---- app/assets/app/dashboard.component.css | 68 ++++ app/assets/app/dashboard.component.html | 16 + app/assets/app/dashboard.component.ts | 28 ++ app/assets/app/hero-detail.component.css | 36 +++ app/assets/app/hero-detail.component.html | 18 ++ app/assets/app/hero-detail.component.ts | 45 +++ app/assets/app/hero-search.component.css | 22 ++ app/assets/app/hero-search.component.html | 17 + app/assets/app/hero-search.component.ts | 56 ++++ app/assets/app/hero-search.service.ts | 24 ++ app/assets/app/hero.service.ts | 65 ++++ app/assets/app/hero.ts | 11 + app/assets/app/heroes.component.css | 73 +++++ app/assets/app/heroes.component.html | 29 ++ app/assets/app/heroes.component.ts | 64 ++++ app/assets/app/in-memory-data.service.ts | 25 ++ app/assets/app/main.ts | 7 +- app/assets/app/rxjs-extensions.ts | 19 ++ app/assets/app/services/store.ts | 67 ---- app/assets/app/services/todo.store.ts | 20 -- app/views/index.scala.html | 17 +- app/views/index1.scala.html | 17 +- app/views/index2.scala.html | 17 +- build.sbt | 4 +- public/stylesheets/styles.css | 148 +++++++++ public/stylesheets/todomvc-app.css | 374 ---------------------- test/assets/todo.spec.ts | 16 +- tslint.json | 6 +- 34 files changed, 922 insertions(+), 611 deletions(-) create mode 100644 app/assets/app/app-routing.module.ts create mode 100644 app/assets/app/app.component.css create mode 100644 app/assets/app/app.component.ts delete mode 100644 app/assets/app/app.html delete mode 100644 app/assets/app/app.ts create mode 100644 app/assets/app/dashboard.component.css create mode 100644 app/assets/app/dashboard.component.html create mode 100644 app/assets/app/dashboard.component.ts create mode 100644 app/assets/app/hero-detail.component.css create mode 100644 app/assets/app/hero-detail.component.html create mode 100644 app/assets/app/hero-detail.component.ts create mode 100644 app/assets/app/hero-search.component.css create mode 100644 app/assets/app/hero-search.component.html create mode 100644 app/assets/app/hero-search.component.ts create mode 100644 app/assets/app/hero-search.service.ts create mode 100644 app/assets/app/hero.service.ts create mode 100644 app/assets/app/hero.ts create mode 100644 app/assets/app/heroes.component.css create mode 100644 app/assets/app/heroes.component.html create mode 100644 app/assets/app/heroes.component.ts create mode 100644 app/assets/app/in-memory-data.service.ts create mode 100644 app/assets/app/rxjs-extensions.ts delete mode 100644 app/assets/app/services/store.ts delete mode 100644 app/assets/app/services/todo.store.ts create mode 100644 public/stylesheets/styles.css delete mode 100644 public/stylesheets/todomvc-app.css diff --git a/app/assets/app/app-routing.module.ts b/app/assets/app/app-routing.module.ts new file mode 100644 index 0000000..37d39e0 --- /dev/null +++ b/app/assets/app/app-routing.module.ts @@ -0,0 +1,26 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +import { DashboardComponent } from './dashboard.component'; +import { HeroesComponent } from './heroes.component'; +import { HeroDetailComponent } from './hero-detail.component'; + +const routes: Routes = [ + { path: '', redirectTo: '/dashboard', pathMatch: 'full' }, + { path: 'dashboard', component: DashboardComponent }, + { path: 'detail/:id', component: HeroDetailComponent }, + { path: 'heroes', component: HeroesComponent } +]; + +@NgModule({ + imports: [ RouterModule.forRoot(routes) ], + exports: [ RouterModule ] +}) +export class AppRoutingModule {} + + +/* +Copyright 2016 Google Inc. All Rights Reserved. +Use of this source code is governed by an MIT-style license that +can be found in the LICENSE file at http://angular.io/license +*/ \ No newline at end of file diff --git a/app/assets/app/app.component.css b/app/assets/app/app.component.css new file mode 100644 index 0000000..6c046fa --- /dev/null +++ b/app/assets/app/app.component.css @@ -0,0 +1,35 @@ +h1 { + font-size: 1.2em; + color: #999; + margin-bottom: 0; +} +h2 { + font-size: 2em; + margin-top: 0; + padding-top: 0; +} +nav a { + padding: 5px 10px; + text-decoration: none; + margin-top: 10px; + display: inline-block; + background-color: #eee; + border-radius: 4px; +} +nav a:visited, a:link { + color: #607D8B; +} +nav a:hover { + color: #039be5; + background-color: #CFD8DC; +} +nav a.active { + color: #039be5; +} + + +/* +Copyright 2016 Google Inc. All Rights Reserved. +Use of this source code is governed by an MIT-style license that +can be found in the LICENSE file at http://angular.io/license +*/ \ No newline at end of file diff --git a/app/assets/app/app.component.ts b/app/assets/app/app.component.ts new file mode 100644 index 0000000..e5e17c5 --- /dev/null +++ b/app/assets/app/app.component.ts @@ -0,0 +1,25 @@ +import { Component } from '@angular/core'; + +@Component({ +// moduleId: module.id, + selector: 'my-app', + template: ` +

{{title}}

+ + + `, + styleUrls: ['assets/app/app.component.css'] +}) +export class AppComponent { + title = 'Tour of Heroes'; +} + + +/* +Copyright 2016 Google Inc. All Rights Reserved. +Use of this source code is governed by an MIT-style license that +can be found in the LICENSE file at http://angular.io/license +*/ diff --git a/app/assets/app/app.html b/app/assets/app/app.html deleted file mode 100644 index e995d84..0000000 --- a/app/assets/app/app.html +++ /dev/null @@ -1,23 +0,0 @@ -
-
-

todos

- -
-
- -
    -
  • -
    - - - -
    - -
  • -
-
-
- {{todoStore.getRemaining().length}} {{todoStore.getRemaining().length == 1 ? 'item' : 'items'}} left - -
-
diff --git a/app/assets/app/app.module.ts b/app/assets/app/app.module.ts index 3cc0f5c..af25792 100644 --- a/app/assets/app/app.module.ts +++ b/app/assets/app/app.module.ts @@ -1,21 +1,46 @@ -import { NgModule } from "@angular/core" -import { BrowserModule } from "@angular/platform-browser" -import { FormsModule } from "@angular/forms" +import './rxjs-extensions'; -import TodoAppComponent from "./app" -import { LocalStorageTodoStore } from "./services/store" -import { TodoStore } from "./services/todo.store" +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule } from '@angular/forms'; +import { HttpModule } from '@angular/http'; + +import { AppRoutingModule } from './app-routing.module'; + +// Imports for loading & configuring the in-memory web api +import { InMemoryWebApiModule } from 'angular-in-memory-web-api'; +import { InMemoryDataService } from './in-memory-data.service'; + +import { AppComponent } from './app.component'; +import { DashboardComponent } from './dashboard.component'; +import { HeroesComponent } from './heroes.component'; +import { HeroDetailComponent } from './hero-detail.component'; +import { HeroService } from './hero.service'; +import { HeroSearchComponent } from './hero-search.component'; @NgModule({ - imports: [ - BrowserModule - , FormsModule - ], - declarations: [TodoAppComponent], - bootstrap: [TodoAppComponent], - providers: [ - {provide: TodoStore, useValue: new LocalStorageTodoStore()} - ] + imports: [ + BrowserModule, + FormsModule, + HttpModule, + InMemoryWebApiModule.forRoot(InMemoryDataService), + AppRoutingModule + ], + declarations: [ + AppComponent, + DashboardComponent, + HeroDetailComponent, + HeroesComponent, + HeroSearchComponent + ], + providers: [ HeroService ], + bootstrap: [ AppComponent ] }) -export class TodoAppModule { } +export class AppModule { } + +/* +Copyright 2016 Google Inc. All Rights Reserved. +Use of this source code is governed by an MIT-style license that +can be found in the LICENSE file at http://angular.io/license +*/ \ No newline at end of file diff --git a/app/assets/app/app.ts b/app/assets/app/app.ts deleted file mode 100644 index c46f9a0..0000000 --- a/app/assets/app/app.ts +++ /dev/null @@ -1,58 +0,0 @@ -import {Component} from "@angular/core" -import {Todo} from "./services/store" -import {TodoStore} from "./services/todo.store" - -@Component({ - selector: "todo-app", - templateUrl: "assets/app/app.html" -}) -export default class TodoAppComponent { - newTodoText = "" - - constructor(private todoStore:TodoStore) { - this.todoStore = todoStore - } - - stopEditing(todo:Todo, editedTitle:string) { - todo.title = editedTitle - todo.editing = false - } - - cancelEditingTodo(todo:Todo) { - todo.editing = false - } - - updateEditingTodo(todo:Todo, editedTitle:string) { - editedTitle = editedTitle.trim() - todo.editing = false - - if (editedTitle.length === 0) { - return this.todoStore.remove(todo) - } - - todo.title = editedTitle - } - - editTodo(todo:Todo) { - todo.editing = true - } - - removeCompleted() { - this.todoStore.removeCompleted() - } - - toggleCompletion(todo:Todo) { - this.todoStore.toggleCompletion(todo) - } - - remove(todo:Todo) { - this.todoStore.remove(todo) - } - - addTodo() { - if (this.newTodoText.trim().length) { - this.todoStore.add(this.newTodoText) - this.newTodoText = "" - } - } -} diff --git a/app/assets/app/dashboard.component.css b/app/assets/app/dashboard.component.css new file mode 100644 index 0000000..9b3226a --- /dev/null +++ b/app/assets/app/dashboard.component.css @@ -0,0 +1,68 @@ +[class*='col-'] { + float: left; + padding-right: 20px; + padding-bottom: 20px; +} +[class*='col-']:last-of-type { + padding-right: 0; +} +a { + text-decoration: none; +} +*, *:after, *:before { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +h3 { + text-align: center; margin-bottom: 0; +} +h4 { + position: relative; +} +.grid { + margin: 0; +} +.col-1-4 { + width: 25%; +} +.module { + padding: 20px; + text-align: center; + color: #eee; + max-height: 120px; + min-width: 120px; + background-color: #607D8B; + border-radius: 2px; +} +.module:hover { + background-color: #EEE; + cursor: pointer; + color: #607d8b; +} +.grid-pad { + padding: 10px 0; +} +.grid-pad > [class*='col-']:last-of-type { + padding-right: 20px; +} +@media (max-width: 600px) { + .module { + font-size: 10px; + max-height: 75px; } +} +@media (max-width: 1024px) { + .grid { + margin: 0; + } + .module { + min-width: 60px; + } +} + + +/* +Copyright 2016 Google Inc. All Rights Reserved. +Use of this source code is governed by an MIT-style license that +can be found in the LICENSE file at http://angular.io/license +*/ \ No newline at end of file diff --git a/app/assets/app/dashboard.component.html b/app/assets/app/dashboard.component.html new file mode 100644 index 0000000..2d9fb5b --- /dev/null +++ b/app/assets/app/dashboard.component.html @@ -0,0 +1,16 @@ +

Top Heroes

+
+ +
+

{{hero.name}}

+
+
+
+ + + + \ No newline at end of file diff --git a/app/assets/app/dashboard.component.ts b/app/assets/app/dashboard.component.ts new file mode 100644 index 0000000..feb655f --- /dev/null +++ b/app/assets/app/dashboard.component.ts @@ -0,0 +1,28 @@ +import { Component, OnInit } from '@angular/core'; + +import { Hero } from './hero'; +import { HeroService } from './hero.service'; + +@Component({ +// moduleId: module.id, + selector: 'my-dashboard', + templateUrl: 'assets/app/dashboard.component.html', + styleUrls: [ 'assets/app/dashboard.component.css' ] +}) +export class DashboardComponent implements OnInit { + heroes: Hero[] = []; + + constructor(private heroService: HeroService) { } + + ngOnInit(): void { + this.heroService.getHeroes() + .then(heroes => this.heroes = heroes.slice(1, 5)); + } +} + + +/* +Copyright 2016 Google Inc. All Rights Reserved. +Use of this source code is governed by an MIT-style license that +can be found in the LICENSE file at http://angular.io/license +*/ diff --git a/app/assets/app/hero-detail.component.css b/app/assets/app/hero-detail.component.css new file mode 100644 index 0000000..2a0e285 --- /dev/null +++ b/app/assets/app/hero-detail.component.css @@ -0,0 +1,36 @@ +label { + display: inline-block; + width: 3em; + margin: .5em 0; + color: #607D8B; + font-weight: bold; +} +input { + height: 2em; + font-size: 1em; + padding-left: .4em; +} +button { + margin-top: 20px; + font-family: Arial; + background-color: #eee; + border: none; + padding: 5px 10px; + border-radius: 4px; + cursor: pointer; cursor: hand; +} +button:hover { + background-color: #cfd8dc; +} +button:disabled { + background-color: #eee; + color: #ccc; + cursor: auto; +} + + +/* +Copyright 2016 Google Inc. All Rights Reserved. +Use of this source code is governed by an MIT-style license that +can be found in the LICENSE file at http://angular.io/license +*/ \ No newline at end of file diff --git a/app/assets/app/hero-detail.component.html b/app/assets/app/hero-detail.component.html new file mode 100644 index 0000000..b700346 --- /dev/null +++ b/app/assets/app/hero-detail.component.html @@ -0,0 +1,18 @@ +
+

{{hero.name}} details!

+
+ {{hero.id}}
+
+ + +
+ + +
+ + + \ No newline at end of file diff --git a/app/assets/app/hero-detail.component.ts b/app/assets/app/hero-detail.component.ts new file mode 100644 index 0000000..1b99567 --- /dev/null +++ b/app/assets/app/hero-detail.component.ts @@ -0,0 +1,45 @@ +import 'rxjs/add/operator/switchMap'; +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Params } from '@angular/router'; +import { Location } from '@angular/common'; + +import { Hero } from './hero'; +import { HeroService } from './hero.service'; + +@Component({ +// moduleId: module.id, + selector: 'my-hero-detail', + templateUrl: 'assets/app/hero-detail.component.html', + styleUrls: [ 'assets/app/hero-detail.component.css' ] +}) +export class HeroDetailComponent implements OnInit { + hero: Hero; + + constructor( + private heroService: HeroService, + private route: ActivatedRoute, + private location: Location + ) {} + + ngOnInit(): void { + this.route.params + .switchMap((params: Params) => this.heroService.getHero(+params['id'])) + .subscribe(hero => this.hero = hero); + } + + save(): void { + this.heroService.update(this.hero) + .then(() => this.goBack()); + } + + goBack(): void { + this.location.back(); + } +} + + +/* +Copyright 2016 Google Inc. All Rights Reserved. +Use of this source code is governed by an MIT-style license that +can be found in the LICENSE file at http://angular.io/license +*/ diff --git a/app/assets/app/hero-search.component.css b/app/assets/app/hero-search.component.css new file mode 100644 index 0000000..462d233 --- /dev/null +++ b/app/assets/app/hero-search.component.css @@ -0,0 +1,22 @@ +.search-result{ + border-bottom: 1px solid gray; + border-left: 1px solid gray; + border-right: 1px solid gray; + width:195px; + height: 20px; + padding: 5px; + background-color: white; + cursor: pointer; +} + +#search-box{ + width: 200px; + height: 20px; +} + + +/* +Copyright 2016 Google Inc. All Rights Reserved. +Use of this source code is governed by an MIT-style license that +can be found in the LICENSE file at http://angular.io/license +*/ \ No newline at end of file diff --git a/app/assets/app/hero-search.component.html b/app/assets/app/hero-search.component.html new file mode 100644 index 0000000..64c6ceb --- /dev/null +++ b/app/assets/app/hero-search.component.html @@ -0,0 +1,17 @@ +
+

Hero Search

+ +
+
+ {{hero.name}} +
+
+
+ + + \ No newline at end of file diff --git a/app/assets/app/hero-search.component.ts b/app/assets/app/hero-search.component.ts new file mode 100644 index 0000000..4f29661 --- /dev/null +++ b/app/assets/app/hero-search.component.ts @@ -0,0 +1,56 @@ +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { Observable } from 'rxjs/Observable'; +import { Subject } from 'rxjs/Subject'; + +import { HeroSearchService } from './hero-search.service'; +import { Hero } from './hero'; + +@Component({ +// moduleId: module.id, + selector: 'hero-search', + templateUrl: 'assets/app/hero-search.component.html', + styleUrls: [ 'assets/app/hero-search.component.css' ], + providers: [HeroSearchService] +}) +export class HeroSearchComponent implements OnInit { + heroes: Observable; + private searchTerms = new Subject(); + + constructor( + private heroSearchService: HeroSearchService, + private router: Router) {} + + // Push a search term into the observable stream. + search(term: string): void { + this.searchTerms.next(term); + } + + ngOnInit(): void { + this.heroes = this.searchTerms + .debounceTime(300) // wait for 300ms pause in events + .distinctUntilChanged() // ignore if next search term is same as previous + .switchMap(term => term // switch to new observable each time + // return the http search observable + ? this.heroSearchService.search(term) + // or the observable of empty heroes if no search term + : Observable.of([])) + .catch(error => { + // TODO: real error handling + console.log(error); + return Observable.of([]); + }); + } + + gotoDetail(hero: Hero): void { + let link = ['/detail', hero.id]; + this.router.navigate(link); + } +} + + +/* +Copyright 2016 Google Inc. All Rights Reserved. +Use of this source code is governed by an MIT-style license that +can be found in the LICENSE file at http://angular.io/license +*/ diff --git a/app/assets/app/hero-search.service.ts b/app/assets/app/hero-search.service.ts new file mode 100644 index 0000000..ab3fcac --- /dev/null +++ b/app/assets/app/hero-search.service.ts @@ -0,0 +1,24 @@ +import { Injectable } from '@angular/core'; +import { Http, Response } from '@angular/http'; +import { Observable } from 'rxjs'; + +import { Hero } from './hero'; + +@Injectable() +export class HeroSearchService { + + constructor(private http: Http) {} + + search(term: string): Observable { + return this.http + .get(`app/heroes/?name=${term}`) + .map((r: Response) => r.json().data as Hero[]); + } +} + + +/* +Copyright 2016 Google Inc. All Rights Reserved. +Use of this source code is governed by an MIT-style license that +can be found in the LICENSE file at http://angular.io/license +*/ \ No newline at end of file diff --git a/app/assets/app/hero.service.ts b/app/assets/app/hero.service.ts new file mode 100644 index 0000000..dcfce57 --- /dev/null +++ b/app/assets/app/hero.service.ts @@ -0,0 +1,65 @@ +import { Injectable } from '@angular/core'; +import { Headers, Http } from '@angular/http'; + +import 'rxjs/add/operator/toPromise'; + +import { Hero } from './hero'; + +@Injectable() +export class HeroService { + + private headers = new Headers({'Content-Type': 'application/json'}); + private heroesUrl = 'app/heroes'; // URL to web api + + constructor(private http: Http) { } + + getHeroes(): Promise { + return this.http.get(this.heroesUrl) + .toPromise() + .then(response => response.json().data as Hero[]) + .catch(this.handleError); + } + + getHero(id: number): Promise { + return this.getHeroes() + .then(heroes => heroes.find(hero => hero.id === id)); + } + + delete(id: number): Promise { + const url = `${this.heroesUrl}/${id}`; + return this.http.delete(url, {headers: this.headers}) + .toPromise() + .then(() => null) + .catch(this.handleError); + } + + create(name: string): Promise { + return this.http + .post(this.heroesUrl, JSON.stringify({name: name}), {headers: this.headers}) + .toPromise() + .then(res => res.json().data) + .catch(this.handleError); + } + + update(hero: Hero): Promise { + const url = `${this.heroesUrl}/${hero.id}`; + return this.http + .put(url, JSON.stringify(hero), {headers: this.headers}) + .toPromise() + .then(() => hero) + .catch(this.handleError); + } + + private handleError(error: any): Promise { + console.error('An error occurred', error); // for demo purposes only + return Promise.reject(error.message || error); + } +} + + + +/* +Copyright 2016 Google Inc. All Rights Reserved. +Use of this source code is governed by an MIT-style license that +can be found in the LICENSE file at http://angular.io/license +*/ \ No newline at end of file diff --git a/app/assets/app/hero.ts b/app/assets/app/hero.ts new file mode 100644 index 0000000..a399c37 --- /dev/null +++ b/app/assets/app/hero.ts @@ -0,0 +1,11 @@ +export class Hero { + id: number; + name: string; +} + + +/* +Copyright 2016 Google Inc. All Rights Reserved. +Use of this source code is governed by an MIT-style license that +can be found in the LICENSE file at http://angular.io/license +*/ \ No newline at end of file diff --git a/app/assets/app/heroes.component.css b/app/assets/app/heroes.component.css new file mode 100644 index 0000000..a1aba0e --- /dev/null +++ b/app/assets/app/heroes.component.css @@ -0,0 +1,73 @@ +.selected { + background-color: #CFD8DC !important; + color: white; +} +.heroes { + margin: 0 0 2em 0; + list-style-type: none; + padding: 0; + width: 15em; +} +.heroes li { + cursor: pointer; + position: relative; + left: 0; + background-color: #EEE; + margin: .5em; + padding: .3em 0; + height: 1.6em; + border-radius: 4px; +} +.heroes li:hover { + color: #607D8B; + background-color: #DDD; + left: .1em; +} +.heroes li.selected:hover { + background-color: #BBD8DC !important; + color: white; +} +.heroes .text { + position: relative; + top: -3px; +} +.heroes .badge { + display: inline-block; + font-size: small; + color: white; + padding: 0.8em 0.7em 0 0.7em; + background-color: #607D8B; + line-height: 1em; + position: relative; + left: -1px; + top: -4px; + height: 1.8em; + margin-right: .8em; + border-radius: 4px 0 0 4px; +} +button { + font-family: Arial; + background-color: #eee; + border: none; + padding: 5px 10px; + border-radius: 4px; + cursor: pointer; + cursor: hand; +} +button:hover { + background-color: #cfd8dc; +} +button.delete { + float:right; + margin-top: 2px; + margin-right: .8em; + background-color: gray !important; + color:white; +} + + +/* +Copyright 2016 Google Inc. All Rights Reserved. +Use of this source code is governed by an MIT-style license that +can be found in the LICENSE file at http://angular.io/license +*/ \ No newline at end of file diff --git a/app/assets/app/heroes.component.html b/app/assets/app/heroes.component.html new file mode 100644 index 0000000..c90ffb4 --- /dev/null +++ b/app/assets/app/heroes.component.html @@ -0,0 +1,29 @@ +

My Heroes

+
+ + +
+
    +
  • + {{hero.id}} + {{hero.name}} + +
  • +
+
+

+ {{selectedHero.name | uppercase}} is my hero +

+ +
+ + + \ No newline at end of file diff --git a/app/assets/app/heroes.component.ts b/app/assets/app/heroes.component.ts new file mode 100644 index 0000000..36c0f2b --- /dev/null +++ b/app/assets/app/heroes.component.ts @@ -0,0 +1,64 @@ +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; + +import { Hero } from './hero'; +import { HeroService } from './hero.service'; + +@Component({ +// moduleId: module.id, + selector: 'my-heroes', + templateUrl: 'assets/app/heroes.component.html', + styleUrls: [ 'assets/app/heroes.component.css' ] +}) +export class HeroesComponent implements OnInit { + heroes: Hero[]; + selectedHero: Hero; + + constructor( + private heroService: HeroService, + private router: Router) { } + + getHeroes(): void { + this.heroService + .getHeroes() + .then(heroes => this.heroes = heroes); + } + + add(name: string): void { + name = name.trim(); + if (!name) { return; } + this.heroService.create(name) + .then(hero => { + this.heroes.push(hero); + this.selectedHero = null; + }); + } + + delete(hero: Hero): void { + this.heroService + .delete(hero.id) + .then(() => { + this.heroes = this.heroes.filter(h => h !== hero); + if (this.selectedHero === hero) { this.selectedHero = null; } + }); + } + + ngOnInit(): void { + this.getHeroes(); + } + + onSelect(hero: Hero): void { + this.selectedHero = hero; + } + + gotoDetail(): void { + this.router.navigate(['/detail', this.selectedHero.id]); + } +} + + +/* +Copyright 2016 Google Inc. All Rights Reserved. +Use of this source code is governed by an MIT-style license that +can be found in the LICENSE file at http://angular.io/license +*/ diff --git a/app/assets/app/in-memory-data.service.ts b/app/assets/app/in-memory-data.service.ts new file mode 100644 index 0000000..31c8e46 --- /dev/null +++ b/app/assets/app/in-memory-data.service.ts @@ -0,0 +1,25 @@ +import { InMemoryDbService } from 'angular-in-memory-web-api'; +export class InMemoryDataService implements InMemoryDbService { + createDb() { + let heroes = [ + {id: 11, name: 'Mr. Nice'}, + {id: 12, name: 'Narco'}, + {id: 13, name: 'Bombasto'}, + {id: 14, name: 'Celeritas'}, + {id: 15, name: 'Magneta'}, + {id: 16, name: 'RubberMan'}, + {id: 17, name: 'Dynama'}, + {id: 18, name: 'Dr IQ'}, + {id: 19, name: 'Magma'}, + {id: 20, name: 'Tornado'} + ]; + return {heroes}; + } +} + + +/* +Copyright 2016 Google Inc. All Rights Reserved. +Use of this source code is governed by an MIT-style license that +can be found in the LICENSE file at http://angular.io/license +*/ \ No newline at end of file diff --git a/app/assets/app/main.ts b/app/assets/app/main.ts index a6df449..1347c80 100644 --- a/app/assets/app/main.ts +++ b/app/assets/app/main.ts @@ -1,4 +1,5 @@ -import { platformBrowserDynamic } from "@angular/platform-browser-dynamic" -import { TodoAppModule } from "./app.module" +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; -platformBrowserDynamic().bootstrapModule(TodoAppModule) +const platform = platformBrowserDynamic(); +platform.bootstrapModule(AppModule); diff --git a/app/assets/app/rxjs-extensions.ts b/app/assets/app/rxjs-extensions.ts new file mode 100644 index 0000000..3aa9030 --- /dev/null +++ b/app/assets/app/rxjs-extensions.ts @@ -0,0 +1,19 @@ +// Observable class extensions +import 'rxjs/add/observable/of'; +import 'rxjs/add/observable/throw'; + +// Observable operators +import 'rxjs/add/operator/catch'; +import 'rxjs/add/operator/debounceTime'; +import 'rxjs/add/operator/distinctUntilChanged'; +import 'rxjs/add/operator/do'; +import 'rxjs/add/operator/filter'; +import 'rxjs/add/operator/map'; +import 'rxjs/add/operator/switchMap'; + + +/* +Copyright 2016 Google Inc. All Rights Reserved. +Use of this source code is governed by an MIT-style license that +can be found in the LICENSE file at http://angular.io/license +*/ \ No newline at end of file diff --git a/app/assets/app/services/store.ts b/app/assets/app/services/store.ts deleted file mode 100644 index 928ae9c..0000000 --- a/app/assets/app/services/store.ts +++ /dev/null @@ -1,67 +0,0 @@ -import {Injectable} from "@angular/core" -import {TodoStore} from "./todo.store" - -export interface Todo { - completed: Boolean - editing: Boolean - - title: String -} -@Injectable() -export class LocalStorageTodoStore implements TodoStore { - todos:Array - - constructor() { - this.todos = JSON.parse(localStorage.getItem("angular2-todos") || "[]") - } - - private updateStore() { - localStorage.setItem("angular2-todos", JSON.stringify(this.todos)) - } - - private getWithCompleted(completed:Boolean) { - return this.todos.filter((todo:Todo) => todo.completed === completed) - } - - allCompleted() { - return this.todos.length === this.getCompleted().length - } - - setAllTo(completed:Boolean) { - this.todos.forEach((t:Todo) => t.completed = completed) - this.updateStore() - } - - removeCompleted() { - this.todos = this.getWithCompleted(false) - this.updateStore() - } - - getRemaining() { - return this.getWithCompleted(false) - } - - getCompleted() { - return this.getWithCompleted(true) - } - - toggleCompletion(todo:Todo) { - todo.completed = !todo.completed - this.updateStore() - } - - remove(todo:Todo) { - this.todos.splice(this.todos.indexOf(todo), 1) - this.updateStore() - } - - add(title:String) { - const todo = { - completed: false, - editing: false, - title: title - } - this.todos.push(todo) - this.updateStore() - } -} diff --git a/app/assets/app/services/todo.store.ts b/app/assets/app/services/todo.store.ts deleted file mode 100644 index 999c74b..0000000 --- a/app/assets/app/services/todo.store.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {Todo} from "./store" - -export abstract class TodoStore { - - abstract allCompleted():boolean - - abstract setAllTo(completed:Boolean):void - - abstract removeCompleted():void - - abstract getRemaining():Array - - abstract getCompleted():Array - - abstract toggleCompletion(todo:Todo):void - - abstract remove(todo:Todo):void - - abstract add(title:String):void -} diff --git a/app/views/index.scala.html b/app/views/index.scala.html index 4c69a3b..b49c077 100644 --- a/app/views/index.scala.html +++ b/app/views/index.scala.html @@ -3,10 +3,10 @@ @* in this version of the application the typescript compilation is done in the browser *@ + - Angular2 • TodoMVC - - + Angular Tour of Heroes + @@ -15,16 +15,7 @@ - - + Loading... @* In this version of the application the typescript compilation is done by the play framework. The browser downloads .js files. *@ - Angular2 • TodoMVC + Angular Tour of Heroes - + @@ -19,6 +20,7 @@ var map = { 'app': 'assets/app', '@@angular':'assets/lib/@@angular', + 'angular-in-memory-web-api':'assets/lib/angular-in-memory-web-api/bundles/in-memory-web-api.umd.js', 'rxjs':'assets/lib/rxjs', 'symbol-observable': 'assets/lib/symbol-observable' @@ -36,6 +38,7 @@ 'compiler', 'core', 'forms', + 'router', 'http', 'platform-browser', 'platform-browser-dynamic', @@ -73,14 +76,6 @@ - - + Loading... diff --git a/app/views/index2.scala.html b/app/views/index2.scala.html index d1562cc..5a9a73f 100644 --- a/app/views/index2.scala.html +++ b/app/views/index2.scala.html @@ -2,10 +2,10 @@ @* this version of the application loads the application as one file. *@ + - Angular2 • TodoMVC - - + Angular Tour of Heroes + @@ -24,15 +24,6 @@ - - + Loading... diff --git a/build.sbt b/build.sbt index eb4e705..35c143c 100644 --- a/build.sbt +++ b/build.sbt @@ -15,11 +15,13 @@ libraryDependencies ++= { "org.webjars.npm" % "angular__common" % ngVersion, "org.webjars.npm" % "angular__compiler" % ngVersion, "org.webjars.npm" % "angular__core" % ngVersion, + "org.webjars.npm" % "angular__http" % ngVersion, "org.webjars.npm" % "angular__forms" % ngVersion, + "org.webjars.npm" % "angular__router" % "3.2.0-rc.0", "org.webjars.npm" % "angular__platform-browser-dynamic" % ngVersion, "org.webjars.npm" % "angular__platform-browser" % ngVersion, + "org.webjars.npm" % "angular-in-memory-web-api" % "0.1.13", "org.webjars.npm" % "systemjs" % "0.19.39", - "org.webjars.npm" % "todomvc-common" % "1.0.2", "org.webjars.npm" % "rxjs" % "5.0.0-beta.12", "org.webjars.npm" % "es6-promise" % "3.1.2", "org.webjars.npm" % "es6-shim" % "0.35.1", diff --git a/public/stylesheets/styles.css b/public/stylesheets/styles.css new file mode 100644 index 0000000..38d492e --- /dev/null +++ b/public/stylesheets/styles.css @@ -0,0 +1,148 @@ +/* Master Styles */ +h1 { + color: #369; + font-family: Arial, Helvetica, sans-serif; + font-size: 250%; +} +h2, h3 { + color: #444; + font-family: Arial, Helvetica, sans-serif; + font-weight: lighter; +} +body { + margin: 2em; +} +body, input[text], button { + color: #888; + font-family: Cambria, Georgia; +} +a { + cursor: pointer; + cursor: hand; +} +button { + font-family: Arial; + background-color: #eee; + border: none; + padding: 5px 10px; + border-radius: 4px; + cursor: pointer; + cursor: hand; +} +button:hover { + background-color: #cfd8dc; +} +button:disabled { + background-color: #eee; + color: #aaa; + cursor: auto; +} + +/* Navigation link styles */ +nav a { + padding: 5px 10px; + text-decoration: none; + margin-top: 10px; + display: inline-block; + background-color: #eee; + border-radius: 4px; +} +nav a:visited, a:link { + color: #607D8B; +} +nav a:hover { + color: #039be5; + background-color: #CFD8DC; +} +nav a.active { + color: #039be5; +} + +/* items class */ +.items { + margin: 0 0 2em 0; + list-style-type: none; + padding: 0; + width: 24em; +} +.items li { + cursor: pointer; + position: relative; + left: 0; + background-color: #EEE; + margin: .5em; + padding: .3em 0; + height: 1.6em; + border-radius: 4px; +} +.items li:hover { + color: #607D8B; + background-color: #DDD; + left: .1em; +} +.items li.selected:hover { + background-color: #BBD8DC; + color: white; +} +.items .text { + position: relative; + top: -3px; +} +.items { + margin: 0 0 2em 0; + list-style-type: none; + padding: 0; + width: 24em; +} +.items li { + cursor: pointer; + position: relative; + left: 0; + background-color: #EEE; + margin: .5em; + padding: .3em 0; + height: 1.6em; + border-radius: 4px; +} +.items li:hover { + color: #607D8B; + background-color: #DDD; + left: .1em; +} +.items li.selected { + background-color: #CFD8DC; + color: white; +} + +.items li.selected:hover { + background-color: #BBD8DC; +} +.items .text { + position: relative; + top: -3px; +} +.items .badge { + display: inline-block; + font-size: small; + color: white; + padding: 0.8em 0.7em 0 0.7em; + background-color: #607D8B; + line-height: 1em; + position: relative; + left: -1px; + top: -4px; + height: 1.8em; + margin-right: .8em; + border-radius: 4px 0 0 4px; +} +/* everywhere else */ +* { + font-family: Arial, Helvetica, sans-serif; +} + + +/* +Copyright 2016 Google Inc. All Rights Reserved. +Use of this source code is governed by an MIT-style license that +can be found in the LICENSE file at http://angular.io/license +*/ \ No newline at end of file diff --git a/public/stylesheets/todomvc-app.css b/public/stylesheets/todomvc-app.css deleted file mode 100644 index de37a5b..0000000 --- a/public/stylesheets/todomvc-app.css +++ /dev/null @@ -1,374 +0,0 @@ -html, -body { - margin: 0; - padding: 0; -} - -button { - margin: 0; - padding: 0; - border: 0; - background: none; - font-size: 100%; - vertical-align: baseline; - font-family: inherit; - font-weight: inherit; - color: inherit; - -webkit-appearance: none; - appearance: none; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -body { - font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; - line-height: 1.4em; - background: #f5f5f5; - color: #4d4d4d; - min-width: 230px; - max-width: 550px; - margin: 0 auto; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - font-weight: 300; -} - -button, -input[type="checkbox"] { - outline: none; -} - -.hidden { - display: none; -} - -.todoapp { - background: #fff; - margin: 130px 0 40px 0; - position: relative; - box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), - 0 25px 50px 0 rgba(0, 0, 0, 0.1); -} - -.todoapp input::-webkit-input-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -.todoapp input::-moz-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -.todoapp input::input-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -.todoapp h1 { - position: absolute; - top: -155px; - width: 100%; - font-size: 100px; - font-weight: 100; - text-align: center; - color: rgba(175, 47, 47, 0.15); - -webkit-text-rendering: optimizeLegibility; - -moz-text-rendering: optimizeLegibility; - text-rendering: optimizeLegibility; -} - -.new-todo, -.edit { - position: relative; - margin: 0; - width: 100%; - font-size: 24px; - font-family: inherit; - font-weight: inherit; - line-height: 1.4em; - border: 0; - outline: none; - color: inherit; - padding: 6px; - border: 1px solid #999; - box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); - box-sizing: border-box; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -.new-todo { - padding: 16px 16px 16px 60px; - border: none; - background: rgba(0, 0, 0, 0.003); - box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); -} - -.main { - position: relative; - z-index: 2; - border-top: 1px solid #e6e6e6; -} - -label[for='toggle-all'] { - display: none; -} - -.toggle-all { - position: absolute; - top: -55px; - left: -12px; - width: 60px; - height: 34px; - text-align: center; - border: none; /* Mobile Safari */ -} - -.toggle-all:before { - content: '❯'; - font-size: 22px; - color: #e6e6e6; - padding: 10px 27px 10px 27px; -} - -.toggle-all:checked:before { - color: #737373; -} - -.todo-list { - margin: 0; - padding: 0; - list-style: none; -} - -.todo-list li { - position: relative; - font-size: 24px; - border-bottom: 1px solid #ededed; -} - -.todo-list li:last-child { - border-bottom: none; -} - -.todo-list li.editing { - border-bottom: none; - padding: 0; -} - -.todo-list li.editing .edit { - display: block; - width: 506px; - padding: 13px 17px 12px 17px; - margin: 0 0 0 43px; -} - -.todo-list li.editing .view { - display: none; -} - -.todo-list li .toggle { - text-align: center; - width: 40px; - /* auto, since non-WebKit browsers doesn't support input styling */ - height: auto; - position: absolute; - top: 0; - bottom: 0; - margin: auto 0; - border: none; /* Mobile Safari */ - -webkit-appearance: none; - appearance: none; -} - -.todo-list li .toggle:after { - content: url('data:image/svg+xml;utf8,'); -} - -.todo-list li .toggle:checked:after { - content: url('data:image/svg+xml;utf8,'); -} - -.todo-list li label { - white-space: pre-line; - word-break: break-all; - padding: 15px 60px 15px 15px; - margin-left: 45px; - display: block; - line-height: 1.2; - transition: color 0.4s; -} - -.todo-list li.completed label { - color: #d9d9d9; - text-decoration: line-through; -} - -.todo-list li .destroy { - display: none; - position: absolute; - top: 0; - right: 10px; - bottom: 0; - width: 40px; - height: 40px; - margin: auto 0; - font-size: 30px; - color: #cc9a9a; - margin-bottom: 11px; - transition: color 0.2s ease-out; -} - -.todo-list li .destroy:hover { - color: #af5b5e; -} - -.todo-list li .destroy:after { - content: '×'; -} - -.todo-list li:hover .destroy { - display: block; -} - -.todo-list li .edit { - display: none; -} - -.todo-list li.editing:last-child { - margin-bottom: -1px; -} - -.footer { - color: #777; - padding: 10px 15px; - height: 20px; - text-align: center; - border-top: 1px solid #e6e6e6; -} - -.footer:before { - content: ''; - position: absolute; - right: 0; - bottom: 0; - left: 0; - height: 50px; - overflow: hidden; - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), - 0 8px 0 -3px #f6f6f6, - 0 9px 1px -3px rgba(0, 0, 0, 0.2), - 0 16px 0 -6px #f6f6f6, - 0 17px 2px -6px rgba(0, 0, 0, 0.2); -} - -.todo-count { - float: left; - text-align: left; -} - -.todo-count strong { - font-weight: 300; -} - -.filters { - margin: 0; - padding: 0; - list-style: none; - position: absolute; - right: 0; - left: 0; -} - -.filters li { - display: inline; -} - -.filters li a { - color: inherit; - margin: 3px; - padding: 3px 7px; - text-decoration: none; - border: 1px solid transparent; - border-radius: 3px; -} - -.filters li a.selected, -.filters li a:hover { - border-color: rgba(175, 47, 47, 0.1); -} - -.filters li a.selected { - border-color: rgba(175, 47, 47, 0.2); -} - -.clear-completed, -html .clear-completed:active { - float: right; - position: relative; - line-height: 20px; - text-decoration: none; - cursor: pointer; -} - -.clear-completed:hover { - text-decoration: underline; -} - -.info { - margin: 65px auto 0; - color: #bfbfbf; - font-size: 10px; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - text-align: center; -} - -.info p { - line-height: 1; -} - -.info a { - color: inherit; - text-decoration: none; - font-weight: 400; -} - -.info a:hover { - text-decoration: underline; -} - -/* - Hack to remove background from Mobile Safari. - Can't use it globally since it destroys checkboxes in Firefox -*/ -@media screen and (-webkit-min-device-pixel-ratio:0) { - .toggle-all, - .todo-list li .toggle { - background: none; - } - - .todo-list li .toggle { - height: 40px; - } - - .toggle-all { - -webkit-transform: rotate(90deg); - transform: rotate(90deg); - -webkit-appearance: none; - appearance: none; - } -} - -@media (max-width: 430px) { - .footer { - height: 50px; - } - - .filters { - bottom: 10px; - } -} diff --git a/test/assets/todo.spec.ts b/test/assets/todo.spec.ts index 28e0d34..5526101 100644 --- a/test/assets/todo.spec.ts +++ b/test/assets/todo.spec.ts @@ -4,20 +4,20 @@ import { inject, beforeEachProviders, TestComponentBuilder -} from "@angular/core/testing" -import { Todo } from "./app/services/store" +} from '@angular/core/testing'; +import { Todo } from './app/services/store'; // class MockTodoStorage implements TodoStorage {} // #docregion describeIt -describe("some component", () => { - it("does something", () => { +describe('some component', () => { + it('does something', () => { const todo = { completed: true, editing: true, - title: "bla" - } + title: 'bla' + }; // This is a test. - console.log("running js test "+JSON.stringify(todo)) + console.log('running js test '+JSON.stringify(todo)); }) -}) \ No newline at end of file +}) diff --git a/tslint.json b/tslint.json index 824e204..190273d 100644 --- a/tslint.json +++ b/tslint.json @@ -6,8 +6,6 @@ "component-selector-name": [true, "kebab-case"], "directive-selector-type": [true, "attribute"], "component-selector-type": [true, "element"], - "directive-selector-prefix": [true, "todo"], - "component-selector-prefix": [true, "todo"], "use-input-property-decorator": true, "use-output-property-decorator": true, "use-host-property-decorator": true, @@ -47,11 +45,11 @@ ], "quotemark": [ true, - "double" + "single" ], "semicolon": [ true, - "never" + "always" ], "triple-equals": [ true, From 52bc66d33310224834079835b36d04665dc6a18a Mon Sep 17 00:00:00 2001 From: Isammoc OFF Date: Wed, 16 Nov 2016 23:03:15 +0100 Subject: [PATCH 02/22] feat: Allow refresh without 404 --- app/controllers/Application.scala | 1 + app/views/index.scala.html | 2 +- app/views/index1.scala.html | 2 +- app/views/index2.scala.html | 2 +- conf/routes | 3 +++ 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/controllers/Application.scala b/app/controllers/Application.scala index 3ba9d4f..2c58e05 100644 --- a/app/controllers/Application.scala +++ b/app/controllers/Application.scala @@ -14,4 +14,5 @@ class Application extends Controller { Ok(views.html.index1()) } + def other(others: String) = index } diff --git a/app/views/index.scala.html b/app/views/index.scala.html index b49c077..cf5398d 100644 --- a/app/views/index.scala.html +++ b/app/views/index.scala.html @@ -3,7 +3,7 @@ @* in this version of the application the typescript compilation is done in the browser *@ - + Angular Tour of Heroes diff --git a/app/views/index1.scala.html b/app/views/index1.scala.html index 0a80864..5281547 100644 --- a/app/views/index1.scala.html +++ b/app/views/index1.scala.html @@ -2,7 +2,7 @@ - + @* In this version of the application the typescript compilation is done by the play framework. The browser downloads .js files. *@ diff --git a/app/views/index2.scala.html b/app/views/index2.scala.html index 5a9a73f..641212a 100644 --- a/app/views/index2.scala.html +++ b/app/views/index2.scala.html @@ -2,7 +2,7 @@ @* this version of the application loads the application as one file. *@ - + Angular Tour of Heroes diff --git a/conf/routes b/conf/routes index e71a0bd..7111cd4 100644 --- a/conf/routes +++ b/conf/routes @@ -7,3 +7,6 @@ GET / controllers.Application.index # Map static resources from the /public folder to the /assets URL path GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset) + +# Map 404 to an index bis +GET /*others controllers.Application.other(others) From 1c198f9947c0091c25a7182df05d486bf419a1aa Mon Sep 17 00:00:00 2001 From: Isammoc OFF Date: Fri, 18 Nov 2016 08:51:53 +0100 Subject: [PATCH 03/22] feat: Implements API from play --- app/assets/app/app.module.ts | 7 +--- app/assets/app/hero.service.ts | 4 +-- app/assets/app/in-memory-data.service.ts | 25 -------------- app/controllers/Application.scala | 2 ++ app/controllers/Heroes.scala | 44 ++++++++++++++++++++++++ app/services/HeroesService.scala | 42 ++++++++++++++++++++++ app/views/index1.scala.html | 5 ++- build.sbt | 1 - conf/routes | 7 ++++ test/assets/todo.spec.ts | 4 +-- 10 files changed, 102 insertions(+), 39 deletions(-) delete mode 100644 app/assets/app/in-memory-data.service.ts create mode 100644 app/controllers/Heroes.scala create mode 100644 app/services/HeroesService.scala diff --git a/app/assets/app/app.module.ts b/app/assets/app/app.module.ts index af25792..cfd1e46 100644 --- a/app/assets/app/app.module.ts +++ b/app/assets/app/app.module.ts @@ -7,10 +7,6 @@ import { HttpModule } from '@angular/http'; import { AppRoutingModule } from './app-routing.module'; -// Imports for loading & configuring the in-memory web api -import { InMemoryWebApiModule } from 'angular-in-memory-web-api'; -import { InMemoryDataService } from './in-memory-data.service'; - import { AppComponent } from './app.component'; import { DashboardComponent } from './dashboard.component'; import { HeroesComponent } from './heroes.component'; @@ -23,7 +19,6 @@ import { HeroSearchComponent } from './hero-search.component'; BrowserModule, FormsModule, HttpModule, - InMemoryWebApiModule.forRoot(InMemoryDataService), AppRoutingModule ], declarations: [ @@ -43,4 +38,4 @@ export class AppModule { } Copyright 2016 Google Inc. All Rights Reserved. Use of this source code is governed by an MIT-style license that can be found in the LICENSE file at http://angular.io/license -*/ \ No newline at end of file +*/ diff --git a/app/assets/app/hero.service.ts b/app/assets/app/hero.service.ts index dcfce57..e347710 100644 --- a/app/assets/app/hero.service.ts +++ b/app/assets/app/hero.service.ts @@ -9,7 +9,7 @@ import { Hero } from './hero'; export class HeroService { private headers = new Headers({'Content-Type': 'application/json'}); - private heroesUrl = 'app/heroes'; // URL to web api + private heroesUrl = 'api/heroes'; constructor(private http: Http) { } @@ -62,4 +62,4 @@ export class HeroService { Copyright 2016 Google Inc. All Rights Reserved. Use of this source code is governed by an MIT-style license that can be found in the LICENSE file at http://angular.io/license -*/ \ No newline at end of file +*/ diff --git a/app/assets/app/in-memory-data.service.ts b/app/assets/app/in-memory-data.service.ts deleted file mode 100644 index 31c8e46..0000000 --- a/app/assets/app/in-memory-data.service.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { InMemoryDbService } from 'angular-in-memory-web-api'; -export class InMemoryDataService implements InMemoryDbService { - createDb() { - let heroes = [ - {id: 11, name: 'Mr. Nice'}, - {id: 12, name: 'Narco'}, - {id: 13, name: 'Bombasto'}, - {id: 14, name: 'Celeritas'}, - {id: 15, name: 'Magneta'}, - {id: 16, name: 'RubberMan'}, - {id: 17, name: 'Dynama'}, - {id: 18, name: 'Dr IQ'}, - {id: 19, name: 'Magma'}, - {id: 20, name: 'Tornado'} - ]; - return {heroes}; - } -} - - -/* -Copyright 2016 Google Inc. All Rights Reserved. -Use of this source code is governed by an MIT-style license that -can be found in the LICENSE file at http://angular.io/license -*/ \ No newline at end of file diff --git a/app/controllers/Application.scala b/app/controllers/Application.scala index 2c58e05..2c6a614 100644 --- a/app/controllers/Application.scala +++ b/app/controllers/Application.scala @@ -14,5 +14,7 @@ class Application extends Controller { Ok(views.html.index1()) } + def notFound(notFound: String) = Default.notFound + def other(others: String) = index } diff --git a/app/controllers/Heroes.scala b/app/controllers/Heroes.scala new file mode 100644 index 0000000..166a592 --- /dev/null +++ b/app/controllers/Heroes.scala @@ -0,0 +1,44 @@ +package controllers + +import javax.inject.Inject + +import play.api.libs.json.{Json, Writes} +import play.api.mvc.{Action, BodyParser, Controller} +import services.{Hero, HeroesService} + +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.Future + +class Heroes @Inject() (heroesService: HeroesService) extends Controller { + def all = Action.async { + heroesService.all.map { s => Ok(Json.toJson(Map(("data" , Json.toJson(s)(Writes.list(Json.writes[Hero]))))))} + } + + def delete(id: String) = Action.async { + heroesService.delete(id.toInt).map { _ => Ok("") } + } + + def create = Action.async { implicit request => + request.body + .asJson + .flatMap( json => (json \ "name").asOpt[String]) + .map {name => + heroesService + .create(name) + .map(hero => Json.toJson(hero)(Json.writes[Hero])) + .map(s => Ok(Json.toJson(Map(("data" , s))))) + }.getOrElse(Future.successful(BadRequest(""))) + } + + def update(id: String) = Action.async { implicit request => + request.body + .asJson + .map(_ \ "name") + .flatMap(_.asOpt[String]) + .map{ name => + heroesService.update(id.toInt, name) + .map(hero => Json.toJson(hero)(Json.writes[Hero])) + .map(s => Ok(Json.toJson(Map("data" -> s)))) + }.getOrElse(Future.successful(BadRequest)) + } +} diff --git a/app/services/HeroesService.scala b/app/services/HeroesService.scala new file mode 100644 index 0000000..c1d1305 --- /dev/null +++ b/app/services/HeroesService.scala @@ -0,0 +1,42 @@ +package services + +import javax.inject.Singleton + +import scala.concurrent.Future + +case class Hero(id: Int, name: String) + +@Singleton +class HeroesService { + @volatile private var heroes = Map( + 11 -> Hero(id = 11, name = "Mr. Nice"), + 12 -> Hero(id = 12, name = "Narco"), + 13 -> Hero(id = 13, name = "Bombasto"), + 14 -> Hero(id = 14, name = "Celeritas"), + 15 -> Hero(id = 15, name = "Magneta"), + 16 -> Hero(id = 16, name = "RubberMan"), + 17 -> Hero(id = 17, name = "Dynama"), + 18 -> Hero(id = 18, name = "Dr IQ"), + 19 -> Hero(id = 19, name = "Magma"), + 20 -> Hero(id = 20, name = "Tornado") + ) + + def all: Future[List[Hero]] = Future.successful(heroes.values.toList.sortBy(_.id)) + + def delete(id: Int):Future[Option[Hero]] = { + val answer = heroes.get(id) + heroes = heroes - id + Future.successful(answer) + } + + def create(name: String): Future[Hero] = Future.successful { + val id = heroes.keys.max + 1 + heroes = heroes + (id -> Hero(id, name)) + Hero(id, name) + } + + def update(id: Int, name: String): Future[Hero] = Future.successful { + heroes = heroes + (id -> Hero(id, name)) + Hero(id, name) + } +} diff --git a/app/views/index1.scala.html b/app/views/index1.scala.html index 5281547..2919c43 100644 --- a/app/views/index1.scala.html +++ b/app/views/index1.scala.html @@ -20,7 +20,6 @@ var map = { 'app': 'assets/app', '@@angular':'assets/lib/@@angular', - 'angular-in-memory-web-api':'assets/lib/angular-in-memory-web-api/bundles/in-memory-web-api.umd.js', 'rxjs':'assets/lib/rxjs', 'symbol-observable': 'assets/lib/symbol-observable' @@ -58,7 +57,7 @@ // Add package entries for angular packages var setPackageConfig = System.packageWithIndex ? packIndex : packUmd; - ngPackageNames.forEach(setPackageConfig) + ngPackageNames.forEach(setPackageConfig); // Add map entries for angular packages ngPackageNames.forEach(function(pkgName){ @@ -67,7 +66,7 @@ System.config({ map : map, - packages: packages, + packages: packages }); @* SystemJs works out which other modules our app module depends on and will download and load them*@ System.import('app') diff --git a/build.sbt b/build.sbt index 35c143c..e7dd1e3 100644 --- a/build.sbt +++ b/build.sbt @@ -20,7 +20,6 @@ libraryDependencies ++= { "org.webjars.npm" % "angular__router" % "3.2.0-rc.0", "org.webjars.npm" % "angular__platform-browser-dynamic" % ngVersion, "org.webjars.npm" % "angular__platform-browser" % ngVersion, - "org.webjars.npm" % "angular-in-memory-web-api" % "0.1.13", "org.webjars.npm" % "systemjs" % "0.19.39", "org.webjars.npm" % "rxjs" % "5.0.0-beta.12", "org.webjars.npm" % "es6-promise" % "3.1.2", diff --git a/conf/routes b/conf/routes index 7111cd4..df3abc4 100644 --- a/conf/routes +++ b/conf/routes @@ -8,5 +8,12 @@ GET / controllers.Application.index # Map static resources from the /public folder to the /assets URL path GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset) +GET /api/heroes controllers.Heroes.all +POST /api/heroes controllers.Heroes.create +PUT /api/heroes/:id controllers.Heroes.update(id) +DELETE /api/heroes/:id controllers.Heroes.delete(id) + +GET /api/*notFound controllers.Application.notFound(notFound) + # Map 404 to an index bis GET /*others controllers.Application.other(others) diff --git a/test/assets/todo.spec.ts b/test/assets/todo.spec.ts index 5526101..5b2ad17 100644 --- a/test/assets/todo.spec.ts +++ b/test/assets/todo.spec.ts @@ -19,5 +19,5 @@ describe('some component', () => { }; // This is a test. console.log('running js test '+JSON.stringify(todo)); - }) -}) + }); +}); From b3518b70f0a6212c23de379cae9fae64150efe7c Mon Sep 17 00:00:00 2001 From: Joost de Vries Date: Sun, 27 Nov 2016 12:00:43 +0100 Subject: [PATCH 04/22] small cleanup --- app/controllers/Heroes.scala | 47 +++++++++++++++----------------- app/services/HeroesService.scala | 6 ++++ conf/routes | 4 +-- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/app/controllers/Heroes.scala b/app/controllers/Heroes.scala index 166a592..2752f96 100644 --- a/app/controllers/Heroes.scala +++ b/app/controllers/Heroes.scala @@ -2,43 +2,40 @@ package controllers import javax.inject.Inject -import play.api.libs.json.{Json, Writes} -import play.api.mvc.{Action, BodyParser, Controller} -import services.{Hero, HeroesService} +import play.api.libs.json.{JsObject, JsValue, Json, Writes} +import play.api.mvc.{Action, Controller} +import services.HeroesService import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future -class Heroes @Inject() (heroesService: HeroesService) extends Controller { +class Heroes @Inject()(heroesService: HeroesService) extends Controller { def all = Action.async { - heroesService.all.map { s => Ok(Json.toJson(Map(("data" , Json.toJson(s)(Writes.list(Json.writes[Hero]))))))} + heroesService.all.map { s => Ok(toDataField(s)) } } - def delete(id: String) = Action.async { - heroesService.delete(id.toInt).map { _ => Ok("") } + private def toDataField[A](value: A)(implicit writes: Writes[A]): JsValue = { + JsObject(Seq("data" -> Json.toJson(value))) } - def create = Action.async { implicit request => - request.body - .asJson - .flatMap( json => (json \ "name").asOpt[String]) - .map {name => + def delete(id: Int) = Action.async { + heroesService.delete(id).map { _ => Ok("") } + } + + def create = Action.async(parse.json) { implicit request => + (request.body \ "name").asOpt[String] + .map { name => heroesService .create(name) - .map(hero => Json.toJson(hero)(Json.writes[Hero])) - .map(s => Ok(Json.toJson(Map(("data" , s))))) - }.getOrElse(Future.successful(BadRequest(""))) + .map(s => Ok(toDataField(s))) + }.getOrElse(Future.successful(BadRequest("expected 'name'"))) } - def update(id: String) = Action.async { implicit request => - request.body - .asJson - .map(_ \ "name") - .flatMap(_.asOpt[String]) - .map{ name => - heroesService.update(id.toInt, name) - .map(hero => Json.toJson(hero)(Json.writes[Hero])) - .map(s => Ok(Json.toJson(Map("data" -> s)))) - }.getOrElse(Future.successful(BadRequest)) + def update(id: Int) = Action.async(parse.json) { implicit request => + (request.body \ "name").asOpt[String] + .map { name => + heroesService.update(id, name) + .map(s => Ok(toDataField(s))) + }.getOrElse(Future.successful(BadRequest("expected 'name'"))) } } diff --git a/app/services/HeroesService.scala b/app/services/HeroesService.scala index c1d1305..5c97e8b 100644 --- a/app/services/HeroesService.scala +++ b/app/services/HeroesService.scala @@ -2,10 +2,16 @@ package services import javax.inject.Singleton +import play.api.libs.json.Json + import scala.concurrent.Future case class Hero(id: Int, name: String) +object Hero{ + implicit val format = Json.format[Hero] +} + @Singleton class HeroesService { @volatile private var heroes = Map( diff --git a/conf/routes b/conf/routes index df3abc4..4f8b8b5 100644 --- a/conf/routes +++ b/conf/routes @@ -10,8 +10,8 @@ GET /assets/*file controllers.Assets.versioned(path="/public", GET /api/heroes controllers.Heroes.all POST /api/heroes controllers.Heroes.create -PUT /api/heroes/:id controllers.Heroes.update(id) -DELETE /api/heroes/:id controllers.Heroes.delete(id) +PUT /api/heroes/:id controllers.Heroes.update(id:Int) +DELETE /api/heroes/:id controllers.Heroes.delete(id:Int) GET /api/*notFound controllers.Application.notFound(notFound) From 46f200c760f0812021f345fcdd4c5fd68364dd1a Mon Sep 17 00:00:00 2001 From: Joost de Vries Date: Sun, 27 Nov 2016 12:14:56 +0100 Subject: [PATCH 05/22] general version update and refresh --- build.sbt | 16 +++++----------- project/build.properties | 2 +- project/plugins.sbt | 6 +++--- typings.json | 7 ------- typings/index.d.ts | 1 - 5 files changed, 9 insertions(+), 23 deletions(-) delete mode 100644 typings.json delete mode 100644 typings/index.d.ts diff --git a/build.sbt b/build.sbt index e7dd1e3..48d6ac0 100644 --- a/build.sbt +++ b/build.sbt @@ -7,7 +7,7 @@ incOptions := incOptions.value.withNameHashing(true) updateOptions := updateOptions.value.withCachedResolution(cachedResoluton = true) libraryDependencies ++= { - val ngVersion="2.0.2" + val ngVersion="2.2.0" Seq( cache, @@ -17,19 +17,17 @@ libraryDependencies ++= { "org.webjars.npm" % "angular__core" % ngVersion, "org.webjars.npm" % "angular__http" % ngVersion, "org.webjars.npm" % "angular__forms" % ngVersion, - "org.webjars.npm" % "angular__router" % "3.2.0-rc.0", + "org.webjars.npm" % "angular__router" % "3.2.0", "org.webjars.npm" % "angular__platform-browser-dynamic" % ngVersion, "org.webjars.npm" % "angular__platform-browser" % ngVersion, - "org.webjars.npm" % "systemjs" % "0.19.39", + "org.webjars.npm" % "systemjs" % "0.19.40", "org.webjars.npm" % "rxjs" % "5.0.0-beta.12", - "org.webjars.npm" % "es6-promise" % "3.1.2", - "org.webjars.npm" % "es6-shim" % "0.35.1", "org.webjars.npm" % "reflect-metadata" % "0.1.8", - "org.webjars.npm" % "zone.js" % "0.6.25", + "org.webjars.npm" % "zone.js" % "0.6.26", "org.webjars.npm" % "core-js" % "2.4.1", "org.webjars.npm" % "symbol-observable" % "1.0.1", - "org.webjars.npm" % "typescript" % "2.0.3", + "org.webjars.npm" % "typescript" % "2.1.1", //tslint dependency "org.webjars.npm" % "tslint-eslint-rules" % "2.1.0", @@ -41,10 +39,6 @@ libraryDependencies ++= { } dependencyOverrides += "org.webjars.npm" % "minimatch" % "3.0.0" -// the typescript typing information is by convention in the typings directory -// It provides ES6 implementations. This is required when compiling to ES5. -typingsFile := Some(baseDirectory.value / "typings" / "index.d.ts") - // use the webjars npm directory (target/web/node_modules ) for resolution of module imports of angular2/core etc resolveFromWebjarsNodeModulesDir := true diff --git a/project/build.properties b/project/build.properties index 43b8278..27e88aa 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.11 +sbt.version=0.13.13 diff --git a/project/plugins.sbt b/project/plugins.sbt index cbfd626..8997daa 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,11 +1,11 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.5.3") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.5.10") // provides server side compilation of typescript to ecmascript 5 or 3 -addSbtPlugin("name.de-vries" % "sbt-typescript" % "0.3.0-beta.5") +addSbtPlugin("name.de-vries" % "sbt-typescript" % "0.3.0-beta.7") // checks your typescript code for error prone constructions -addSbtPlugin("name.de-vries" % "sbt-tslint" % "3.13.0") +addSbtPlugin("name.de-vries" % "sbt-tslint" % "3.15.1") addSbtPlugin("com.typesafe.sbt" % "sbt-digest" % "1.1.0") diff --git a/typings.json b/typings.json deleted file mode 100644 index b8a8f2e..0000000 --- a/typings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "dependencies": { - }, - "ambientDependencies": { - "jasmine": "github:DefinitelyTyped/DefinitelyTyped/jasmine/jasmine.d.ts#5c182b9af717f73146399c2485f70f1e2ac0ff2b" - } -} diff --git a/typings/index.d.ts b/typings/index.d.ts deleted file mode 100644 index 8b13789..0000000 --- a/typings/index.d.ts +++ /dev/null @@ -1 +0,0 @@ - From 8d06a5baafa8dafec46652495bd2ab9b56c4eb27 Mon Sep 17 00:00:00 2001 From: Joost de Vries Date: Sun, 27 Nov 2016 13:37:00 +0100 Subject: [PATCH 06/22] fixes tslint errors --- README.md | 5 +++ app/assets/app/app-routing.module.ts | 7 ++- app/assets/app/app.component.ts | 5 +-- app/assets/app/app.module.ts | 7 ++- app/assets/app/dashboard.component.ts | 7 ++- app/assets/app/hero-detail.component.ts | 13 +++--- app/assets/app/hero-search.component.ts | 13 +++--- app/assets/app/hero-search.service.ts | 5 +-- app/assets/app/hero.service.ts | 14 +++--- app/assets/app/hero.ts | 7 ++- app/assets/app/heroes.component.ts | 29 ++++++------- app/assets/app/rxjs-extensions.ts | 3 +- attribution.md | 4 -- build.sbt | 4 +- project/plugins.sbt | 4 +- tslint.json | 58 ++----------------------- 16 files changed, 62 insertions(+), 123 deletions(-) delete mode 100644 attribution.md diff --git a/README.md b/README.md index 8f5eac9..6068c9f 100644 --- a/README.md +++ b/README.md @@ -27,3 +27,8 @@ We can do both without changing our source code by using `sbt ~run` for the form "I've created the application through activator and it runs fine in activator but it hangs when I try to run it through sbt" This is a [known problem](https://github.com/typesafehub/activator/issues/1036) with activator. Activator generates a file `project\play-fork-run.sbt` that causes this. If you remove it or comment out its contents the application will run in sbt. +##History + +### v0.2.0-beta.5 +- replace todomvc with tour of heroes as demo ng2 application. tx [Isammoc](https://github.com/Isammoc) +- use 'tslint:reommended' as a basis for tslint rules. Downgrade typescript to a supported version by tslint 3.x diff --git a/app/assets/app/app-routing.module.ts b/app/assets/app/app-routing.module.ts index 37d39e0..a39f549 100644 --- a/app/assets/app/app-routing.module.ts +++ b/app/assets/app/app-routing.module.ts @@ -9,18 +9,17 @@ const routes: Routes = [ { path: '', redirectTo: '/dashboard', pathMatch: 'full' }, { path: 'dashboard', component: DashboardComponent }, { path: 'detail/:id', component: HeroDetailComponent }, - { path: 'heroes', component: HeroesComponent } + { path: 'heroes', component: HeroesComponent }, ]; @NgModule({ imports: [ RouterModule.forRoot(routes) ], - exports: [ RouterModule ] + exports: [ RouterModule ], }) export class AppRoutingModule {} - /* Copyright 2016 Google Inc. All Rights Reserved. Use of this source code is governed by an MIT-style license that can be found in the LICENSE file at http://angular.io/license -*/ \ No newline at end of file +*/ diff --git a/app/assets/app/app.component.ts b/app/assets/app/app.component.ts index e5e17c5..ecf8371 100644 --- a/app/assets/app/app.component.ts +++ b/app/assets/app/app.component.ts @@ -11,13 +11,12 @@ import { Component } from '@angular/core'; `, - styleUrls: ['assets/app/app.component.css'] + styleUrls: ['assets/app/app.component.css'], }) export class AppComponent { - title = 'Tour of Heroes'; + public title = 'Tour of Heroes'; } - /* Copyright 2016 Google Inc. All Rights Reserved. Use of this source code is governed by an MIT-style license that diff --git a/app/assets/app/app.module.ts b/app/assets/app/app.module.ts index cfd1e46..70ab63a 100644 --- a/app/assets/app/app.module.ts +++ b/app/assets/app/app.module.ts @@ -19,21 +19,20 @@ import { HeroSearchComponent } from './hero-search.component'; BrowserModule, FormsModule, HttpModule, - AppRoutingModule + AppRoutingModule, ], declarations: [ AppComponent, DashboardComponent, HeroDetailComponent, HeroesComponent, - HeroSearchComponent + HeroSearchComponent, ], providers: [ HeroService ], - bootstrap: [ AppComponent ] + bootstrap: [ AppComponent ], }) export class AppModule { } - /* Copyright 2016 Google Inc. All Rights Reserved. Use of this source code is governed by an MIT-style license that diff --git a/app/assets/app/dashboard.component.ts b/app/assets/app/dashboard.component.ts index feb655f..ba6b04c 100644 --- a/app/assets/app/dashboard.component.ts +++ b/app/assets/app/dashboard.component.ts @@ -7,20 +7,19 @@ import { HeroService } from './hero.service'; // moduleId: module.id, selector: 'my-dashboard', templateUrl: 'assets/app/dashboard.component.html', - styleUrls: [ 'assets/app/dashboard.component.css' ] + styleUrls: [ 'assets/app/dashboard.component.css' ], }) export class DashboardComponent implements OnInit { - heroes: Hero[] = []; + public heroes: Hero[] = []; constructor(private heroService: HeroService) { } - ngOnInit(): void { + public ngOnInit(): void { this.heroService.getHeroes() .then(heroes => this.heroes = heroes.slice(1, 5)); } } - /* Copyright 2016 Google Inc. All Rights Reserved. Use of this source code is governed by an MIT-style license that diff --git a/app/assets/app/hero-detail.component.ts b/app/assets/app/hero-detail.component.ts index 1b99567..f4a38b6 100644 --- a/app/assets/app/hero-detail.component.ts +++ b/app/assets/app/hero-detail.component.ts @@ -10,10 +10,10 @@ import { HeroService } from './hero.service'; // moduleId: module.id, selector: 'my-hero-detail', templateUrl: 'assets/app/hero-detail.component.html', - styleUrls: [ 'assets/app/hero-detail.component.css' ] + styleUrls: [ 'assets/app/hero-detail.component.css' ], }) export class HeroDetailComponent implements OnInit { - hero: Hero; + public hero: Hero; constructor( private heroService: HeroService, @@ -21,23 +21,22 @@ export class HeroDetailComponent implements OnInit { private location: Location ) {} - ngOnInit(): void { - this.route.params + public ngOnInit(): void { + this.route.params // tslint:disable-next-line:no-string-literal .switchMap((params: Params) => this.heroService.getHero(+params['id'])) .subscribe(hero => this.hero = hero); } - save(): void { + public save(): void { this.heroService.update(this.hero) .then(() => this.goBack()); } - goBack(): void { + public goBack(): void { this.location.back(); } } - /* Copyright 2016 Google Inc. All Rights Reserved. Use of this source code is governed by an MIT-style license that diff --git a/app/assets/app/hero-search.component.ts b/app/assets/app/hero-search.component.ts index 4f29661..3953c8b 100644 --- a/app/assets/app/hero-search.component.ts +++ b/app/assets/app/hero-search.component.ts @@ -11,10 +11,10 @@ import { Hero } from './hero'; selector: 'hero-search', templateUrl: 'assets/app/hero-search.component.html', styleUrls: [ 'assets/app/hero-search.component.css' ], - providers: [HeroSearchService] + providers: [HeroSearchService], }) export class HeroSearchComponent implements OnInit { - heroes: Observable; + public heroes: Observable; private searchTerms = new Subject(); constructor( @@ -22,11 +22,11 @@ export class HeroSearchComponent implements OnInit { private router: Router) {} // Push a search term into the observable stream. - search(term: string): void { + public search(term: string): void { this.searchTerms.next(term); } - ngOnInit(): void { + public ngOnInit(): void { this.heroes = this.searchTerms .debounceTime(300) // wait for 300ms pause in events .distinctUntilChanged() // ignore if next search term is same as previous @@ -37,18 +37,17 @@ export class HeroSearchComponent implements OnInit { : Observable.of([])) .catch(error => { // TODO: real error handling - console.log(error); +// console.log(error); return Observable.of([]); }); } - gotoDetail(hero: Hero): void { + public gotoDetail(hero: Hero): void { let link = ['/detail', hero.id]; this.router.navigate(link); } } - /* Copyright 2016 Google Inc. All Rights Reserved. Use of this source code is governed by an MIT-style license that diff --git a/app/assets/app/hero-search.service.ts b/app/assets/app/hero-search.service.ts index ab3fcac..9d11036 100644 --- a/app/assets/app/hero-search.service.ts +++ b/app/assets/app/hero-search.service.ts @@ -9,16 +9,15 @@ export class HeroSearchService { constructor(private http: Http) {} - search(term: string): Observable { + public search(term: string): Observable { return this.http .get(`app/heroes/?name=${term}`) .map((r: Response) => r.json().data as Hero[]); } } - /* Copyright 2016 Google Inc. All Rights Reserved. Use of this source code is governed by an MIT-style license that can be found in the LICENSE file at http://angular.io/license -*/ \ No newline at end of file +*/ diff --git a/app/assets/app/hero.service.ts b/app/assets/app/hero.service.ts index e347710..c1c30eb 100644 --- a/app/assets/app/hero.service.ts +++ b/app/assets/app/hero.service.ts @@ -13,19 +13,19 @@ export class HeroService { constructor(private http: Http) { } - getHeroes(): Promise { + public getHeroes(): Promise { return this.http.get(this.heroesUrl) .toPromise() .then(response => response.json().data as Hero[]) .catch(this.handleError); } - getHero(id: number): Promise { + public getHero(id: number): Promise { return this.getHeroes() .then(heroes => heroes.find(hero => hero.id === id)); } - delete(id: number): Promise { + public delete(id: number): Promise { const url = `${this.heroesUrl}/${id}`; return this.http.delete(url, {headers: this.headers}) .toPromise() @@ -33,7 +33,7 @@ export class HeroService { .catch(this.handleError); } - create(name: string): Promise { + public create(name: string): Promise { return this.http .post(this.heroesUrl, JSON.stringify({name: name}), {headers: this.headers}) .toPromise() @@ -41,7 +41,7 @@ export class HeroService { .catch(this.handleError); } - update(hero: Hero): Promise { + public update(hero: Hero): Promise { const url = `${this.heroesUrl}/${hero.id}`; return this.http .put(url, JSON.stringify(hero), {headers: this.headers}) @@ -51,13 +51,11 @@ export class HeroService { } private handleError(error: any): Promise { - console.error('An error occurred', error); // for demo purposes only +// console.error('An error occurred', error); // for demo purposes only return Promise.reject(error.message || error); } } - - /* Copyright 2016 Google Inc. All Rights Reserved. Use of this source code is governed by an MIT-style license that diff --git a/app/assets/app/hero.ts b/app/assets/app/hero.ts index a399c37..f518f22 100644 --- a/app/assets/app/hero.ts +++ b/app/assets/app/hero.ts @@ -1,11 +1,10 @@ export class Hero { - id: number; - name: string; + public id: number; + public name: string; } - /* Copyright 2016 Google Inc. All Rights Reserved. Use of this source code is governed by an MIT-style license that can be found in the LICENSE file at http://angular.io/license -*/ \ No newline at end of file +*/ diff --git a/app/assets/app/heroes.component.ts b/app/assets/app/heroes.component.ts index 36c0f2b..eb785d0 100644 --- a/app/assets/app/heroes.component.ts +++ b/app/assets/app/heroes.component.ts @@ -8,23 +8,17 @@ import { HeroService } from './hero.service'; // moduleId: module.id, selector: 'my-heroes', templateUrl: 'assets/app/heroes.component.html', - styleUrls: [ 'assets/app/heroes.component.css' ] + styleUrls: [ 'assets/app/heroes.component.css' ], }) export class HeroesComponent implements OnInit { - heroes: Hero[]; - selectedHero: Hero; + public heroes: Hero[]; + public selectedHero: Hero; constructor( private heroService: HeroService, private router: Router) { } - getHeroes(): void { - this.heroService - .getHeroes() - .then(heroes => this.heroes = heroes); - } - - add(name: string): void { + public add(name: string): void { name = name.trim(); if (!name) { return; } this.heroService.create(name) @@ -34,7 +28,7 @@ export class HeroesComponent implements OnInit { }); } - delete(hero: Hero): void { + public delete(hero: Hero): void { this.heroService .delete(hero.id) .then(() => { @@ -43,19 +37,24 @@ export class HeroesComponent implements OnInit { }); } - ngOnInit(): void { + public ngOnInit(): void { this.getHeroes(); } - onSelect(hero: Hero): void { + public onSelect(hero: Hero): void { this.selectedHero = hero; } - gotoDetail(): void { + public gotoDetail(): void { this.router.navigate(['/detail', this.selectedHero.id]); } -} + private getHeroes(): void { + this.heroService + .getHeroes() + .then(heroes => this.heroes = heroes); + } +} /* Copyright 2016 Google Inc. All Rights Reserved. diff --git a/app/assets/app/rxjs-extensions.ts b/app/assets/app/rxjs-extensions.ts index 3aa9030..4c3e2b9 100644 --- a/app/assets/app/rxjs-extensions.ts +++ b/app/assets/app/rxjs-extensions.ts @@ -11,9 +11,8 @@ import 'rxjs/add/operator/filter'; import 'rxjs/add/operator/map'; import 'rxjs/add/operator/switchMap'; - /* Copyright 2016 Google Inc. All Rights Reserved. Use of this source code is governed by an MIT-style license that can be found in the LICENSE file at http://angular.io/license -*/ \ No newline at end of file +*/ diff --git a/attribution.md b/attribution.md deleted file mode 100644 index 9c12522..0000000 --- a/attribution.md +++ /dev/null @@ -1,4 +0,0 @@ -#Attribution - -This activator template uses -- the [Angular2 Typescript todomvc](https://github.com/tastejs/todomvc/tree/master/examples/angular2) \ No newline at end of file diff --git a/build.sbt b/build.sbt index 48d6ac0..e0af285 100644 --- a/build.sbt +++ b/build.sbt @@ -1,5 +1,5 @@ name := """play-angular2-typescript""" -version := "0.2.0-beta.4" +version := "0.2.0-beta.5" lazy val root = (project in file(".")).enablePlugins(PlayScala) scalaVersion := "2.11.8" @@ -27,7 +27,7 @@ libraryDependencies ++= { "org.webjars.npm" % "core-js" % "2.4.1", "org.webjars.npm" % "symbol-observable" % "1.0.1", - "org.webjars.npm" % "typescript" % "2.1.1", + "org.webjars.npm" % "typescript" % "2.0.10", //tslint dependency "org.webjars.npm" % "tslint-eslint-rules" % "2.1.0", diff --git a/project/plugins.sbt b/project/plugins.sbt index 8997daa..abf24e6 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -2,10 +2,10 @@ addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.5.10") // provides server side compilation of typescript to ecmascript 5 or 3 -addSbtPlugin("name.de-vries" % "sbt-typescript" % "0.3.0-beta.7") +addSbtPlugin("name.de-vries" % "sbt-typescript" % "0.3.0-beta.6") // checks your typescript code for error prone constructions -addSbtPlugin("name.de-vries" % "sbt-tslint" % "3.15.1") +addSbtPlugin("name.de-vries" % "sbt-tslint" % "3.15.1-1") addSbtPlugin("com.typesafe.sbt" % "sbt-digest" % "1.1.0") diff --git a/tslint.json b/tslint.json index 190273d..a2c787e 100644 --- a/tslint.json +++ b/tslint.json @@ -1,4 +1,5 @@ { + "extends": "tslint:recommended", "rules": { /* ng2 rules */ @@ -23,59 +24,8 @@ "no-empty": true, /* this is an eslint rule */ - /* the following rules are tslint rules */ - "class-name": true, - "comment-format": [ - true, - "check-space" - ], - "indent": [ - true, - "spaces" - ], - "no-duplicate-variable": true, - "no-eval": true, - "no-internal-module": true, - "no-trailing-whitespace": true, - "no-var-keyword": true, - "one-line": [ - true, - "check-open-brace", - "check-whitespace" - ], - "quotemark": [ - true, - "single" - ], - "semicolon": [ - true, - "always" - ], - "triple-equals": [ - true, - "allow-null-check" - ], - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - } - ], - "variable-name": [ - true, - "ban-keywords" - ], - "whitespace": [ - false, - "check-branch", - "check-decl", - "check-operator", - "check-separator", - "check-type" - ] + /* you can override any of the default tslint rules provided by 'tslint:recommended' */ + "object-literal-sort-keys": false, + "quotemark": [true, "single", "avoid-escape"] } } From 3d111b1013599ba75e933151f9831441927adc93 Mon Sep 17 00:00:00 2001 From: Joost de Vries Date: Sat, 10 Dec 2016 11:16:46 +0100 Subject: [PATCH 07/22] upgrades to tslint 4.0.2 --- README.md | 2 ++ app/assets/app/hero-detail.component.ts | 2 +- build.sbt | 10 ++++--- project/plugins.sbt | 5 ++-- tslint.json | 40 ++++++++++++++----------- 5 files changed, 34 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 6068c9f..5120cdc 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ We can do both without changing our source code by using `sbt ~run` for the form This is a [known problem](https://github.com/typesafehub/activator/issues/1036) with activator. Activator generates a file `project\play-fork-run.sbt` that causes this. If you remove it or comment out its contents the application will run in sbt. ##History +### v0.2.0-beta.6 +- uses tslint 4.0.2 ### v0.2.0-beta.5 - replace todomvc with tour of heroes as demo ng2 application. tx [Isammoc](https://github.com/Isammoc) diff --git a/app/assets/app/hero-detail.component.ts b/app/assets/app/hero-detail.component.ts index f4a38b6..93982e3 100644 --- a/app/assets/app/hero-detail.component.ts +++ b/app/assets/app/hero-detail.component.ts @@ -18,7 +18,7 @@ export class HeroDetailComponent implements OnInit { constructor( private heroService: HeroService, private route: ActivatedRoute, - private location: Location + private location: Location, ) {} public ngOnInit(): void { diff --git a/build.sbt b/build.sbt index e0af285..e4089db 100644 --- a/build.sbt +++ b/build.sbt @@ -1,5 +1,5 @@ name := """play-angular2-typescript""" -version := "0.2.0-beta.5" +version := "0.2.0-beta.6" lazy val root = (project in file(".")).enablePlugins(PlayScala) scalaVersion := "2.11.8" @@ -27,11 +27,12 @@ libraryDependencies ++= { "org.webjars.npm" % "core-js" % "2.4.1", "org.webjars.npm" % "symbol-observable" % "1.0.1", - "org.webjars.npm" % "typescript" % "2.0.10", + "org.webjars.npm" % "typescript" % "2.1.1", //tslint dependency - "org.webjars.npm" % "tslint-eslint-rules" % "2.1.0", - "org.webjars.npm" % "codelyzer" % "0.0.28", + "org.webjars.npm" % "tslint-eslint-rules" % "3.1.0", + "org.webjars.npm" % "tslint-microsoft-contrib" % "2.0.12", + // "org.webjars.npm" % "codelyzer" % "2.0.0-beta.1", "org.webjars.npm" % "types__jasmine" % "2.2.26-alpha" % "test" //test // "org.webjars.npm" % "jasmine-core" % "2.4.1" @@ -48,4 +49,5 @@ resolveFromWebjarsNodeModulesDir := true ng2LintRulesDir.value )) +logLevel in tslint := Level.Debug routesGenerator := InjectedRoutesGenerator diff --git a/project/plugins.sbt b/project/plugins.sbt index abf24e6..52f230a 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,11 +1,12 @@ + // The Play plugin addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.5.10") // provides server side compilation of typescript to ecmascript 5 or 3 -addSbtPlugin("name.de-vries" % "sbt-typescript" % "0.3.0-beta.6") +addSbtPlugin("name.de-vries" % "sbt-typescript" % "0.3.0-beta.8") // checks your typescript code for error prone constructions -addSbtPlugin("name.de-vries" % "sbt-tslint" % "3.15.1-1") +addSbtPlugin("name.de-vries" % "sbt-tslint" % "4.0.2-1") addSbtPlugin("com.typesafe.sbt" % "sbt-digest" % "1.1.0") diff --git a/tslint.json b/tslint.json index a2c787e..aeafc56 100644 --- a/tslint.json +++ b/tslint.json @@ -3,29 +3,33 @@ "rules": { /* ng2 rules */ - "directive-selector-name": [true, "camelCase"], - "component-selector-name": [true, "kebab-case"], - "directive-selector-type": [true, "attribute"], - "component-selector-type": [true, "element"], - "use-input-property-decorator": true, - "use-output-property-decorator": true, - "use-host-property-decorator": true, - "no-attribute-parameter-decorator": true, - "no-input-rename": true, - "no-output-rename": true, - "no-forward-ref" :true, - "use-life-cycle-interface": true, - "use-pipe-transform-interface": true, - "pipe-naming": [true, "camelCase", "todo"], - "component-class-suffix": true, - "directive-class-suffix": true, - "import-destructuring-spacing": false, +// "directive-selector": [true, "attribute", "sg", "camelCase"], +// "component-selector": [true, "element", "sg", "kebab-case"], +// "use-input-property-decorator": true, +// "use-output-property-decorator": true, +// "use-host-property-decorator": true, +// "no-attribute-parameter-decorator": true, +// "no-input-rename": true, +// "no-output-rename": true, +// "no-forward-ref": true, +// "use-life-cycle-interface": true, +// "use-pipe-transform-interface": true, +// "pipe-naming": [true, "camelCase", "sg"], +// "component-class-suffix": true, +// "directive-class-suffix": true, +// "import-destructuring-spacing": true, +// "templates-use-public": true, +// "no-access-missing-member": true, +// "invoke-injectable": true, "no-empty": true, /* this is an eslint rule */ /* you can override any of the default tslint rules provided by 'tslint:recommended' */ "object-literal-sort-keys": false, - "quotemark": [true, "single", "avoid-escape"] + "quotemark": [true, "single", "avoid-escape"], + "ordered-imports": false, + "arrow-parens": false, + "object-literal-shorthand": false } } From 0e0eb7f8ac196a135eb0ac95276ae96028e106bf Mon Sep 17 00:00:00 2001 From: Joost de Vries Date: Sat, 10 Dec 2016 11:17:45 +0100 Subject: [PATCH 08/22] fixes ts version --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index e4089db..bbe88c1 100644 --- a/build.sbt +++ b/build.sbt @@ -27,7 +27,7 @@ libraryDependencies ++= { "org.webjars.npm" % "core-js" % "2.4.1", "org.webjars.npm" % "symbol-observable" % "1.0.1", - "org.webjars.npm" % "typescript" % "2.1.1", + "org.webjars.npm" % "typescript" % "2.1.4", //tslint dependency "org.webjars.npm" % "tslint-eslint-rules" % "3.1.0", From 2e3efadeced85473f6490d78cc5d24be5b6832d5 Mon Sep 17 00:00:00 2001 From: Joost de Vries Date: Sun, 26 Feb 2017 20:02:17 +0100 Subject: [PATCH 09/22] tslint 4.4.2 --- README.md | 3 +++ build.sbt | 1 + project/plugins.sbt | 7 +++++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5120cdc..6ed8bc6 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,9 @@ We can do both without changing our source code by using `sbt ~run` for the form This is a [known problem](https://github.com/typesafehub/activator/issues/1036) with activator. Activator generates a file `project\play-fork-run.sbt` that causes this. If you remove it or comment out its contents the application will run in sbt. ##History +### v4.4.2 +- tslint 4.4.2 + ### v0.2.0-beta.6 - uses tslint 4.0.2 diff --git a/build.sbt b/build.sbt index bbe88c1..90bc36d 100644 --- a/build.sbt +++ b/build.sbt @@ -5,6 +5,7 @@ lazy val root = (project in file(".")).enablePlugins(PlayScala) scalaVersion := "2.11.8" incOptions := incOptions.value.withNameHashing(true) updateOptions := updateOptions.value.withCachedResolution(cachedResoluton = true) +JsEngineKeys.engineType := JsEngineKeys.EngineType.Node libraryDependencies ++= { val ngVersion="2.2.0" diff --git a/project/plugins.sbt b/project/plugins.sbt index 52f230a..6f3cf5a 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -3,12 +3,15 @@ addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.5.10") // provides server side compilation of typescript to ecmascript 5 or 3 -addSbtPlugin("name.de-vries" % "sbt-typescript" % "0.3.0-beta.8") +addSbtPlugin("name.de-vries" % "sbt-typescript" % "0.3.0-beta.10") // checks your typescript code for error prone constructions -addSbtPlugin("name.de-vries" % "sbt-tslint" % "4.0.2-1") +addSbtPlugin("name.de-vries" % "sbt-tslint" % "4.4.2") addSbtPlugin("com.typesafe.sbt" % "sbt-digest" % "1.1.0") addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.1.10") addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.8.2") + +//addSbtPlugin("com.typesafe.sbt" % "sbt-js-engine" % "1.1.4") +//addSbtPlugin("com.typesafe.sbt" % "sbt-web" % "1.4.0") From 62e20b8d7efb9d5f2cb453e656b2e24293263c9f Mon Sep 17 00:00:00 2001 From: Joost de Vries Date: Sun, 26 Feb 2017 20:03:43 +0100 Subject: [PATCH 10/22] undo --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 6ed8bc6..22fe539 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,6 @@ We can do both without changing our source code by using `sbt ~run` for the form This is a [known problem](https://github.com/typesafehub/activator/issues/1036) with activator. Activator generates a file `project\play-fork-run.sbt` that causes this. If you remove it or comment out its contents the application will run in sbt. ##History -### v4.4.2 -- tslint 4.4.2 ### v0.2.0-beta.6 - uses tslint 4.0.2 From bd755fc51519bce8e53287ba4aac295084db0955 Mon Sep 17 00:00:00 2001 From: Joost de Vries Date: Sun, 26 Feb 2017 20:15:10 +0100 Subject: [PATCH 11/22] version upgrade --- build.sbt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/build.sbt b/build.sbt index 90bc36d..5a2af2a 100644 --- a/build.sbt +++ b/build.sbt @@ -7,6 +7,8 @@ incOptions := incOptions.value.withNameHashing(true) updateOptions := updateOptions.value.withCachedResolution(cachedResoluton = true) JsEngineKeys.engineType := JsEngineKeys.EngineType.Node +resolvers += Resolver.jcenterRepo + libraryDependencies ++= { val ngVersion="2.2.0" Seq( @@ -28,11 +30,11 @@ libraryDependencies ++= { "org.webjars.npm" % "core-js" % "2.4.1", "org.webjars.npm" % "symbol-observable" % "1.0.1", - "org.webjars.npm" % "typescript" % "2.1.4", + "org.webjars.npm" % "typescript" % "2.2.1", //tslint dependency - "org.webjars.npm" % "tslint-eslint-rules" % "3.1.0", - "org.webjars.npm" % "tslint-microsoft-contrib" % "2.0.12", + "org.webjars.npm" % "tslint-eslint-rules" % "3.4.0", + "org.webjars.npm" % "tslint-microsoft-contrib" % "4.0.0", // "org.webjars.npm" % "codelyzer" % "2.0.0-beta.1", "org.webjars.npm" % "types__jasmine" % "2.2.26-alpha" % "test" //test From 68944596d901e9f911c5b136e1b9049eece63f35 Mon Sep 17 00:00:00 2001 From: Itchy Date: Thu, 30 Mar 2017 19:55:13 +0200 Subject: [PATCH 12/22] =?UTF-8?q?Switched=20also=20the=20=E2=80=9CTour=20o?= =?UTF-8?q?f=20Heroes=E2=80=9D-sample=20to=20Java?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/Application.java | 8 ++++ app/controllers/Heroes.java | 56 +++++++++++++++++++++++ app/controllers/Heroes.scala | 41 ----------------- app/services/HeroesService.java | 77 ++++++++++++++++++++++++++++++++ app/services/HeroesService.scala | 48 -------------------- build.sbt | 2 + conf/routes | 2 +- tsconfig.json | 4 +- 8 files changed, 146 insertions(+), 92 deletions(-) create mode 100644 app/controllers/Heroes.java delete mode 100644 app/controllers/Heroes.scala create mode 100644 app/services/HeroesService.java delete mode 100644 app/services/HeroesService.scala diff --git a/app/controllers/Application.java b/app/controllers/Application.java index 349dea3..8ff7302 100644 --- a/app/controllers/Application.java +++ b/app/controllers/Application.java @@ -15,4 +15,12 @@ public Result index() { return ok(index1.render()); } + public Result apiNotFound(String notFound) { + return notFound(notFound); + } + + public Result other(String other) { + return index(); + } + } diff --git a/app/controllers/Heroes.java b/app/controllers/Heroes.java new file mode 100644 index 0000000..5d81726 --- /dev/null +++ b/app/controllers/Heroes.java @@ -0,0 +1,56 @@ +package controllers; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.POJONode; +import com.google.inject.Inject; +import play.mvc.Result; +import services.HeroesService; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; + +import static play.mvc.Controller.request; +import static play.mvc.Results.badRequest; +import static play.mvc.Results.ok; + + +public class Heroes { + @Inject + private HeroesService heroesService; + + public CompletionStage all() { + return heroesService.all().thenApply((List heroes) -> ok(toDataField(heroes))); + } + + private JsonNode toDataField(Object value) { + ObjectNode jsonNodes = JsonNodeFactory.instance.objectNode(); + jsonNodes.set("data", new POJONode(value)); + return jsonNodes; + } + + public CompletionStage delete(int id) { + return heroesService.delete(id).thenApply((Optional hero) -> ok("")); + } + + public CompletionStage create() { + final String name = request().body().asJson().get("name").textValue(); + if (name != null) { + return heroesService.create(name).thenApply((HeroesService.Hero hero) -> ok(toDataField(hero))); + } else { + return CompletableFuture.completedFuture(badRequest("expected 'name'")); + } + } + + public CompletionStage update(int id) { + final String name = request().body().asJson().get("name").textValue(); + if (name != null) { + return heroesService.update(id, name).thenApply((HeroesService.Hero hero) -> ok(toDataField(hero))); + } else { + return CompletableFuture.completedFuture(badRequest("expected 'name'")); + } + } +} diff --git a/app/controllers/Heroes.scala b/app/controllers/Heroes.scala deleted file mode 100644 index 2752f96..0000000 --- a/app/controllers/Heroes.scala +++ /dev/null @@ -1,41 +0,0 @@ -package controllers - -import javax.inject.Inject - -import play.api.libs.json.{JsObject, JsValue, Json, Writes} -import play.api.mvc.{Action, Controller} -import services.HeroesService - -import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.Future - -class Heroes @Inject()(heroesService: HeroesService) extends Controller { - def all = Action.async { - heroesService.all.map { s => Ok(toDataField(s)) } - } - - private def toDataField[A](value: A)(implicit writes: Writes[A]): JsValue = { - JsObject(Seq("data" -> Json.toJson(value))) - } - - def delete(id: Int) = Action.async { - heroesService.delete(id).map { _ => Ok("") } - } - - def create = Action.async(parse.json) { implicit request => - (request.body \ "name").asOpt[String] - .map { name => - heroesService - .create(name) - .map(s => Ok(toDataField(s))) - }.getOrElse(Future.successful(BadRequest("expected 'name'"))) - } - - def update(id: Int) = Action.async(parse.json) { implicit request => - (request.body \ "name").asOpt[String] - .map { name => - heroesService.update(id, name) - .map(s => Ok(toDataField(s))) - }.getOrElse(Future.successful(BadRequest("expected 'name'"))) - } -} diff --git a/app/services/HeroesService.java b/app/services/HeroesService.java new file mode 100644 index 0000000..1f56552 --- /dev/null +++ b/app/services/HeroesService.java @@ -0,0 +1,77 @@ +package services; + +import com.google.inject.Singleton; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.stream.Collectors; + +@Singleton +public class HeroesService { + + private Map heroes = new HashMap() {{ + put(11, new Hero(11, "Mr. Nice")); + put(12, new Hero(12, "Narco")); + put(13, new Hero(13, "Bombasto")); + put(14, new Hero(14, "Celeritas")); + put(15, new Hero(15, "Magneta")); + put(16, new Hero(16, "RubberMan")); + put(17, new Hero(17, "Dynama")); + put(18, new Hero(18, "Dr IQ")); + put(19, new Hero(19, "Magma")); + put(20, new Hero(20, "Tornado")); + }}; + + public CompletionStage> all() { + return CompletableFuture.supplyAsync(() -> heroes.values().stream().sorted((o1, o2) -> Comparator.comparing((Hero h) -> h.id).compare(o1, o2)).collect(Collectors.toList())); + } + + public CompletionStage> delete(int id) { + return CompletableFuture.supplyAsync(() -> Optional.ofNullable(heroes.remove(id))); + } + + public CompletionStage create(String name) { + return CompletableFuture.supplyAsync(() -> { + final int nextNewId = heroes.keySet().stream().mapToInt(i -> i).max().getAsInt() + 1; + Hero hero = new Hero(nextNewId, name); + heroes.put(hero.id, hero); + return hero; + }); + } + + public CompletionStage update(int id, String name) { + return CompletableFuture.supplyAsync(() -> { + Hero hero = new Hero(id, name); + heroes.put(hero.id, hero); + return hero; + }); + } + + + public static class Hero { + private int id; + private String name; + + public Hero(int id, String name) { + this.id = id; + this.name = name; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } +} diff --git a/app/services/HeroesService.scala b/app/services/HeroesService.scala deleted file mode 100644 index 5c97e8b..0000000 --- a/app/services/HeroesService.scala +++ /dev/null @@ -1,48 +0,0 @@ -package services - -import javax.inject.Singleton - -import play.api.libs.json.Json - -import scala.concurrent.Future - -case class Hero(id: Int, name: String) - -object Hero{ - implicit val format = Json.format[Hero] -} - -@Singleton -class HeroesService { - @volatile private var heroes = Map( - 11 -> Hero(id = 11, name = "Mr. Nice"), - 12 -> Hero(id = 12, name = "Narco"), - 13 -> Hero(id = 13, name = "Bombasto"), - 14 -> Hero(id = 14, name = "Celeritas"), - 15 -> Hero(id = 15, name = "Magneta"), - 16 -> Hero(id = 16, name = "RubberMan"), - 17 -> Hero(id = 17, name = "Dynama"), - 18 -> Hero(id = 18, name = "Dr IQ"), - 19 -> Hero(id = 19, name = "Magma"), - 20 -> Hero(id = 20, name = "Tornado") - ) - - def all: Future[List[Hero]] = Future.successful(heroes.values.toList.sortBy(_.id)) - - def delete(id: Int):Future[Option[Hero]] = { - val answer = heroes.get(id) - heroes = heroes - id - Future.successful(answer) - } - - def create(name: String): Future[Hero] = Future.successful { - val id = heroes.keys.max + 1 - heroes = heroes + (id -> Hero(id, name)) - Hero(id, name) - } - - def update(id: Int, name: String): Future[Hero] = Future.successful { - heroes = heroes + (id -> Hero(id, name)) - Hero(id, name) - } -} diff --git a/build.sbt b/build.sbt index 00d0dad..c26d488 100644 --- a/build.sbt +++ b/build.sbt @@ -46,6 +46,8 @@ dependencyOverrides += "org.webjars.npm" % "minimatch" % "3.0.0" // use the webjars npm directory (target/web/node_modules ) for resolution of module imports of angular2/core etc resolveFromWebjarsNodeModulesDir := true +tsCodesToIgnore := List(canNotFindModule) + // use the combined tslint and eslint rules plus ng2 lint rules (rulesDirectories in tslint) := Some(List( tslintEslintRulesDir.value, diff --git a/conf/routes b/conf/routes index 4f8b8b5..5da21b2 100644 --- a/conf/routes +++ b/conf/routes @@ -13,7 +13,7 @@ POST /api/heroes controllers.Heroes.create PUT /api/heroes/:id controllers.Heroes.update(id:Int) DELETE /api/heroes/:id controllers.Heroes.delete(id:Int) -GET /api/*notFound controllers.Application.notFound(notFound) +GET /api/*notFound controllers.Application.apiNotFound(notFound) # Map 404 to an index bis GET /*others controllers.Application.other(others) diff --git a/tsconfig.json b/tsconfig.json index 388f559..700be8b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,7 +22,7 @@ ] }, /* noImplicitAny when you want your typescript to be fully typed */ - "noImplicitAny":true, + "noImplicitAny":false, "noFallthroughCasesInSwitch":true, "noImplicitReturns":true, "noImplicitThis":true, @@ -39,4 +39,4 @@ "typings/browser", "target/web" ] -} \ No newline at end of file +} From 0ee91c978dc0ce8791d73460b763221c477e138d Mon Sep 17 00:00:00 2001 From: Joost de Vries Date: Tue, 11 Apr 2017 17:55:05 +0200 Subject: [PATCH 13/22] upgrades to typescript 2.3.0 --- README.md | 13 ++++++++----- app/views/index1.scala.html | 1 - build.sbt | 3 ++- project/build.properties | 2 +- project/plugins.sbt | 2 +- tsconfig.json | 9 +++------ 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 22fe539..442d7a4 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,17 @@ -#Play Angular2 Typescript sample application [![Build Status](https://travis-ci.org/joost-de-vries/play-angular2-typescript.png?branch=master)](https://travis-ci.org/joost-de-vries/play-angular2-typescript) +# Play Angular2 Typescript sample application [![Build Status](https://travis-ci.org/joost-de-vries/play-angular2-typescript.png?branch=master)](https://travis-ci.org/joost-de-vries/play-angular2-typescript) This is an activator template that generates a sample Play Angular2 RC4 Typescript 2.0 application. As mentioned it features an Angular2 application with Typescript compilation integrated with the continuous compilation of Play Scala code. The Typescript code is linted with `tslint`. -##Installation +## Installation Once you have [activator](https://www.typesafe.com/community/core-tools/activator-and-sbt) installed you can run `activator new play-angular2-typescript` and you'll have a local application with a tutorial. Or you can just clone this repo and run `sbt ~run`. **NB**: Make sure you don't have `typescript` installed globally. If you do have a global npm installation of `typescript` that version will be picked up. And then all bets are off. A symptom of having an older global `typescript` installation is that you get a `JsTaskFailure` / `TypeError` that the function `convertCompilerOptionsFromJson` can't be found. See [this issue](https://github.com/joost-de-vries/play-angular2-typescript/issues/1) -##Getting started +## Getting started The NG2 application is the standard todomvc app. This Play project shows 3 ways of loading that app in the browser using Play. 1. let the browser load the typescript files and have them compiled in the browser itself. This is easy to setup. But it makes greater computation demands on the client device. And it is really hard to find out about compilation errors. Which rather defies the added value of typed programming that typescript provides. This is implemented in [this html file](https://github.com/joost-de-vries/play-angular2-typescript/blob/master/app/views/index.scala.html). This template hasn't been ported to Angular RC4 yet. @@ -22,12 +22,15 @@ For a lot of production applications option 3 will be required. While option 2 i We can do both without changing our source code by using `sbt ~run` for the former and `sbt stage -DtsCompileMode=stage` for the latter. So to get option 3 to work you'll have to provide that `-DtsCompileMode=stage` jvm argument. -##what to do if +## what to do if "I've created the application through activator and it runs fine in activator but it hangs when I try to run it through sbt" This is a [known problem](https://github.com/typesafehub/activator/issues/1036) with activator. Activator generates a file `project\play-fork-run.sbt` that causes this. If you remove it or comment out its contents the application will run in sbt. -##History +## History + +### v0.2.0-beta.7 +- upgrades to sbt-typescript 0.3.0-beta.11 and hence typescript 2.3.0 ### v0.2.0-beta.6 - uses tslint 4.0.2 diff --git a/app/views/index1.scala.html b/app/views/index1.scala.html index 2919c43..5321c7a 100644 --- a/app/views/index1.scala.html +++ b/app/views/index1.scala.html @@ -7,7 +7,6 @@ The browser downloads .js files. *@ Angular Tour of Heroes - diff --git a/build.sbt b/build.sbt index 5a2af2a..26d2458 100644 --- a/build.sbt +++ b/build.sbt @@ -1,10 +1,11 @@ name := """play-angular2-typescript""" -version := "0.2.0-beta.6" +version := "0.2.0-beta.7" lazy val root = (project in file(".")).enablePlugins(PlayScala) scalaVersion := "2.11.8" incOptions := incOptions.value.withNameHashing(true) updateOptions := updateOptions.value.withCachedResolution(cachedResoluton = true) +//we use nodejs to make our typescript build as fast as possible JsEngineKeys.engineType := JsEngineKeys.EngineType.Node resolvers += Resolver.jcenterRepo diff --git a/project/build.properties b/project/build.properties index 27e88aa..64317fd 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.13 +sbt.version=0.13.15 diff --git a/project/plugins.sbt b/project/plugins.sbt index 6f3cf5a..7b09517 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -3,7 +3,7 @@ addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.5.10") // provides server side compilation of typescript to ecmascript 5 or 3 -addSbtPlugin("name.de-vries" % "sbt-typescript" % "0.3.0-beta.10") +addSbtPlugin("name.de-vries" % "sbt-typescript" % "0.3.0-beta.11") // checks your typescript code for error prone constructions addSbtPlugin("name.de-vries" % "sbt-tslint" % "4.4.2") diff --git a/tsconfig.json b/tsconfig.json index 388f559..ed8286f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,11 +22,8 @@ ] }, /* noImplicitAny when you want your typescript to be fully typed */ - "noImplicitAny":true, - "noFallthroughCasesInSwitch":true, - "noImplicitReturns":true, - "noImplicitThis":true, - // "strictNullChecks":true, doesn't work yet with @angular RC4 + "strict":true, + "strictNullChecks":false, //doesn't work yet with @angular RC4 "outDir": "./target/ts", "lib": ["es6", "dom"] }, @@ -39,4 +36,4 @@ "typings/browser", "target/web" ] -} \ No newline at end of file +} From 496d0577f0c8abd41d91b76f4bb05e104a9facf7 Mon Sep 17 00:00:00 2001 From: Joost de Vries Date: Tue, 11 Apr 2017 22:46:51 +0200 Subject: [PATCH 14/22] add node --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9c20b8b..86ff824 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ cache: scala: - 2.11.6 script: - - sbt stage + - sbt "stage -DtsCompileMode=stage" +node_js: "node" jdk: - - oraclejdk8 \ No newline at end of file + - oraclejdk8 From 01f9c53cab15c3ea8454473de58a64bf61afecd7 Mon Sep 17 00:00:00 2001 From: Joost de Vries Date: Tue, 11 Apr 2017 22:48:40 +0200 Subject: [PATCH 15/22] angular --- activator.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activator.properties b/activator.properties index 0b84290..b38e958 100644 --- a/activator.properties +++ b/activator.properties @@ -1,7 +1,7 @@ name=play-angular2-typescript title=Angular2 Typescript application with Play description=A Play Angular2 starter application with incremental Typescript compilation. -tags=playframework,angular2,typescript,scala +tags=playframework,angular,angular2,typescript,scala authorTwitter=jouke authorLogo=https://avatars.githubusercontent.com/u/1859426?v=3 authorName=Joost de Vries From 562e733f72664e11155db30e2a35e8a2363d5643 Mon Sep 17 00:00:00 2001 From: Joost de Vries Date: Tue, 11 Apr 2017 22:52:20 +0200 Subject: [PATCH 16/22] stage --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 86ff824..137c5dd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ cache: scala: - 2.11.6 script: - - sbt "stage -DtsCompileMode=stage" + - sbt stage -DtsCompileMode=stage node_js: "node" jdk: - oraclejdk8 From e22199cf3d2e0f848442fc24f85dce5dc83c6dab Mon Sep 17 00:00:00 2001 From: Joost de Vries Date: Tue, 11 Apr 2017 23:15:25 +0200 Subject: [PATCH 17/22] use trusty --- .travis.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 137c5dd..d81db4b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,14 @@ +sudo: required +dist: trusty language: scala -sudo: false cache: directories: - $HOME/.ivy2 scala: - 2.11.6 -script: - - sbt stage -DtsCompileMode=stage -node_js: "node" +node_js: + - "6" jdk: - oraclejdk8 +script: + - sbt stage -DtsCompileMode=stage From fa2a6a35b6834d90397a5f8f06949fcdefebc5d1 Mon Sep 17 00:00:00 2001 From: Joost de Vries Date: Tue, 11 Apr 2017 23:25:32 +0200 Subject: [PATCH 18/22] use nvm --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index d81db4b..dc45b77 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,14 @@ -sudo: required -dist: trusty +sudo: false language: scala cache: directories: - $HOME/.ivy2 +env: + - NODE_VERSION=6.10.2 scala: - 2.11.6 -node_js: - - "6" jdk: - oraclejdk8 script: + - nvm use $NODE_VERSION - sbt stage -DtsCompileMode=stage From aa265adae6f1fb2650b18b2d46c83e653f240e4b Mon Sep 17 00:00:00 2001 From: Joost de Vries Date: Tue, 11 Apr 2017 23:29:28 +0200 Subject: [PATCH 19/22] only use minor version --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index dc45b77..0da91dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ cache: directories: - $HOME/.ivy2 env: - - NODE_VERSION=6.10.2 + - NODE_VERSION=6.10 scala: - 2.11.6 jdk: From 470838396a7906afc90b731dc99bcffe3cd2f53b Mon Sep 17 00:00:00 2001 From: Joost de Vries Date: Tue, 11 Apr 2017 23:32:59 +0200 Subject: [PATCH 20/22] only use major version --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0da91dc..925dfbf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ cache: directories: - $HOME/.ivy2 env: - - NODE_VERSION=6.10 + - NODE_VERSION=6 scala: - 2.11.6 jdk: From 657cd62db80940068659935d2f9ab7f80890d8cd Mon Sep 17 00:00:00 2001 From: Joost de Vries Date: Tue, 11 Apr 2017 23:36:22 +0200 Subject: [PATCH 21/22] back to trusty --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 925dfbf..7fddc31 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +dist: trusty sudo: false language: scala cache: From 9fe2a316e477ff98ea6aa7e82f968406365e4497 Mon Sep 17 00:00:00 2001 From: Joost de Vries Date: Sun, 23 Apr 2017 19:42:26 +0200 Subject: [PATCH 22/22] use tslint 5.1.0 --- app/assets/app/hero-search.component.ts | 2 +- build.sbt | 5 ++++- project/plugins.sbt | 2 +- tslint.json | 6 +++++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app/assets/app/hero-search.component.ts b/app/assets/app/hero-search.component.ts index 3953c8b..6c33fd6 100644 --- a/app/assets/app/hero-search.component.ts +++ b/app/assets/app/hero-search.component.ts @@ -43,7 +43,7 @@ export class HeroSearchComponent implements OnInit { } public gotoDetail(hero: Hero): void { - let link = ['/detail', hero.id]; + const link = ['/detail', hero.id]; this.router.navigate(link); } } diff --git a/build.sbt b/build.sbt index 26d2458..25740fb 100644 --- a/build.sbt +++ b/build.sbt @@ -8,7 +8,10 @@ updateOptions := updateOptions.value.withCachedResolution(cachedResoluton = true //we use nodejs to make our typescript build as fast as possible JsEngineKeys.engineType := JsEngineKeys.EngineType.Node -resolvers += Resolver.jcenterRepo +resolvers ++= Seq( + Resolver.jcenterRepo, + Resolver.bintrayRepo("webjars","maven") +) libraryDependencies ++= { val ngVersion="2.2.0" diff --git a/project/plugins.sbt b/project/plugins.sbt index 7b09517..ef5deee 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -6,7 +6,7 @@ addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.5.10") addSbtPlugin("name.de-vries" % "sbt-typescript" % "0.3.0-beta.11") // checks your typescript code for error prone constructions -addSbtPlugin("name.de-vries" % "sbt-tslint" % "4.4.2") +addSbtPlugin("name.de-vries" % "sbt-tslint" % "5.1.0") addSbtPlugin("com.typesafe.sbt" % "sbt-digest" % "1.1.0") diff --git a/tslint.json b/tslint.json index aeafc56..4f2e5db 100644 --- a/tslint.json +++ b/tslint.json @@ -1,5 +1,8 @@ { "extends": "tslint:recommended", + "lintOptions": { + "fix": true + }, "rules": { /* ng2 rules */ @@ -30,6 +33,7 @@ "quotemark": [true, "single", "avoid-escape"], "ordered-imports": false, "arrow-parens": false, - "object-literal-shorthand": false + "object-literal-shorthand": false, + "import-spacing": false } }