├── .dockerignore ├── .editorconfig ├── .gitignore ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── README.md ├── app ├── app.component.spec.ts.not.yet ├── app.component.ts ├── heroes.json ├── heroes │ ├── hero.component.html │ ├── hero.component.ts │ ├── hero.model.ts │ ├── hero.service.ts │ ├── heroes.component.css │ ├── heroes.component.html │ └── heroes.component.ts └── main.ts ├── e2e-spec.js ├── favicon.ico ├── index.html ├── karma-test-shim.js ├── karma.conf.js ├── package.json ├── protractor.config.js ├── styles.css ├── systemjs.config.js ├── tsconfig.json ├── tslint.json ├── typings.json ├── typings └── typings.d.ts └── wallaby.js /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | 13 | [*.md] 14 | max_line_length = 0 15 | trim_trailing_whitespace = false 16 | 17 | # Indentation override 18 | #[lib/**.js] 19 | #[{package.json,.travis.yml}] 20 | #[**/**.js] 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | typings/** 3 | node_modules 4 | jspm_packages 5 | link-checker-results.txt 6 | **/*npm-debug.log.* 7 | *.js 8 | *.js.map 9 | _test-output 10 | _temp 11 | 12 | !**/*e2e-spec.js 13 | !karma*.js 14 | !protractor*.js 15 | !systemjs.config.js 16 | !typings/typings.d.ts 17 | !wallaby.js 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # 0.2.1 (2016-05-03) 3 | * Angular 2 RC01 version 4 | 5 | 6 | # 0.2.0 (2016-05-02) 7 | * Angular 2 RC0 version 8 | 9 | 10 | # 0.1.17 (2016-04-29) 11 | * update packages 12 | * Angular 2 beta 17 13 | * RxJS 5.0.0-beta.6 14 | * a2-in-memory-web-api 0.1.17 15 | 16 | 17 | # 0.1.16 (2016-04-26) 18 | * update packages 19 | * Angular 2 beta 16 20 | * a2-in-memory-web-api 0.1.6 21 | * protractor 3.3.0 22 | * typings 0.8.1 23 | * zone.js 0.6.12 24 | 25 | * added favicon.ico 26 | 27 | * testing 28 | - updated wallaby.js and karma.conf.js 29 | - updated app.component.spec.ts 30 | 31 | 32 | 33 | # 0.1.15 (2016-04-13) 34 | * Add testing support 35 | * npm scripts 36 | * karma/jasmine 37 | * protractor 38 | 39 | * update packages 40 | * Angular 2 beta 15 41 | * lite-server 2.2.0 42 | * systemjs 0.19.26 43 | * typescript 1.8.10 44 | * typings 0.7.12 45 | 46 | * add run packages 47 | * a2-in-memory-web-api 48 | 49 | * add testing dev-dependency packages 50 | * canonical-path: 0.0.2, 51 | * http-server: ^0.9.0, 52 | * jasmine-core: ~2.4.1, 53 | * karma: ^0.13.22, 54 | * karma-chrome-launcher: ^0.2.3, 55 | * karma-cli: ^0.1.2, 56 | * karma-htmlfile-reporter: ^0.2.2, 57 | * karma-jasmine: ^0.3.8, 58 | * protractor: ^3.2.2, 59 | * rimraf: ^2.5.2 60 | 61 | 62 | # 0.1.14 (2016-04-07) 63 | * update packages 64 | * Angular 2 beta 14 65 | * lite-server 2.2.0 66 | * typings 0.7.12 67 | 68 | 69 | # 0.1.13 (2016-03-31) 70 | * update packages 71 | * Angular 2 beta 13 72 | 73 | 74 | # 0.1.12 (2016-03-23) 75 | * update packages 76 | * Angular 2 beta 12 77 | * zones 0.6.6 78 | * remove es6-promise because no longer needed. 79 | 80 | 81 | # 0.1.11 (2016-03-18) 82 | * update packages 83 | * Angular 2 beta 11 84 | * zones 0.6.4 85 | * typescript 1.8.9 86 | * typings 0.7.9 87 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # To build and run with Docker: 2 | # 3 | # $ docker build -t ng2-quickstart . 4 | # $ docker run -it --rm -p 3000:3000 -p 3001:3001 ng2-quickstart 5 | # 6 | FROM node:latest 7 | 8 | RUN mkdir -p /quickstart /home/nodejs && \ 9 | groupadd -r nodejs && \ 10 | useradd -r -g nodejs -d /home/nodejs -s /sbin/nologin nodejs && \ 11 | chown -R nodejs:nodejs /home/nodejs 12 | 13 | WORKDIR /quickstart 14 | COPY package.json typings.json /quickstart/ 15 | RUN npm install --unsafe-perm=true 16 | 17 | COPY . /quickstart 18 | RUN chown -R nodejs:nodejs /quickstart 19 | USER nodejs 20 | 21 | CMD npm start 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2014-2016 Google, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular 2 Demo from ngConf 2016 2 | 3 | [View the presentation here](https://www.youtube.com/watch?v=WAPQF_GA7Qg&index=2&list=PLOETEcp3DkCq788xapkP_OU-78jhTf68j) 4 | 5 | Clone it and run it! 6 | 7 | ```bash 8 | npm install 9 | npm start 10 | ``` 11 | 12 | 13 | [View the slides here](https://docs.google.com/presentation/d/1-WxJ-gncWVBOyXWJvh4vjhXPns_Ohy6iCzhZ0OL2jes/edit?usp=sharing) 14 | 15 | 16 | Other links: 17 | 18 | - [John's Angular 2 Pluralight Course](http://jpapa.me/a2ps1stlook) 19 | - [Angular 2 App in 17 Minutes Live Coding Demo](https://johnpapa.net/17-minute-angular-2-app/) 20 | - Mobile 21 | - [Ionic 2](http://ionic.io/2) 22 | - [NativeScript](http://www.nativescript.org) 23 | - [React Native](http://facebook.github.io/react-native/) 24 | - [Electron](http://electron.atom.io/) 25 | - [Angular Material](https://material.angular.io/) 26 | - [Angular material demo](https://puppy-love.firebaseapp.com/) 27 | - [TypeScript](http://typescriptlang.org) 28 | - [Angular 2 Docs](https://angular.io/docs/ts/latest/) 29 | - [Angular 2 Tutorial](https://angular.io/docs/ts/latest/tutorial/) 30 | - [Angular 2 CLI](http://cli.angular.io) 31 | - [Angular 2 Codelyzer](https://github.com/mgechev/codelyzer) 32 | - [Code snippets in vs code](http://jpapa.me/a2vscode) 33 | - [Angular 2 Style Guide](http://jpapa.me/ng2styleguide) 34 | - [Angular 2 Workshop in Barcelona with Dan Wahlin](https://johnpapa.net/angular-2-workshop-in-barcelona/) 35 | - [Angular 2 ngFor syntax change](https://johnpapa.net/angular-2-ngfor/) 36 | - [Why TypeScript?](https://johnpapa.net/es5-es2015-typescript/) 37 | -------------------------------------------------------------------------------- /app/app.component.spec.ts.not.yet: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-unused-variable */ 2 | import { AppComponent } from './app.component'; 3 | 4 | import { 5 | expect, it, iit, xit, 6 | describe, ddescribe, xdescribe, 7 | beforeEach, beforeEachProviders, withProviders, 8 | async, inject, TestComponentBuilder 9 | } from 'angular2/testing'; 10 | 11 | import { By } from 'angular2/platform/browser'; 12 | import { provide } from 'angular2/core'; 13 | import { ViewMetadata } from 'angular2/core'; 14 | import { PromiseWrapper } from 'angular2/src/facade/promise'; 15 | 16 | //////// SPECS ///////////// 17 | 18 | /// Delete this 19 | describe('Smoke test', () => { 20 | it('should run a passing test', () => { 21 | expect(true).toEqual(true, 'should pass'); 22 | }); 23 | }); 24 | 25 | describe('AppComponent with new', function () { 26 | it('should instantiate component', () => { 27 | expect(new AppComponent()).toBeDefined('Whoopie!'); 28 | }); 29 | }); 30 | 31 | describe('AppComponent with TCB', function () { 32 | 33 | it('should instantiate component', 34 | async(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { 35 | 36 | tcb.createAsync(AppComponent).then(fixture => { 37 | expect(fixture.componentInstance instanceof AppComponent).toBe(true, 'should create AppComponent'); 38 | }); 39 | }))); 40 | 41 | it('should have expected

text', 42 | async(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { 43 | 44 | tcb.createAsync(AppComponent).then(fixture => { 45 | // fixture.detectChanges(); // would need to resolve a binding but we don't have a binding 46 | 47 | let h1 = fixture.debugElement.query(el => el.name === 'h1').nativeElement; // it works 48 | 49 | h1 = fixture.debugElement.query(By.css('h1')).nativeElement; // preferred 50 | 51 | expect(h1.innerText).toMatch(/angular 2 app/i, '

should say something about "Angular 2 App"'); 52 | }); 53 | 54 | }))); 55 | }); 56 | -------------------------------------------------------------------------------- /app/app.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | 3 | import { HeroesComponent } from './heroes/heroes.component'; 4 | 5 | @Component({ 6 | selector: 'my-app', 7 | template: ` 8 |

{{title}}

9 | 10 | `, 11 | directives: [HeroesComponent] 12 | }) 13 | export class AppComponent { 14 | title = 'An Angular 2 Force Awakens'; 15 | } 16 | -------------------------------------------------------------------------------- /app/heroes.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 11, 4 | "name": "Chewbacca", 5 | "twitter": "@im_chewy" 6 | }, 7 | { 8 | "id": 12, 9 | "name": "Rey", 10 | "twitter": "@rey" 11 | }, 12 | { 13 | "id": 13, 14 | "name": "Finn (FN2187)", 15 | "twitter": "@finn" 16 | }, 17 | { 18 | "id": 14, 19 | "name": "Han Solo", 20 | "twitter": "@i_know" 21 | }, 22 | { 23 | "id": 15, 24 | "name": "Leia Organa", 25 | "twitter": "@organa" 26 | }, 27 | { 28 | "id": 16, 29 | "name": "Luke Skywalker", 30 | "twitter": "@chosen_one_son" 31 | }, 32 | { 33 | "id": 17, 34 | "name": "Poe Dameron", 35 | "twitter": "@i_am_poe" 36 | }, 37 | { 38 | "id": 18, 39 | "name": "Kylo Ren", 40 | "twitter": "@daddy_issues" 41 | }, 42 | { 43 | "id": 19, 44 | "name": "Supreme Commander Snoke", 45 | "twitter": "@snoker" 46 | }, 47 | { 48 | "id": 20, 49 | "name": "R2-D2", 50 | "twitter": "@r2d2" 51 | }, 52 | { 53 | "id": 21, 54 | "name": "BB8", 55 | "twitter": "@bb_eight" 56 | } 57 | ] 58 | -------------------------------------------------------------------------------- /app/heroes/hero.component.html: -------------------------------------------------------------------------------- 1 |
2 | We selected 3 |
4 | -------------------------------------------------------------------------------- /app/heroes/hero.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | 3 | import { Hero } from './hero.model'; 4 | 5 | @Component({ 6 | moduleId: module.id, 7 | selector: 'toh-hero', 8 | templateUrl: 'hero.component.html' 9 | }) 10 | export class HeroComponent { 11 | @Input() hero: Hero; 12 | } 13 | -------------------------------------------------------------------------------- /app/heroes/hero.model.ts: -------------------------------------------------------------------------------- 1 | export class Hero { 2 | id: number; 3 | name: string; 4 | } 5 | -------------------------------------------------------------------------------- /app/heroes/hero.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { Http, Response } from '@angular/http'; 4 | import 'rxjs/add/operator/map'; 5 | 6 | @Injectable() 7 | export class HeroService { 8 | constructor(private http: Http) { } 9 | 10 | getHeroes() { 11 | return this.http.get('app/heroes.json') 12 | .map((res: Response) => res.json()); 13 | 14 | // return [ 15 | // { 'id': 11, 'name': 'Chewbacca' }, 16 | // { 'id': 12, 'name': 'Rey' }, 17 | // { 'id': 13, 'name': 'Finn (FN2187)' }, 18 | // { 'id': 14, 'name': 'Han Solo' }, 19 | // { 'id': 15, 'name': 'Leia Organa' } 20 | // ]; 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/heroes/heroes.component.css: -------------------------------------------------------------------------------- 1 | .selected { 2 | background-color: #a62e5c !important; 3 | color: white; 4 | } 5 | .heroes { 6 | margin: 0 0 2em 0; 7 | list-style-type: none; 8 | padding: 0; 9 | width: 15em; 10 | } 11 | .heroes li { 12 | cursor: pointer; 13 | position: relative; 14 | left: 0; 15 | background-color: #EEE; 16 | margin: .5em; 17 | padding: 0.3em 0.3em 0.3em 0; 18 | height: 1.6em; 19 | border-radius: 4px; 20 | text-overflow: ellipsis; 21 | overflow: hidden; 22 | white-space: nowrap; 23 | } 24 | .heroes li.selected:hover { 25 | background-color: #792e5c !important; 26 | color: white; 27 | } 28 | .heroes li:hover { 29 | color: #a62e5c; 30 | background-color: #DDD; 31 | left: .1em; 32 | } 33 | .heroes .text { 34 | position: relative; 35 | top: -3px; 36 | } 37 | .heroes .badge { 38 | display: inline-block; 39 | font-size: small; 40 | color: white; 41 | padding: 0.8em 0.7em 0 0.7em; 42 | background-color: #a62e5c; 43 | line-height: 1em; 44 | position: relative; 45 | left: -1px; 46 | top: -4px; 47 | height: 1.8em; 48 | margin-right: .8em; 49 | border-radius: 4px 0 0 4px; 50 | } 51 | -------------------------------------------------------------------------------- /app/heroes/heroes.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /app/heroes/heroes.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | import { Hero } from './hero.model'; 4 | import { HeroComponent } from './hero.component'; 5 | import { HeroService } from './hero.service'; 6 | 7 | @Component({ 8 | moduleId: module.id, 9 | selector: 'toh-heroes', 10 | templateUrl: 'heroes.component.html', 11 | styleUrls: ['heroes.component.css'], 12 | directives: [HeroComponent], 13 | providers: [HeroService] 14 | }) 15 | export class HeroesComponent implements OnInit { 16 | heroes: Hero[]; 17 | selectedHero: Hero; 18 | 19 | constructor(private heroService: HeroService) { } 20 | 21 | ngOnInit() { 22 | this.heroService.getHeroes() 23 | .subscribe(heroes => this.heroes = heroes); 24 | 25 | // this.heroes = this.heroService.getHeroes(); 26 | 27 | // this.heroes = [ 28 | // { 'id': 11, 'name': 'Chewbacca' }, 29 | // { 'id': 12, 'name': 'Rey' }, 30 | // { 'id': 13, 'name': 'Finn (FN2187)' }, 31 | // { 'id': 14, 'name': 'Han Solo' }, 32 | // { 'id': 15, 'name': 'Leia Organa' } 33 | // ]; 34 | } 35 | 36 | onSelect(hero: Hero){ 37 | this.selectedHero = hero; 38 | } 39 | 40 | } 41 | 42 | 43 | -------------------------------------------------------------------------------- /app/main.ts: -------------------------------------------------------------------------------- 1 | import {bootstrap} from '@angular/platform-browser-dynamic'; 2 | import { HTTP_PROVIDERS } from '@angular/http'; 3 | 4 | import {AppComponent} from './app.component'; 5 | 6 | bootstrap(AppComponent, [HTTP_PROVIDERS]); 7 | -------------------------------------------------------------------------------- /e2e-spec.js: -------------------------------------------------------------------------------- 1 | 2 | describe('QuickStart E2E Tests', function () { 3 | 4 | var expectedMsg = 'My First Angular 2 App'; 5 | 6 | 7 | beforeEach(function () { 8 | browser.get(''); 9 | }); 10 | 11 | it('should display: ' + expectedMsg, function () { 12 | expect(element(by.css('h1')).getText()).toEqual(expectedMsg); 13 | }); 14 | 15 | }); 16 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnpapa/angular2-force/54d3e87c8efa9e1672af76d703650dfb488133ba/favicon.ico -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Angular 2 QuickStart 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | 21 | 22 | Loading... 23 | 24 | 25 | -------------------------------------------------------------------------------- /karma-test-shim.js: -------------------------------------------------------------------------------- 1 | /*global jasmine, __karma__, window*/ 2 | (function () { 3 | 4 | // Error.stackTraceLimit = Infinity; 5 | 6 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000; 7 | 8 | // Cancel Karma's synchronous start, 9 | // we call `__karma__.start()` later, once all the specs are loaded. 10 | __karma__.loaded = function () { }; 11 | 12 | // SET THE RUNTIME APPLICATION ROOT HERE 13 | var appRoot ='app'; // no trailing slash! 14 | 15 | // RegExp for client application base path within karma (which always starts 'base\') 16 | var karmaBase = '^\/base\/'; // RegEx string for base of karma folders 17 | var appPackage = 'base/' + appRoot; //e.g., base/app 18 | var appRootRe = new RegExp(karmaBase + appRoot + '\/'); 19 | var onlyAppFilesRe = new RegExp(karmaBase + appRoot + '\/(?!.*\.spec\.js$)([a-z0-9-_\.\/]+)\.js$'); 20 | 21 | var moduleNames = []; 22 | 23 | // Configure systemjs packages to use the .js extension for imports from the app folder 24 | var packages = {}; 25 | packages[appPackage] = { 26 | defaultExtension: false, 27 | format: 'register', 28 | map: Object.keys(window.__karma__.files) 29 | .filter(onlyAppFiles) 30 | // Create local module name mapping to karma file path for app files 31 | // with karma's fingerprint in query string, e.g.: 32 | // './hero.service': '/base/app/hero.service.js?f4523daf879cfb7310ef6242682ccf10b2041b3e' 33 | .reduce(function (pathsMapping, appPath) { 34 | var moduleName = appPath.replace(appRootRe, './').replace(/\.js$/, ''); 35 | pathsMapping[moduleName] = appPath + '?' + window.__karma__.files[appPath]; 36 | return pathsMapping; 37 | }, {}) 38 | } 39 | 40 | System.config({ packages: packages }); 41 | 42 | // Configure Angular for the browser and 43 | // with test versions of the platform providers 44 | Promise.all([ 45 | System.import('angular2/testing'), 46 | System.import('angular2/platform/testing/browser') 47 | ]) 48 | .then(function (results) { 49 | var testing = results[0]; 50 | var browser = results[1]; 51 | testing.setBaseTestProviders( 52 | browser.TEST_BROWSER_PLATFORM_PROVIDERS, 53 | browser.TEST_BROWSER_APPLICATION_PROVIDERS); 54 | 55 | // Load all spec files 56 | // (e.g. 'base/app/hero.service.spec.js') 57 | return Promise.all( 58 | Object.keys(window.__karma__.files) 59 | .filter(onlySpecFiles) 60 | .map(function (moduleName) { 61 | moduleNames.push(moduleName); 62 | return System.import(moduleName); 63 | })); 64 | }) 65 | 66 | .then(success, fail); 67 | 68 | ////// Helpers ////// 69 | 70 | function onlyAppFiles(filePath) { 71 | return onlyAppFilesRe.test(filePath); 72 | } 73 | 74 | function onlySpecFiles(filePath) { 75 | return /\.spec\.js$/.test(filePath); 76 | } 77 | 78 | function success () { 79 | console.log( 80 | 'Spec files loaded:\n ' + 81 | moduleNames.join('\n ') + 82 | '\nStarting Jasmine testrunner'); 83 | __karma__.start(); 84 | } 85 | 86 | function fail(error) { 87 | __karma__.error(error.stack || error); 88 | } 89 | 90 | })(); 91 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | 3 | var appBase = 'app/'; // transpiled app JS files 4 | var appAssets ='/base/app/'; // component assets fetched by Angular's compiler 5 | 6 | config.set({ 7 | basePath: '', 8 | frameworks: ['jasmine'], 9 | plugins: [ 10 | require('karma-jasmine'), 11 | require('karma-chrome-launcher'), 12 | require('karma-htmlfile-reporter') 13 | ], 14 | 15 | customLaunchers: { 16 | // From the CLI. Not used here but interesting 17 | // chrome setup for travis CI using chromium 18 | Chrome_travis_ci: { 19 | base: 'Chrome', 20 | flags: ['--no-sandbox'] 21 | } 22 | }, 23 | files: [ 24 | // System.js for module loading 25 | 'node_modules/systemjs/dist/system-polyfills.js', 26 | 'node_modules/systemjs/dist/system.src.js', 27 | 28 | // Polyfills 29 | 'node_modules/es6-shim/es6-shim.js', 30 | 'node_modules/angular2/bundles/angular2-polyfills.js', 31 | 32 | // Zone.js dependencies 33 | // Note - do not include zone.js itself or long-stack-trace-zone.js` here as 34 | // they are included already in angular2-polyfills 35 | 'node_modules/zone.js/dist/jasmine-patch.js', 36 | 'node_modules/zone.js/dist/async-test.js', 37 | 'node_modules/zone.js/dist/fake-async-test.js', 38 | 39 | // RxJs 40 | 'node_modules/rxjs/bundles/Rx.js', 41 | 42 | // Angular 2 itself and the testing library 43 | 'node_modules/angular2/bundles/angular2.js', 44 | 'node_modules/angular2/bundles/router.dev.js', 45 | 'node_modules/angular2/bundles/http.dev.js', 46 | 'node_modules/angular2/bundles/testing.dev.js', 47 | 48 | 'karma-test-shim.js', 49 | 50 | // transpiled application & spec code paths loaded via module imports 51 | {pattern: appBase + '**/*.js', included: false, watched: true}, 52 | 53 | // asset (HTML & CSS) paths loaded via Angular's component compiler 54 | // (these paths need to be rewritten, see proxies section) 55 | {pattern: appBase + '**/*.html', included: false, watched: true}, 56 | {pattern: appBase + '**/*.css', included: false, watched: true}, 57 | 58 | // paths for debugging with source maps in dev tools 59 | {pattern: appBase + '**/*.ts', included: false, watched: false}, 60 | {pattern: appBase + '**/*.js.map', included: false, watched: false} 61 | ], 62 | 63 | // proxied base paths for loading assets 64 | proxies: { 65 | // required for component assets fetched by Angular's compiler 66 | "/app/": appAssets 67 | }, 68 | 69 | exclude: [], 70 | preprocessors: {}, 71 | reporters: ['progress', 'html'], 72 | 73 | // HtmlReporter configuration 74 | htmlReporter: { 75 | // Open this file to see results in browser 76 | outputFile: '_test-output/tests.html', 77 | 78 | // Optional 79 | pageTitle: 'Unit Tests', 80 | subPageTitle: __dirname 81 | }, 82 | 83 | port: 9876, 84 | colors: true, 85 | logLevel: config.LOG_INFO, 86 | autoWatch: true, 87 | browsers: ['Chrome'], 88 | singleRun: false 89 | }) 90 | } 91 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular2-quickstart", 3 | "version": "1.0.0", 4 | "description": "QuickStart package.json from the documentation, supplemented with testing support", 5 | "scripts": { 6 | "start": "tsc && concurrently \"tsc -w\" \"lite-server\" ", 7 | "docker-build": "docker build -t ng2-quickstart .", 8 | "docker": "npm run docker-build && docker run -it --rm -p 3000:3000 -p 3001:3001 ng2-quickstart", 9 | "e2e": "tsc && concurrently \"http-server\" \"protractor protractor.config.js\"", 10 | "lint": "tslint ./app/**/*.ts -t verbose", 11 | "lite": "lite-server", 12 | "postinstall": "typings install", 13 | "test": "tsc && concurrently \"tsc -w\" \"karma start karma.conf.js\"", 14 | "tsc": "tsc", 15 | "tsc:w": "tsc -w", 16 | "typings": "typings", 17 | "webdriver:update": "webdriver-manager update" 18 | }, 19 | "keywords": [], 20 | "author": "", 21 | "license": "ISC", 22 | "dependencies": { 23 | "@angular/common": "2.0.0-rc.1", 24 | "@angular/compiler": "2.0.0-rc.1", 25 | "@angular/core": "2.0.0-rc.1", 26 | "@angular/http": "2.0.0-rc.1", 27 | "@angular/platform-browser": "2.0.0-rc.1", 28 | "@angular/platform-browser-dynamic": "2.0.0-rc.1", 29 | "@angular/router": "2.0.0-rc.1", 30 | "@angular/router-deprecated": "2.0.0-rc.1", 31 | "@angular/upgrade": "2.0.0-rc.1", 32 | 33 | "systemjs": "0.19.27", 34 | "es6-shim": "^0.35.0", 35 | "reflect-metadata": "^0.1.3", 36 | "rxjs": "5.0.0-beta.6", 37 | "zone.js": "^0.6.12", 38 | 39 | "angular2-in-memory-web-api": "0.0.7", 40 | "bootstrap": "^3.3.6" 41 | }, 42 | "devDependencies": { 43 | "concurrently": "^2.0.0", 44 | "lite-server": "^2.2.0", 45 | "typescript": "^1.8.10", 46 | "typings": "^0.8.1", 47 | 48 | "canonical-path": "0.0.2", 49 | "http-server": "^0.9.0", 50 | "tslint": "^3.7.4", 51 | "lodash": "^4.11.1", 52 | "jasmine-core": "~2.4.1", 53 | "karma": "^0.13.22", 54 | "karma-chrome-launcher": "^0.2.3", 55 | "karma-cli": "^0.1.2", 56 | "karma-htmlfile-reporter": "^0.2.2", 57 | "karma-jasmine": "^0.3.8", 58 | "protractor": "^3.3.0", 59 | "rimraf": "^2.5.2" 60 | }, 61 | "repository": {} 62 | } 63 | -------------------------------------------------------------------------------- /protractor.config.js: -------------------------------------------------------------------------------- 1 | // FIRST TIME ONLY- run: 2 | // ./node_modules/.bin/webdriver-manager update 3 | // 4 | // Try: `npm run webdriver:update` 5 | // 6 | // AND THEN EVERYTIME ... 7 | // 1. Compile with `tsc` 8 | // 2. Make sure the test server (e.g., http-server: localhost:8080) is running. 9 | // 3. ./node_modules/.bin/protractor protractor.config.js 10 | // 11 | // To do all steps, try: `npm run e2e` 12 | 13 | var fs = require('fs'); 14 | var path = require('canonical-path'); 15 | var _ = require('lodash'); 16 | 17 | 18 | exports.config = { 19 | directConnect: true, 20 | 21 | // Capabilities to be passed to the webdriver instance. 22 | capabilities: { 23 | 'browserName': 'chrome' 24 | }, 25 | 26 | // Framework to use. Jasmine is recommended. 27 | framework: 'jasmine', 28 | 29 | // Spec patterns are relative to this config file 30 | specs: ['**/*e2e-spec.js' ], 31 | 32 | 33 | // For angular2 tests 34 | useAllAngular2AppRoots: true, 35 | 36 | // Base URL for application server 37 | baseUrl: 'http://localhost:8080', 38 | 39 | // doesn't seem to work. 40 | // resultJsonOutputFile: "foo.json", 41 | 42 | onPrepare: function() { 43 | //// SpecReporter 44 | //var SpecReporter = require('jasmine-spec-reporter'); 45 | //jasmine.getEnv().addReporter(new SpecReporter({displayStacktrace: 'none'})); 46 | //// jasmine.getEnv().addReporter(new SpecReporter({displayStacktrace: 'all'})); 47 | 48 | // debugging 49 | // console.log('browser.params:' + JSON.stringify(browser.params)); 50 | jasmine.getEnv().addReporter(new Reporter( browser.params )) ; 51 | 52 | global.sendKeys = sendKeys; 53 | 54 | // Allow changing bootstrap mode to NG1 for upgrade tests 55 | global.setProtractorToNg1Mode = function() { 56 | browser.useAllAngular2AppRoots = false; 57 | browser.rootEl = 'body'; 58 | }; 59 | }, 60 | 61 | jasmineNodeOpts: { 62 | // defaultTimeoutInterval: 60000, 63 | defaultTimeoutInterval: 10000, 64 | showTiming: true, 65 | print: function() {} 66 | } 67 | }; 68 | 69 | // Hack - because of bug with protractor send keys 70 | function sendKeys(element, str) { 71 | return str.split('').reduce(function (promise, char) { 72 | return promise.then(function () { 73 | return element.sendKeys(char); 74 | }); 75 | }, element.getAttribute('value')); 76 | // better to create a resolved promise here but ... don't know how with protractor; 77 | } 78 | 79 | // Custom reporter 80 | function Reporter(options) { 81 | var _defaultOutputFile = path.resolve(process.cwd(), './_test-output', 'protractor-results.txt'); 82 | options.outputFile = options.outputFile || _defaultOutputFile; 83 | 84 | initOutputFile(options.outputFile); 85 | options.appDir = options.appDir || './'; 86 | var _root = { appDir: options.appDir, suites: [] }; 87 | log('AppDir: ' + options.appDir, +1); 88 | var _currentSuite; 89 | 90 | this.suiteStarted = function(suite) { 91 | _currentSuite = { description: suite.description, status: null, specs: [] }; 92 | _root.suites.push(_currentSuite); 93 | log('Suite: ' + suite.description, +1); 94 | }; 95 | 96 | this.suiteDone = function(suite) { 97 | var statuses = _currentSuite.specs.map(function(spec) { 98 | return spec.status; 99 | }); 100 | statuses = _.uniq(statuses); 101 | var status = statuses.indexOf('failed') >= 0 ? 'failed' : statuses.join(', '); 102 | _currentSuite.status = status; 103 | log('Suite ' + _currentSuite.status + ': ' + suite.description, -1); 104 | }; 105 | 106 | this.specStarted = function(spec) { 107 | 108 | }; 109 | 110 | this.specDone = function(spec) { 111 | var currentSpec = { 112 | description: spec.description, 113 | status: spec.status 114 | }; 115 | if (spec.failedExpectations.length > 0) { 116 | currentSpec.failedExpectations = spec.failedExpectations; 117 | } 118 | 119 | _currentSuite.specs.push(currentSpec); 120 | log(spec.status + ' - ' + spec.description); 121 | }; 122 | 123 | this.jasmineDone = function() { 124 | outputFile = options.outputFile; 125 | //// Alternate approach - just stringify the _root - not as pretty 126 | //// but might be more useful for automation. 127 | // var output = JSON.stringify(_root, null, 2); 128 | var output = formatOutput(_root); 129 | fs.appendFileSync(outputFile, output); 130 | }; 131 | 132 | function initOutputFile(outputFile) { 133 | var header = "Protractor results for: " + (new Date()).toLocaleString() + "\n\n"; 134 | fs.writeFileSync(outputFile, header); 135 | } 136 | 137 | // for output file output 138 | function formatOutput(output) { 139 | var indent = ' '; 140 | var pad = ' '; 141 | var results = []; 142 | results.push('AppDir:' + output.appDir); 143 | output.suites.forEach(function(suite) { 144 | results.push(pad + 'Suite: ' + suite.description + ' -- ' + suite.status); 145 | pad+=indent; 146 | suite.specs.forEach(function(spec) { 147 | results.push(pad + spec.status + ' - ' + spec.description); 148 | if (spec.failedExpectations) { 149 | pad+=indent; 150 | spec.failedExpectations.forEach(function (fe) { 151 | results.push(pad + 'message: ' + fe.message); 152 | }); 153 | pad=pad.substr(2); 154 | } 155 | }); 156 | pad = pad.substr(2); 157 | results.push(''); 158 | }); 159 | results.push(''); 160 | return results.join('\n'); 161 | } 162 | 163 | // for console output 164 | var _pad; 165 | function log(str, indent) { 166 | _pad = _pad || ''; 167 | if (indent == -1) { 168 | _pad = _pad.substr(2); 169 | } 170 | console.log(_pad + str); 171 | if (indent == 1) { 172 | _pad = _pad + ' '; 173 | } 174 | } 175 | 176 | } 177 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | /* Master Styles */ 2 | h1 { 3 | color: #444; 4 | font-family: Arial, Helvetica, sans-serif; 5 | font-size: 250%; 6 | } 7 | h2, h3 { 8 | color: #369; 9 | font-family: Arial, Helvetica, sans-serif; 10 | font-weight: lighter; 11 | } 12 | body { 13 | margin: 2em; 14 | } 15 | body, input[text], button { 16 | color: #888; 17 | font-family: Cambria, Georgia; 18 | } 19 | a { 20 | cursor: pointer; 21 | cursor: hand; 22 | } 23 | button { 24 | font-family: Arial; 25 | background-color: #eee; 26 | border: none; 27 | padding: 5px 10px; 28 | border-radius: 4px; 29 | cursor: pointer; 30 | cursor: hand; 31 | } 32 | button:hover { 33 | background-color: #cfd8dc; 34 | } 35 | button:disabled { 36 | background-color: #eee; 37 | color: #aaa; 38 | cursor: auto; 39 | } 40 | 41 | /* Navigation link styles */ 42 | nav a { 43 | padding: 5px 10px; 44 | text-decoration: none; 45 | margin-top: 10px; 46 | display: inline-block; 47 | background-color: #eee; 48 | border-radius: 4px; 49 | } 50 | nav a:visited, a:link { 51 | color: #607D8B; 52 | } 53 | nav a:hover { 54 | color: #039be5; 55 | background-color: #CFD8DC; 56 | } 57 | nav a.router-link-active { 58 | color: #039be5; 59 | } 60 | 61 | /* items class */ 62 | .items { 63 | margin: 0 0 2em 0; 64 | list-style-type: none; 65 | padding: 0; 66 | width: 24em; 67 | } 68 | .items li { 69 | cursor: pointer; 70 | position: relative; 71 | left: 0; 72 | background-color: #EEE; 73 | margin: .5em; 74 | padding: .3em 0; 75 | height: 1.6em; 76 | border-radius: 4px; 77 | } 78 | .items li:hover { 79 | color: #607D8B; 80 | background-color: #DDD; 81 | left: .1em; 82 | } 83 | .items li.selected:hover { 84 | background-color: #BBD8DC; 85 | color: white; 86 | } 87 | .items .text { 88 | position: relative; 89 | top: -3px; 90 | } 91 | .items { 92 | margin: 0 0 2em 0; 93 | list-style-type: none; 94 | padding: 0; 95 | width: 24em; 96 | } 97 | .items li { 98 | cursor: pointer; 99 | position: relative; 100 | left: 0; 101 | background-color: #EEE; 102 | margin: .5em; 103 | padding: .3em 0; 104 | height: 1.6em; 105 | border-radius: 4px; 106 | } 107 | .items li:hover { 108 | color: #607D8B; 109 | background-color: #DDD; 110 | left: .1em; 111 | } 112 | .items li.selected { 113 | background-color: #CFD8DC; 114 | color: white; 115 | } 116 | 117 | .items li.selected:hover { 118 | background-color: #BBD8DC; 119 | } 120 | .items .text { 121 | position: relative; 122 | top: -3px; 123 | } 124 | .items .badge { 125 | display: inline-block; 126 | font-size: small; 127 | color: white; 128 | padding: 0.8em 0.7em 0 0.7em; 129 | background-color: #607D8B; 130 | line-height: 1em; 131 | position: relative; 132 | left: -1px; 133 | top: -4px; 134 | height: 1.8em; 135 | margin-right: .8em; 136 | border-radius: 4px 0 0 4px; 137 | } 138 | 139 | /* everywhere else */ 140 | * { 141 | font-family: Arial, Helvetica, sans-serif; 142 | } 143 | 144 | input { 145 | line-height: 24px; 146 | font-size: 18px; 147 | border: 1px solid lightgray; 148 | border-left: rgba(35,115,23,0.64) 4px solid; 149 | padding: 6px; 150 | width: 280px; 151 | } 152 | -------------------------------------------------------------------------------- /systemjs.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * System configuration for Angular 2 samples 3 | * Adjust as necessary for your application needs. 4 | * Override at the last minute with global.filterSystemConfig (as plunkers do) 5 | */ 6 | (function(global) { 7 | 8 | // map tells the System loader where to look for things 9 | var map = { 10 | 'app': 'app', // 'dist', 11 | 'rxjs': 'node_modules/rxjs', 12 | 'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api', 13 | '@angular': 'node_modules/@angular' 14 | }; 15 | 16 | // packages tells the System loader how to load when no filename and/or no extension 17 | var packages = { 18 | 'app': { main: 'main.js', defaultExtension: 'js' }, 19 | 'rxjs': { defaultExtension: 'js' }, 20 | 'angular2-in-memory-web-api': { defaultExtension: 'js' }, 21 | }; 22 | 23 | var packageNames = [ 24 | '@angular/common', 25 | '@angular/compiler', 26 | '@angular/core', 27 | '@angular/http', 28 | '@angular/platform-browser', 29 | '@angular/platform-browser-dynamic', 30 | '@angular/router-deprecated', 31 | '@angular/testing', 32 | '@angular/upgrade', 33 | ]; 34 | 35 | // add package entries for angular packages in the form '@angular/common': { main: 'index.js', defaultExtension: 'js' } 36 | packageNames.forEach(function(pkgName) { 37 | packages[pkgName] = { main: 'index.js', defaultExtension: 'js' }; 38 | }); 39 | 40 | var config = { 41 | map: map, 42 | packages: packages 43 | } 44 | 45 | System.config(config); 46 | 47 | })(this); 48 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "sourceMap": true, 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true, 9 | "removeComments": false, 10 | "noImplicitAny": true, 11 | "suppressImplicitAnyIndexErrors": true 12 | }, 13 | "exclude": [ 14 | "node_modules", 15 | "typings/main", 16 | "typings/main.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "class-name": true, 4 | "comment-format": [ 5 | true, 6 | "check-space" 7 | ], 8 | "curly": true, 9 | "eofline": true, 10 | "forin": true, 11 | "indent": [ 12 | true, 13 | "spaces" 14 | ], 15 | "label-position": true, 16 | "label-undefined": true, 17 | "max-line-length": [ 18 | true, 19 | 140 20 | ], 21 | "member-access": false, 22 | "member-ordering": [ 23 | true, 24 | "static-before-instance", 25 | "variables-before-functions" 26 | ], 27 | "no-arg": true, 28 | "no-bitwise": true, 29 | "no-console": [ 30 | true, 31 | "debug", 32 | "info", 33 | "time", 34 | "timeEnd", 35 | "trace" 36 | ], 37 | "no-construct": true, 38 | "no-debugger": true, 39 | "no-duplicate-key": true, 40 | "no-duplicate-variable": true, 41 | "no-empty": false, 42 | "no-eval": true, 43 | "no-inferrable-types": true, 44 | "no-shadowed-variable": true, 45 | "no-string-literal": false, 46 | "no-switch-case-fall-through": true, 47 | "no-trailing-whitespace": true, 48 | "no-unused-expression": true, 49 | "no-unused-variable": true, 50 | "no-unreachable": true, 51 | "no-use-before-declare": true, 52 | "no-var-keyword": true, 53 | "object-literal-sort-keys": false, 54 | "one-line": [ 55 | true, 56 | "check-open-brace", 57 | "check-catch", 58 | "check-else", 59 | "check-whitespace" 60 | ], 61 | "quotemark": [ 62 | true, 63 | "single" 64 | ], 65 | "radix": true, 66 | "semicolon": [ 67 | "always" 68 | ], 69 | "triple-equals": [ 70 | true, 71 | "allow-null-check" 72 | ], 73 | "typedef-whitespace": [ 74 | true, 75 | { 76 | "call-signature": "nospace", 77 | "index-signature": "nospace", 78 | "parameter": "nospace", 79 | "property-declaration": "nospace", 80 | "variable-declaration": "nospace" 81 | } 82 | ], 83 | "variable-name": false, 84 | "whitespace": [ 85 | true, 86 | "check-branch", 87 | "check-decl", 88 | "check-operator", 89 | "check-separator", 90 | "check-type" 91 | ] 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ambientDependencies": { 3 | "es6-shim": "registry:dt/es6-shim#0.31.2+20160317120654", 4 | "jasmine": "registry:dt/jasmine#2.2.0+20160412134438" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /typings/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare var module: {id: string}; 2 | -------------------------------------------------------------------------------- /wallaby.js: -------------------------------------------------------------------------------- 1 | // Configuration for the Wallaby Visual Studio Code testing extension 2 | // https://marketplace.visualstudio.com/items?itemName=WallabyJs.wallaby-vscode 3 | // Note: Wallaby is not open source and costs money 4 | module.exports = function () { 5 | 6 | return { 7 | files: [ 8 | // System.js for module loading 9 | {pattern: 'node_modules/systemjs/dist/system-polyfills.js', instrument: false}, 10 | {pattern: 'node_modules/systemjs/dist/system.js', instrument: false}, 11 | 12 | // Polyfills 13 | {pattern: 'node_modules/es6-shim/es6-shim.js', instrument: false}, 14 | {pattern: 'node_modules/angular2/bundles/angular2-polyfills.js', instrument: false}, 15 | 16 | // Zone.js dependencies 17 | // Note - do not include zone.js itself or long-stack-trace-zone.js` here as 18 | // they are included already in angular2-polyfills 19 | {pattern: 'node_modules/zone.js/dist/jasmine-patch.js', instrument: false}, 20 | {pattern: 'node_modules/zone.js/dist/async-test.js', instrument: false}, 21 | {pattern: 'node_modules/zone.js/dist/fake-async-test.js', instrument: false}, 22 | 23 | // Rx.js, Angular 2 itself, and the testing library not here because loaded by systemjs 24 | 25 | {pattern: 'app/**/*+(ts|html|css)', load: false}, 26 | {pattern: 'app/**/*.spec.ts', ignore: true} 27 | ], 28 | 29 | tests: [ 30 | {pattern: 'app/**/*.spec.ts', load: false} 31 | ], 32 | 33 | middleware: function (app, express) { 34 | app.use('/node_modules', express.static(require('path').join(__dirname, 'node_modules'))); 35 | }, 36 | 37 | testFramework: 'jasmine', 38 | 39 | bootstrap: function (wallaby) { 40 | wallaby.delayStart(); 41 | 42 | System.config({ 43 | defaultJSExtensions: true, 44 | packages: { 45 | app: { 46 | meta: { 47 | '*': { 48 | scriptLoad: true 49 | } 50 | } 51 | } 52 | }, 53 | paths: { 54 | 'npm:*': 'node_modules/*' 55 | }, 56 | map: { 57 | 'angular2': 'npm:angular2', 58 | 'rxjs': 'npm:rxjs' 59 | } 60 | }); 61 | 62 | // Configure Angular for the browser and 63 | // with test versions of the platform providers 64 | Promise.all([ 65 | System.import('angular2/testing'), 66 | System.import('angular2/platform/testing/browser') 67 | ]) 68 | .then(function (results) { 69 | var testing = results[0]; 70 | var browser = results[1]; 71 | testing.setBaseTestProviders( 72 | browser.TEST_BROWSER_PLATFORM_PROVIDERS, 73 | browser.TEST_BROWSER_APPLICATION_PROVIDERS); 74 | 75 | // Load all spec files 76 | return Promise.all(wallaby.tests.map(function (specFile) { 77 | return System.import(specFile); 78 | })); 79 | }) 80 | .then(function () { 81 | wallaby.start(); 82 | }) 83 | .catch(function (e) { 84 | setTimeout(function () { 85 | throw e; 86 | }, 0); 87 | }); 88 | }, 89 | 90 | debug: true 91 | }; 92 | }; 93 | --------------------------------------------------------------------------------