diff --git a/public/docs/_examples/cb-electron/e2e-spec.ts b/public/docs/_examples/cb-electron/e2e-spec.ts new file mode 100644 index 0000000000..96277a910a --- /dev/null +++ b/public/docs/_examples/cb-electron/e2e-spec.ts @@ -0,0 +1,225 @@ +/// +'use strict'; +describe('TOH Http Chapter', function () { + + beforeEach(function () { + browser.get(''); + }); + + function getPageStruct() { + let hrefEles = element.all(by.css('my-app a')); + + return { + hrefs: hrefEles, + myDashboardHref: hrefEles.get(0), + myDashboardParent: element(by.css('my-app my-dashboard')), + topHeroes: element.all(by.css('my-app my-dashboard .module.hero')), + + myHeroesHref: hrefEles.get(1), + myHeroesParent: element(by.css('my-app my-heroes')), + allHeroes: element.all(by.css('my-app my-heroes li .hero-element')), + + firstDeleteButton: element.all(by.buttonText('Delete')).get(0), + + addButton: element.all(by.buttonText('Add New Hero')).get(0), + + heroDetail: element(by.css('my-app my-hero-detail')) + }; + } + + it('should be able to add a hero from the "Heroes" view', function(){ + let page = getPageStruct(); + let heroCount: webdriver.promise.Promise; + + page.myHeroesHref.click().then(function() { + browser.waitForAngular(); + heroCount = page.allHeroes.count(); + expect(heroCount).toBe(10, 'should show 10'); + }).then(function() { + return page.addButton.click(); + }).then(function(){ + return save(page, '', 'The New Hero'); + }).then(function(){ + browser.waitForAngular(); + + heroCount = page.allHeroes.count(); + expect(heroCount).toBe(11, 'should show 11'); + + let newHero = element(by.xpath('//span[@class="hero-element" and contains(text(),"The New Hero")]')); + expect(newHero).toBeDefined(); + }); + }); + + it('should be able to delete hero from "Heroes" view', function(){ + let page = getPageStruct(); + let heroCount: webdriver.promise.Promise; + + page.myHeroesHref.click().then(function() { + browser.waitForAngular(); + heroCount = page.allHeroes.count(); + expect(heroCount).toBe(10, 'should show 10'); + }).then(function() { + return page.firstDeleteButton.click(); + }).then(function(){ + browser.waitForAngular(); + heroCount = page.allHeroes.count(); + expect(heroCount).toBe(9, 'should show 9'); + }); + }); + + it('should be able to save details from "Dashboard" view', function () { + let page = getPageStruct(); + expect(page.myDashboardParent.isPresent()).toBe(true, 'dashboard element should be available'); + let heroEle = page.topHeroes.get(2); + let heroDescrEle = heroEle.element(by.css('h4')); + let heroDescr: string; + + return heroDescrEle.getText().then(function(text) { + heroDescr = text; + return heroEle.click(); + }).then(function() { + return save(page, heroDescr, '-foo'); + }) + .then(function(){ + return page.myDashboardHref.click(); + }) + .then(function() { + expect(page.myDashboardParent.isPresent()).toBe(true, 'dashboard element should be back'); + expect(heroDescrEle.getText()).toEqual(heroDescr + '-foo'); + }); + }); + + it('should be able to save details from "Heroes" view', function () { + let page = getPageStruct(); + + let viewDetailsButtonEle = page.myHeroesParent.element(by.cssContainingText('button', 'View Details')); + let heroEle: protractor.ElementFinder, heroDescr: string; + + page.myHeroesHref.click().then(function() { + expect(page.myDashboardParent.isPresent()).toBe(false, 'dashboard element should NOT be present'); + expect(page.myHeroesParent.isPresent()).toBe(true, 'myHeroes element should be present'); + expect(viewDetailsButtonEle.isPresent()).toBe(false, 'viewDetails button should not yet be present'); + heroEle = page.allHeroes.get(0); + return heroEle.getText(); + }).then(function(text) { + // remove leading 'id' from the element + heroDescr = text.substr(text.indexOf(' ') + 1); + return heroEle.click(); + }).then(function() { + expect(viewDetailsButtonEle.isDisplayed()).toBe(true, 'viewDetails button should now be visible'); + return viewDetailsButtonEle.click(); + }).then(function() { + return save(page, heroDescr, '-bar'); + }) + .then(function(){ + return page.myHeroesHref.click(); + }) + .then(function() { + expect(heroEle.getText()).toContain(heroDescr + '-bar'); + }); + }); + + function save(page: any, origValue: string, textToAdd: string) { + let inputEle = page.heroDetail.element(by.css('input')); + expect(inputEle.isDisplayed()).toBe(true, 'should be able to see the input box'); + let saveButtonEle = page.heroDetail.element(by.buttonText('Save')); + let backButtonEle = page.heroDetail.element(by.buttonText('Back')); + expect(backButtonEle.isDisplayed()).toBe(true, 'should be able to see the back button'); + let detailTextEle = page.heroDetail.element(by.css('div h2')); + expect(detailTextEle.getText()).toContain(origValue); + return sendKeys(inputEle, textToAdd).then(function () { + expect(detailTextEle.getText()).toContain(origValue + textToAdd); + return saveButtonEle.click(); + }); + } + + it('should be able to see the start screen', function () { + let page = getPageStruct(); + expect(page.hrefs.count()).toEqual(2, 'should be two dashboard choices'); + expect(page.myDashboardHref.getText()).toEqual('Dashboard'); + expect(page.myHeroesHref.getText()).toEqual('Heroes'); + }); + + it('should be able to see dashboard choices', function () { + let page = getPageStruct(); + expect(page.topHeroes.count()).toBe(4, 'should be 4 dashboard hero choices'); + }); + + it('should be able to toggle the views', function () { + let page = getPageStruct(); + + expect(page.myDashboardParent.element(by.css('h3')).getText()).toEqual('Top Heroes'); + page.myHeroesHref.click().then(function() { + expect(page.myDashboardParent.isPresent()).toBe(false, 'should no longer see dashboard element'); + expect(page.allHeroes.count()).toBeGreaterThan(4, 'should be more than 4 heroes shown'); + return page.myDashboardHref.click(); + }).then(function() { + expect(page.myDashboardParent.isPresent()).toBe(true, 'should once again see the dashboard element'); + }); + + }); + + it('should be able to edit details from "Dashboard" view', function () { + let page = getPageStruct(); + expect(page.myDashboardParent.isPresent()).toBe(true, 'dashboard element should be available'); + let heroEle = page.topHeroes.get(3); + let heroDescrEle = heroEle.element(by.css('h4')); + let heroDescr: string; + return heroDescrEle.getText().then(function(text) { + heroDescr = text; + return heroEle.click(); + }).then(function() { + return editDetails(page, heroDescr, '-foo'); + }).then(function() { + expect(page.myDashboardParent.isPresent()).toBe(true, 'dashboard element should be back'); + expect(heroDescrEle.getText()).toEqual(heroDescr + '-foo'); + }); + }); + + it('should be able to edit details from "Heroes" view', function () { + let page = getPageStruct(); + expect(page.myDashboardParent.isPresent()).toBe(true, 'dashboard element should be present'); + let viewDetailsButtonEle = page.myHeroesParent.element(by.cssContainingText('button', 'View Details')); + let heroEle: protractor.ElementFinder, heroDescr: string; + page.myHeroesHref.click().then(function() { + expect(page.myDashboardParent.isPresent()).toBe(false, 'dashboard element should NOT be present'); + expect(page.myHeroesParent.isPresent()).toBe(true, 'myHeroes element should be present'); + expect(viewDetailsButtonEle.isPresent()).toBe(false, 'viewDetails button should not yet be present'); + heroEle = page.allHeroes.get(2); + return heroEle.getText(); + }).then(function(text) { + // remove leading 'id' from the element + heroDescr = text.substr(text.indexOf(' ') + 1); + return heroEle.click(); + }).then(function() { + expect(viewDetailsButtonEle.isDisplayed()).toBe(true, 'viewDetails button should now be visible'); + return viewDetailsButtonEle.click(); + }).then(function() { + return editDetails(page, heroDescr, '-bar'); + }).then(function() { + expect(page.myHeroesParent.isPresent()).toBe(true, 'myHeroes element should be back'); + expect(heroEle.getText()).toContain(heroDescr + '-bar'); + expect(viewDetailsButtonEle.isPresent()).toBe(false, 'viewDetails button should again NOT be present'); + }); + }); + + function editDetails(page: any, origValue: string, textToAdd: string) { + expect(page.myDashboardParent.isPresent()).toBe(false, 'dashboard element should NOT be present'); + expect(page.myHeroesParent.isPresent()).toBe(false, 'myHeroes element should NOT be present'); + expect(page.heroDetail.isDisplayed()).toBe(true, 'should be able to see hero-details'); + let inputEle = page.heroDetail.element(by.css('input')); + expect(inputEle.isDisplayed()).toBe(true, 'should be able to see the input box'); + let buttons = page.heroDetail.all(by.css('button')); + let backButtonEle = buttons.get(0); + let saveButtonEle = buttons.get(1); + expect(backButtonEle.isDisplayed()).toBe(true, 'should be able to see the back button'); + expect(saveButtonEle.isDisplayed()).toBe(true, 'should be able to see the save button'); + let detailTextEle = page.heroDetail.element(by.css('div h2')); + expect(detailTextEle.getText()).toContain(origValue); + return sendKeys(inputEle, textToAdd).then(function () { + expect(detailTextEle.getText()).toContain(origValue + textToAdd); + return saveButtonEle.click(); + }); + } + +}); diff --git a/public/docs/_examples/cb-electron/ts/.gitignore b/public/docs/_examples/cb-electron/ts/.gitignore new file mode 100644 index 0000000000..9e76e3f401 --- /dev/null +++ b/public/docs/_examples/cb-electron/ts/.gitignore @@ -0,0 +1,3 @@ +**/*.js + +!index.js \ No newline at end of file diff --git a/public/docs/_examples/cb-electron/ts/app/app.component.css b/public/docs/_examples/cb-electron/ts/app/app.component.css new file mode 100644 index 0000000000..137e9be7be --- /dev/null +++ b/public/docs/_examples/cb-electron/ts/app/app.component.css @@ -0,0 +1,31 @@ +/* #docplaster */ +/* #docregion css */ +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.router-link-active { + color: #039be5; +} +/* #enddocregion css */ diff --git a/public/docs/_examples/cb-electron/ts/app/app.component.ts b/public/docs/_examples/cb-electron/ts/app/app.component.ts new file mode 100644 index 0000000000..22317d4403 --- /dev/null +++ b/public/docs/_examples/cb-electron/ts/app/app.component.ts @@ -0,0 +1,36 @@ +// #docplaster +// #docregion +import { Component } from '@angular/core'; +import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from '@angular/router-deprecated'; + +import { DashboardComponent } from './dashboard.component'; +import { HeroesComponent } from './heroes.component'; +import { HeroDetailComponent } from './hero-detail.component'; +import { HeroService } from './hero.service'; + +@Component({ + selector: 'my-app', + + template: ` +

{{title}}

+ + + `, + styleUrls: ['app/app.component.css'], + directives: [ROUTER_DIRECTIVES], + providers: [ + ROUTER_PROVIDERS, + HeroService, + ] +}) +@RouteConfig([ + { path: '/dashboard', name: 'Dashboard', component: DashboardComponent, useAsDefault: true }, + { path: '/detail/:id', name: 'HeroDetail', component: HeroDetailComponent }, + { path: '/heroes', name: 'Heroes', component: HeroesComponent } +]) +export class AppComponent { + title = 'Tour of Heroes'; +} diff --git a/public/docs/_examples/cb-electron/ts/app/dashboard.component.css b/public/docs/_examples/cb-electron/ts/app/dashboard.component.css new file mode 100644 index 0000000000..ce6e963a5f --- /dev/null +++ b/public/docs/_examples/cb-electron/ts/app/dashboard.component.css @@ -0,0 +1,63 @@ +/* #docplaster */ +/* #docregion */ +[class*='col-'] { + float: left; +} +*, *:after, *:before { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +h3 { + text-align: center; margin-bottom: 0; +} +[class*='col-'] { + padding-right: 20px; + padding-bottom: 20px; +} +[class*='col-']:last-of-type { + padding-right: 0; +} +.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; +} +h4 { + position: relative; +} +.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; + } +} +/* #enddocregion */ diff --git a/public/docs/_examples/cb-electron/ts/app/dashboard.component.html b/public/docs/_examples/cb-electron/ts/app/dashboard.component.html new file mode 100644 index 0000000000..028eab6eb3 --- /dev/null +++ b/public/docs/_examples/cb-electron/ts/app/dashboard.component.html @@ -0,0 +1,11 @@ + +

Top Heroes

+
+ +
+ +
+

{{hero.name}}

+
+
+
diff --git a/public/docs/_examples/cb-electron/ts/app/dashboard.component.ts b/public/docs/_examples/cb-electron/ts/app/dashboard.component.ts new file mode 100644 index 0000000000..8ca1e3a2e2 --- /dev/null +++ b/public/docs/_examples/cb-electron/ts/app/dashboard.component.ts @@ -0,0 +1,32 @@ +// #docplaster +// #docregion +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router-deprecated'; + +import { Hero } from './hero'; +import { HeroService } from './hero.service'; + +@Component({ + selector: 'my-dashboard', + templateUrl: 'app/dashboard.component.html', + styleUrls: ['app/dashboard.component.css'] +}) +export class DashboardComponent implements OnInit { + + heroes: Hero[] = []; + + constructor( + private router: Router, + private heroService: HeroService) { + } + + ngOnInit() { + this.heroService.getHeroes() + .then(heroes => this.heroes = heroes.slice(1, 5)); + } + + gotoDetail(hero: Hero) { + let link = ['HeroDetail', { id: hero.id }]; + this.router.navigate(link); + } +} diff --git a/public/docs/_examples/cb-electron/ts/app/hero-detail.component.css b/public/docs/_examples/cb-electron/ts/app/hero-detail.component.css new file mode 100644 index 0000000000..ab2437efd8 --- /dev/null +++ b/public/docs/_examples/cb-electron/ts/app/hero-detail.component.css @@ -0,0 +1,30 @@ +/* #docregion */ +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; +} diff --git a/public/docs/_examples/cb-electron/ts/app/hero-detail.component.html b/public/docs/_examples/cb-electron/ts/app/hero-detail.component.html new file mode 100644 index 0000000000..f532eb0109 --- /dev/null +++ b/public/docs/_examples/cb-electron/ts/app/hero-detail.component.html @@ -0,0 +1,13 @@ + + +
+

{{hero.name}} details!

+
+ {{hero.id}}
+
+ + +
+ + +
\ No newline at end of file diff --git a/public/docs/_examples/cb-electron/ts/app/hero-detail.component.ts b/public/docs/_examples/cb-electron/ts/app/hero-detail.component.ts new file mode 100644 index 0000000000..bce249dcc1 --- /dev/null +++ b/public/docs/_examples/cb-electron/ts/app/hero-detail.component.ts @@ -0,0 +1,60 @@ +// #docplaster +// #docregion, variables-imports +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; + +// #enddocregion variables-imports +import { RouteParams } from '@angular/router-deprecated'; + +import { Hero } from './hero'; +import { HeroService } from './hero.service'; + +@Component({ + selector: 'my-hero-detail', + templateUrl: 'app/hero-detail.component.html', + styleUrls: ['app/hero-detail.component.css'] +}) +// #docregion variables-imports +export class HeroDetailComponent implements OnInit { + @Input() hero: Hero; + @Output() close = new EventEmitter(); + error: any; + navigated = false; // true if navigated here + // #enddocregion variables-imports + + constructor( + private heroService: HeroService, + private routeParams: RouteParams) { + } + + // #docregion ngOnInit + ngOnInit() { + if (this.routeParams.get('id') !== null) { + let id = +this.routeParams.get('id'); + this.navigated = true; + this.heroService.getHero(id) + .then(hero => this.hero = hero); + } else { + this.navigated = false; + this.hero = new Hero(); + } + } + // #enddocregion ngOnInit + // #docregion save + save() { + this.heroService + .save(this.hero) + .then(hero => { + this.hero = hero; // saved hero, w/ id if new + this.goBack(hero); + }) + .catch(error => this.error = error); // TODO: Display error message + } + // #enddocregion save + // #docregion goback + goBack(savedHero: Hero = null) { + this.close.emit(savedHero); + if (this.navigated) { window.history.back(); } + } + // #enddocregion goback +} + diff --git a/public/docs/_examples/cb-electron/ts/app/hero.service.ts b/public/docs/_examples/cb-electron/ts/app/hero.service.ts new file mode 100644 index 0000000000..fa1e1ac4e4 --- /dev/null +++ b/public/docs/_examples/cb-electron/ts/app/hero.service.ts @@ -0,0 +1,99 @@ +// #docplaster +// #docregion +import { Injectable } from '@angular/core'; +import { Headers, Http } from '@angular/http'; + +// #docregion rxjs +import 'rxjs/add/operator/toPromise'; +// #enddocregion rxjs + +import { Hero } from './hero'; + +@Injectable() +export class HeroService { + + private heroesUrl = 'app/heroes'; // URL to web api + + constructor(private http: Http) { } + + // #docregion get-heroes + getHeroes(): Promise { + return this.http.get(this.heroesUrl) + // #docregion to-promise + .toPromise() + // #enddocregion to-promise + // #docregion to-data + .then(response => response.json().data) + // #enddocregion to-data + // #docregion catch + .catch(this.handleError); + // #enddocregion catch + } + // #enddocregion get-heroes + + getHero(id: number) { + return this.getHeroes() + .then(heroes => heroes.filter(hero => hero.id === id)[0]); + } + + // #docregion save + save(hero: Hero): Promise { + if (hero.id) { + return this.put(hero); + } + return this.post(hero); + } + // #enddocregion save + + // #docregion delete-hero + delete(hero: Hero) { + let headers = new Headers(); + headers.append('Content-Type', 'application/json'); + + let url = `${this.heroesUrl}/${hero.id}`; + + return this.http + .delete(url, headers) + .toPromise() + .catch(this.handleError); + } + // #enddocregion delete-hero + + // #docregion post-hero + // Add new Hero + private post(hero: Hero): Promise { + let headers = new Headers({ + 'Content-Type': 'application/json'}); + + return this.http + .post(this.heroesUrl, JSON.stringify(hero), {headers: headers}) + .toPromise() + .then(res => res.json().data) + .catch(this.handleError); + } + // #enddocregion post-hero + + // #docregion put-hero + // Update existing Hero + private put(hero: Hero) { + let headers = new Headers(); + headers.append('Content-Type', 'application/json'); + + let url = `${this.heroesUrl}/${hero.id}`; + + return this.http + .put(url, JSON.stringify(hero), {headers: headers}) + .toPromise() + .then(() => hero) + .catch(this.handleError); + } + // #enddocregion put-hero + + // #docregion error-handler + private handleError(error: any) { + console.error('An error occurred', error); + return Promise.reject(error.message || error); + } + // #enddocregion error-handler +} +// #enddocregion diff --git a/public/docs/_examples/cb-electron/ts/app/hero.ts b/public/docs/_examples/cb-electron/ts/app/hero.ts new file mode 100644 index 0000000000..e3eac516da --- /dev/null +++ b/public/docs/_examples/cb-electron/ts/app/hero.ts @@ -0,0 +1,4 @@ +export class Hero { + id: number; + name: string; +} diff --git a/public/docs/_examples/cb-electron/ts/app/heroes.component.css b/public/docs/_examples/cb-electron/ts/app/heroes.component.css new file mode 100644 index 0000000000..35e45af98d --- /dev/null +++ b/public/docs/_examples/cb-electron/ts/app/heroes.component.css @@ -0,0 +1,59 @@ +.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; +} diff --git a/public/docs/_examples/cb-electron/ts/app/heroes.component.html b/public/docs/_examples/cb-electron/ts/app/heroes.component.html new file mode 100644 index 0000000000..705c67677f --- /dev/null +++ b/public/docs/_examples/cb-electron/ts/app/heroes.component.html @@ -0,0 +1,26 @@ + +

My Heroes

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

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

+ +
diff --git a/public/docs/_examples/cb-electron/ts/app/heroes.component.ts b/public/docs/_examples/cb-electron/ts/app/heroes.component.ts new file mode 100644 index 0000000000..df3f26c69b --- /dev/null +++ b/public/docs/_examples/cb-electron/ts/app/heroes.component.ts @@ -0,0 +1,71 @@ +// #docregion +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router-deprecated'; + +import { Hero } from './hero'; +import { HeroService } from './hero.service'; +// #docregion hero-detail-component +import { HeroDetailComponent } from './hero-detail.component'; + +@Component({ + selector: 'my-heroes', + templateUrl: 'app/heroes.component.html', + styleUrls: ['app/heroes.component.css'], + directives: [HeroDetailComponent] +}) +// #enddocregion hero-detail-component +export class HeroesComponent implements OnInit { + heroes: Hero[]; + selectedHero: Hero; + addingHero = false; + error: any; + + constructor( + private router: Router, + private heroService: HeroService) { } + + getHeroes() { + this.heroService + .getHeroes() + .then(heroes => this.heroes = heroes) + .catch(error => this.error = error); // TODO: Display error message + } + + // #docregion add + addHero() { + this.addingHero = true; + this.selectedHero = null; + } + + close(savedHero: Hero) { + this.addingHero = false; + if (savedHero) { this.getHeroes(); } + } + // #enddocregion add + + // #docregion delete + delete(hero: Hero, event: any) { + event.stopPropagation(); + this.heroService + .delete(hero) + .then(res => { + this.heroes = this.heroes.filter(h => h !== hero); + if (this.selectedHero === hero) { this.selectedHero = null; } + }) + .catch(error => this.error = error); // TODO: Display error message + } + // #enddocregion delete + + ngOnInit() { + this.getHeroes(); + } + + onSelect(hero: Hero) { + this.selectedHero = hero; + this.addingHero = false; + } + + gotoDetail() { + this.router.navigate(['HeroDetail', { id: this.selectedHero.id }]); + } +} diff --git a/public/docs/_examples/cb-electron/ts/app/in-memory-data.service.ts b/public/docs/_examples/cb-electron/ts/app/in-memory-data.service.ts new file mode 100644 index 0000000000..791b6ae2c5 --- /dev/null +++ b/public/docs/_examples/cb-electron/ts/app/in-memory-data.service.ts @@ -0,0 +1,18 @@ +// #docregion +export class InMemoryDataService { + 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}; + } +} diff --git a/public/docs/_examples/cb-electron/ts/app/main.ts b/public/docs/_examples/cb-electron/ts/app/main.ts new file mode 100644 index 0000000000..958b9a8c69 --- /dev/null +++ b/public/docs/_examples/cb-electron/ts/app/main.ts @@ -0,0 +1,28 @@ +// #docplaster +// #docregion final +// Imports for loading & configuring the in-memory web api +import { XHRBackend } from '@angular/http'; + +import { InMemoryBackendService, SEED_DATA } from 'angular2-in-memory-web-api'; +import { InMemoryDataService } from './in-memory-data.service'; + +// The usual bootstrapping imports +// #docregion v1 +import { bootstrap } from '@angular/platform-browser-dynamic'; +import { HTTP_PROVIDERS } from '@angular/http'; + +import { AppComponent } from './app.component'; + +// #enddocregion v1, final +/* +// #docregion v1 +bootstrap(AppComponent, [ HTTP_PROVIDERS ]); +// #enddocregion v1 + */ +// #docregion final +bootstrap(AppComponent, [ + HTTP_PROVIDERS, + { provide: XHRBackend, useClass: InMemoryBackendService }, // in-mem server + { provide: SEED_DATA, useClass: InMemoryDataService } // in-mem server data +]); +// #enddocregion final diff --git a/public/docs/_examples/cb-electron/ts/example-config.json b/public/docs/_examples/cb-electron/ts/example-config.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/public/docs/_examples/cb-electron/ts/index.html b/public/docs/_examples/cb-electron/ts/index.html new file mode 100644 index 0000000000..082f913c47 --- /dev/null +++ b/public/docs/_examples/cb-electron/ts/index.html @@ -0,0 +1,27 @@ + + + + + Angular 2 Tour of Heroes - Electron + + + + + + + + + + + + + + + + + + Loading... + + diff --git a/public/docs/_examples/cb-electron/ts/index.js b/public/docs/_examples/cb-electron/ts/index.js new file mode 100644 index 0000000000..7de00a7e19 --- /dev/null +++ b/public/docs/_examples/cb-electron/ts/index.js @@ -0,0 +1,53 @@ +// #docregion +//This configuration is a sample from the Electron Quick Start http://electron.atom.io/docs/tutorial/quick-start/ + +const electron = require('electron'); +// Module to control application life. +const {app} = electron; +// Module to create native browser window. +const {BrowserWindow} = electron; + +// Keep a global reference of the window object, if you don't, the window will +// be closed automatically when the JavaScript object is garbage collected. +let win; + +function createWindow() { + // Create the browser window. + win = new BrowserWindow({width: 800, height: 600}); + + // and load the index.html of the app. + win.loadURL(`file://${__dirname}/index.html`); + + // Open the DevTools. + win.webContents.openDevTools(); + + // Emitted when the window is closed. + win.on('closed', () => { + // Dereference the window object, usually you would store windows + // in an array if your app supports multi windows, this is the time + // when you should delete the corresponding element. + win = null; + }); +} + +// This method will be called when Electron has finished +// initialization and is ready to create browser windows. +// Some APIs can only be used after this event occurs. +app.on('ready', createWindow); + +// Quit when all windows are closed. +app.on('window-all-closed', () => { + // On macOS it is common for applications and their menu bar + // to stay active until the user quits explicitly with Cmd + Q + if (process.platform !== 'darwin') { + app.quit(); + } +}); + +app.on('activate', () => { + // On macOS it's common to re-create a window in the app when the + // dock icon is clicked and there are no other windows open. + if (win === null) { + createWindow(); + } +}); diff --git a/public/docs/_examples/cb-electron/ts/sample.css b/public/docs/_examples/cb-electron/ts/sample.css new file mode 100644 index 0000000000..042f0494f6 --- /dev/null +++ b/public/docs/_examples/cb-electron/ts/sample.css @@ -0,0 +1,8 @@ +button.delete-button{ + float:right; + background-color: gray !important; + color:white; +} + + + diff --git a/public/docs/ts/latest/cookbook/toh-electron.jade b/public/docs/ts/latest/cookbook/toh-electron.jade new file mode 100644 index 0000000000..f8a16603c3 --- /dev/null +++ b/public/docs/ts/latest/cookbook/toh-electron.jade @@ -0,0 +1,97 @@ +include ../_util-fns + +:marked + JavaScript has traditionally been a website scripting language, but has become a popular language for desktop and server side development as well. + + In this cookbook we show how to create cross platform dektop applications using Angular and Electron. + + +:marked + ## Table of contents + + [Electron](#electron) + + [Adding Tour of Heroes](#toh) + + [Running the Application](#running-application) + +.l-main-section + +:marked + ## Electron + + Electron provides a runtime envrionment for executing JavaScript in dektop applications. + + We will take our existing Tour of Heroes application and turn it into an Electron dekstop application instead of a traditional web application. + + Before we can get started we have to install the Electron runtime. + + The latest build of Electron can be installed by running `npm install -g electron-prebuilt`. + + Installing `electron-prebuilt` with `-g` makes the installation global, but this is not a requirement. + +.l-main-section + +:marked + ## Adding Tour of Heroes + + It may seem like an ambitious goal to convert an application like Tour of Heroes to a dektop application. The question is, can it be done without making major changes to the existing code? + + Luckily the answer is yes. In fact the code can be ported to Electron nearly without any code changes at all. + + We pick up where Tour of Heroes left of with the following files: + +.filetree + .file angular2-tour-of-heroes + .children + .file app + .children + .file app.component.ts + .file app.component.css + .file dashboard.component.css + .file dashboard.component.html + .file dashboard.component.ts + .file hero.ts + .file hero-detail.component.css + .file hero-detail.component.html + .file hero-detail.component.ts + .file hero.service.ts + .file heroes.component.css + .file heroes.component.html + .file heroes.component.ts + .file main.ts + .file hero-data.service.ts + .file node_modules ... + .file typings ... + .file index.html + .file package.json + .file styles.css + .file sample.css + .file systemjs.config.json + .file tsconfig.json + .file typings.json + +:marked + The only modification we have to make is change the `href` of the base tag to `href="."` from `href="/"` ++makeExample('cb-electron/ts/index.html', 'base', 'ts/index.html')(format=".") + +:marked + The `href` change is necessary since Electron will reference files as physical file paths instead of relative web urls. + +.l-main-section + +:marked + ## Running the Application + + In order to run our Tour of Heroes application we have to create a simple Electron application to host our Angular code. + + We have defined the Electron application in index.js and put it in the same folder as index.html. + ++makeExample('cb-electron/ts/index.js', null, 'index.js')(format=".") + +:marked + We can now launch the Electron application by running `electron` from the command line, and pass it the path to the application as a command line argument. + + If we are starting the application from the same folder as index.html we can simple start it as `electron .` + + \ No newline at end of file