├── .editorconfig
├── .gitignore
├── .jshintrc
├── .travis.yml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── app
├── ch3
│ ├── arrow-functions
│ │ ├── arrow-functions.ts
│ │ ├── context-demo.ts
│ │ └── simple-reduce.ts
│ ├── decorators
│ │ └── nonenumerable.ts
│ ├── es6-classes
│ │ └── sample-classes.ts
│ ├── hello-world
│ │ └── hello-world.ts
│ ├── let
│ │ ├── let.ts
│ │ └── var.ts
│ ├── modules
│ │ ├── app.ts
│ │ ├── app2.ts
│ │ ├── app3.ts
│ │ ├── app4.ts
│ │ ├── app5.ts
│ │ ├── math.ts
│ │ ├── math2.ts
│ │ └── math3.ts
│ └── object-literals
│ │ ├── enhanced-object-literal.ts
│ │ └── simple-object.ts
├── ch4
│ ├── es5
│ │ ├── hello-world
│ │ │ ├── app.js
│ │ │ ├── index.html
│ │ │ └── meta.json
│ │ ├── tabs
│ │ │ ├── app.html
│ │ │ ├── index.html
│ │ │ └── tabs.js
│ │ └── tooltip
│ │ │ ├── app.html
│ │ │ ├── index.html
│ │ │ └── tooltip.js
│ └── ts
│ │ ├── basic-tab-content-children
│ │ ├── app.ts
│ │ ├── index.html
│ │ └── meta.json
│ │ ├── basic-tab
│ │ ├── app.ts
│ │ ├── index.html
│ │ └── meta.json
│ │ ├── change_detection_strategy
│ │ ├── app.ts
│ │ ├── index.html
│ │ └── meta.json
│ │ ├── change_detection_strategy_broken
│ │ ├── app.ts
│ │ ├── index.html
│ │ └── meta.json
│ │ ├── change_detection_strategy_order
│ │ ├── app.ts
│ │ ├── index.html
│ │ └── meta.json
│ │ ├── custom-element
│ │ ├── app.ts
│ │ ├── index.html
│ │ └── meta.json
│ │ ├── hello-world
│ │ ├── app.html
│ │ ├── app.ts
│ │ ├── index.html
│ │ └── meta.json
│ │ ├── inputs-outputs
│ │ ├── app.ts
│ │ ├── index.html
│ │ └── meta.json
│ │ ├── life-cycle
│ │ ├── app.ts
│ │ ├── index.html
│ │ └── meta.json
│ │ ├── ng-content
│ │ ├── app.ts
│ │ ├── index.html
│ │ └── meta.json
│ │ ├── ng-for
│ │ ├── detailed-syntax
│ │ │ ├── app.html
│ │ │ ├── app.ts
│ │ │ ├── index.html
│ │ │ └── meta.json
│ │ └── syntax-sugar
│ │ │ ├── app.html
│ │ │ ├── app.ts
│ │ │ ├── index.html
│ │ │ └── meta.json
│ │ ├── template-ref
│ │ ├── app.ts
│ │ ├── index.html
│ │ └── meta.json
│ │ ├── todo-app
│ │ ├── app.html
│ │ ├── app.ts
│ │ ├── index.html
│ │ └── meta.json
│ │ ├── tooltip
│ │ ├── app.html
│ │ ├── app.ts
│ │ ├── index.html
│ │ └── meta.json
│ │ ├── view-child-content-child
│ │ ├── app.ts
│ │ ├── index.html
│ │ └── meta.json
│ │ └── zippy
│ │ ├── app.ts
│ │ ├── index.html
│ │ └── meta.json
├── ch5
│ ├── es5
│ │ └── simple-example
│ │ │ ├── app.js
│ │ │ ├── index.html
│ │ │ └── meta.json
│ └── ts
│ │ ├── configuring-providers
│ │ ├── dummy-http.ts
│ │ ├── existing.ts
│ │ ├── factory.ts
│ │ └── multi-providers.ts
│ │ ├── decorators
│ │ ├── optional.ts
│ │ ├── self.ts
│ │ └── skip-self.ts
│ │ ├── directives-ngmodules
│ │ ├── app.ts
│ │ ├── index.html
│ │ ├── meta.json
│ │ └── util.js
│ │ ├── directives
│ │ ├── app.ts
│ │ ├── index.html
│ │ ├── meta.json
│ │ └── util.js
│ │ ├── injector-basics
│ │ ├── forward-ref.ts
│ │ └── injector.ts
│ │ └── parent-child
│ │ └── simple-example.ts
├── ch6
│ └── ts
│ │ ├── multi-page-template-driven
│ │ ├── add_developer.html
│ │ ├── add_developer.ts
│ │ ├── app.ts
│ │ ├── control_errors.ts
│ │ ├── developer.ts
│ │ ├── developer_collection.ts
│ │ ├── email_validator.ts
│ │ ├── home.html
│ │ ├── home.ts
│ │ ├── index.html
│ │ └── meta.json
│ │ ├── simple-two-way-data-binding
│ │ ├── app.ts
│ │ ├── index.html
│ │ └── meta.json
│ │ ├── step-0
│ │ ├── app.ts
│ │ ├── developer.ts
│ │ ├── developer_collection.ts
│ │ └── index.html
│ │ ├── step-1-async
│ │ ├── add_developer.ts
│ │ ├── app.ts
│ │ ├── developer.ts
│ │ ├── developer_collection.ts
│ │ ├── home.ts
│ │ └── index.html
│ │ ├── step-1
│ │ ├── add_developer.ts
│ │ ├── app.ts
│ │ ├── developer.ts
│ │ ├── developer_collection.ts
│ │ ├── home.ts
│ │ └── index.html
│ │ └── step-2
│ │ ├── add_developer.html
│ │ ├── add_developer.ts
│ │ ├── app.ts
│ │ ├── control_errors.ts
│ │ ├── developer.ts
│ │ ├── developer_collection.ts
│ │ ├── email_validator.ts
│ │ ├── home.html
│ │ ├── home.ts
│ │ └── index.html
├── ch7
│ └── ts
│ │ ├── async_pipe
│ │ ├── app.ts
│ │ ├── index.html
│ │ └── meta.json
│ │ ├── builtin_pipes
│ │ ├── app.ts
│ │ ├── index.html
│ │ └── meta.json
│ │ ├── multi-page-model-driven
│ │ ├── add_developer.html
│ │ ├── add_developer.ts
│ │ ├── app.ts
│ │ ├── boolean_pipe.ts
│ │ ├── control_errors.ts
│ │ ├── developer.ts
│ │ ├── developer_advanced_info.ts
│ │ ├── developer_basic_info.ts
│ │ ├── developer_collection.ts
│ │ ├── developer_details.ts
│ │ ├── github_gateway.ts
│ │ ├── home.html
│ │ ├── home.ts
│ │ ├── index.html
│ │ └── meta.json
│ │ └── statful_pipe
│ │ ├── app.ts
│ │ ├── fetch_json_pipe.ts
│ │ ├── index.html
│ │ └── meta.json
├── ch8
│ └── ts
│ │ └── todo_webworkers
│ │ ├── background_app.ts
│ │ ├── bootstrap.ts
│ │ ├── config.ts
│ │ ├── index.html
│ │ ├── loader.ts
│ │ └── meta.json
├── index.html
├── system.config.js
└── tsconfig.json
├── appveyor.yml
├── gulpfile.ts
├── img
└── book-ed1.jpg
├── manual_typings
├── connect-livereload.d.ts
├── gulp-load-plugins.d.ts
├── jsonfile.d.ts
├── karma.d.ts
├── merge-stream.d.ts
├── open.d.ts
├── run-sequence.d.ts
├── slash.d.ts
├── systemjs-builder.d.ts
├── tiny-lr.d.ts
└── yargs.d.ts
├── package.json
├── tools
├── config.ts
├── tasks
│ ├── build.assets.dev.ts
│ ├── build.bundles.ts
│ ├── build.deps.ts
│ ├── build.docs.ts
│ ├── build.html_css.prod.ts
│ ├── build.index.ts
│ ├── build.js.dev.ts
│ ├── build.js.prod.ts
│ ├── build.test.ts
│ ├── check.versions.ts
│ ├── clean.ts
│ ├── karma.start.ts
│ ├── npm.ts
│ ├── serve.docs.ts
│ ├── server.start.ts
│ ├── tsd.ts
│ ├── tslint.ts
│ ├── watch.dev.ts
│ ├── watch.serve.ts
│ └── watch.test.ts
├── utils.ts
└── utils
│ ├── server.ts
│ ├── tasks_tools.ts
│ ├── template_injectables.ts
│ └── template_locals.ts
├── tsconfig.json
├── tslint.json
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | indent_style = space
8 | indent_size = 2
9 | end_of_line = lf
10 | insert_final_newline = true
11 | trim_trailing_whitespace = true
12 |
13 | [*.md]
14 | insert_final_newline = false
15 | trim_trailing_whitespace = false
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # Compiled binary addons (http://nodejs.org/api/addons.html)
20 | build/Release
21 |
22 | # Dependency directory
23 | # Commenting this out is preferred by some people, see
24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
25 | node_modules
26 |
27 | # Users Environment Variables
28 | .lock-wscript
29 | .tsdrc
30 |
31 | #IDE configuration files
32 | .idea
33 | .vscode
34 |
35 | dist
36 | dev
37 | docs
38 | lib
39 | test
40 | tools/typings/tsd
41 | typings
42 | tmp
43 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "bitwise": true,
3 | "immed": true,
4 | "newcap": true,
5 | "noarg": true,
6 | "noempty": true,
7 | "nonew": true,
8 | "trailing": true,
9 | "maxlen": 200,
10 | "boss": true,
11 | "eqnull": true,
12 | "expr": true,
13 | "globalstrict": true,
14 | "laxbreak": true,
15 | "loopfunc": true,
16 | "sub": true,
17 | "undef": true,
18 | "indent": 2,
19 | "unused": true,
20 |
21 | "node": true,
22 | "globals": {
23 | "System": true
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '4.0'
4 | - '4.1'
5 | - '5.1'
6 | sudo: false
7 | services:
8 | before_install:
9 | - npm update -g npm
10 | - npm --version
11 | - export CHROME_BIN=chromium-browser
12 | - export DISPLAY=:99.0
13 | - sh -e /etc/init.d/xvfb start
14 | before_script:
15 | notifications:
16 | email: true
17 | after_failure: cat /home/travis/build/mgechev/angular2-seed/npm-debug.log
18 | branches:
19 | only:
20 | - master
21 | env:
22 | global:
23 | # https://github.com/DefinitelyTyped/tsd#tsdrc
24 | # Token has no scope (read-only access to public information)
25 | - TSD_GITHUB_TOKEN=9b18c72997769f3867ef2ec470e626d39661795d
26 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Submitting Pull Requests
2 |
3 | **Please follow these basic steps to simplify pull request reviews - if you don't you'll probably just be asked to anyway.**
4 |
5 | * Please rebase your branch against the current master
6 | * Run ```npm install``` to make sure your development dependencies are up-to-date
7 | * Please ensure that the test suite passes **and** that code is lint free before submitting a PR by running:
8 | * ```npm test```
9 | * If you've added new functionality, **please** include tests which validate its behaviour
10 | * Make reference to possible [issues](https://github.com/mgechev/angular2-seed/issues) on PR comment
11 |
12 | ## Submitting bug reports
13 |
14 | * Please detail the affected browser(s) and operating system(s)
15 | * Please be sure to state which version of node **and** npm you're using
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Minko Gechev
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 all
13 | 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 THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 |
3 | This repository contains the code samples for my book "Getting started with Angular".
4 |
5 | # How to start
6 |
7 | **Note** that this seed project requires node v4.x.x or higher and npm 2.14.7.
8 |
9 | ```bash
10 | git clone --depth 1 https://github.com/mgechev/getting-started-with-angular.git
11 | cd getting-started-with-angular
12 | npm install
13 | npm start
14 | ```
15 |
16 | # First edition
17 |
18 | The code for the first edition could be found [here](https://github.com/mgechev/switching-to-angular2).
19 |
20 | # License
21 |
22 | MIT
23 |
24 |
--------------------------------------------------------------------------------
/app/ch3/arrow-functions/arrow-functions.ts:
--------------------------------------------------------------------------------
1 | var result = [1, 2, 3]
2 | .reduce((total, current) => total + current, 0);
3 |
4 | console.log(result);
5 |
6 | var even = [3, 1, 56, 7].filter(el => !(el % 2));
7 |
8 | console.log(even);
9 |
10 | var data = [];
11 | var sorted = data.sort((a, b) => {
12 | var diff = a.price - b.price;
13 | if (diff !== 0) {
14 | return diff;
15 | }
16 | return a.total - b.total;
17 | });
18 |
--------------------------------------------------------------------------------
/app/ch3/arrow-functions/context-demo.ts:
--------------------------------------------------------------------------------
1 | function MyComponent() {
2 | this.age = 42;
3 | setTimeout(() => {
4 | this.age += 1;
5 | console.log(this.age);
6 | }, 100);
7 | }
8 |
9 | new MyComponent(); // 43 in 100ms.
10 |
11 |
--------------------------------------------------------------------------------
/app/ch3/arrow-functions/simple-reduce.ts:
--------------------------------------------------------------------------------
1 | // ch3/arrow-functions/simple-reduce.ts
2 | var result = [1, 2, 3].reduce(function (total, current) {
3 | return total + current;
4 | }, 0); // 6
5 |
6 |
--------------------------------------------------------------------------------
/app/ch3/decorators/nonenumerable.ts:
--------------------------------------------------------------------------------
1 | class Person {
2 | @nonenumerable
3 | get kidCount() {
4 | return 42;
5 | }
6 | }
7 |
8 | function nonenumerable(target, name, descriptor) {
9 | descriptor.enumerable = false;
10 | return descriptor;
11 | }
12 |
13 | var person = new Person();
14 |
15 | for (let prop in person) {
16 | console.log(prop);
17 | }
18 |
19 | console.log(person.kidCount);
20 |
--------------------------------------------------------------------------------
/app/ch3/es6-classes/sample-classes.ts:
--------------------------------------------------------------------------------
1 | class Human {
2 | static totalPeople = 0;
3 | _name; // ES2016 property declaration syntax
4 | constructor(name) {
5 | this._name = name;
6 | Human.totalPeople += 1;
7 | }
8 | get name() {
9 | return this._name;
10 | }
11 | set name(val) {
12 | this._name = val;
13 | }
14 | talk() {
15 | return `Hi, I'm ${this.name}!`;
16 | }
17 | }
18 |
19 | class Developer extends Human {
20 | _languages; // ES2016 property declaration syntax
21 | constructor(name, languages) {
22 | super(name);
23 | this._languages = languages;
24 | }
25 | get languages() {
26 | return this._languages;
27 | }
28 | talk() {
29 | return `${super.talk()} And I know ${this.languages.join(', ')}.`;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/ch3/hello-world/hello-world.ts:
--------------------------------------------------------------------------------
1 | console.log('Hello world!');
2 |
--------------------------------------------------------------------------------
/app/ch3/let/let.ts:
--------------------------------------------------------------------------------
1 | // Available in ES2015
2 | // var fns = [];
3 | // for (let i = 0; i < 5; i += 1) {
4 | // fns.push(function() {
5 | // console.log(i);
6 | // })
7 | // }
8 | // fns.forEach(fn => fn());
9 |
10 |
--------------------------------------------------------------------------------
/app/ch3/let/var.ts:
--------------------------------------------------------------------------------
1 | var fns = [];
2 | for (var i = 0; i < 5; i += 1) {
3 | fns.push(function() {
4 | console.log(i);
5 | })
6 | }
7 | fns.forEach(fn => fn());
8 |
9 |
--------------------------------------------------------------------------------
/app/ch3/modules/app.ts:
--------------------------------------------------------------------------------
1 | // app.ts
2 | import {square, log10} from './math';
3 | console.log(square(2)); // 4
4 | console.log(log10(10)); // 1
5 |
--------------------------------------------------------------------------------
/app/ch3/modules/app2.ts:
--------------------------------------------------------------------------------
1 | import * as math from './math';
2 | console.log(math.square(2)); // 4
3 | console.log(math.log10(10)); // 1
4 | console.log(math.PI); // 3.141592653589793
5 |
--------------------------------------------------------------------------------
/app/ch3/modules/app3.ts:
--------------------------------------------------------------------------------
1 | import cube from './math3';
2 | console.log(cube(3)); // 27
3 |
4 |
--------------------------------------------------------------------------------
/app/ch3/modules/app4.ts:
--------------------------------------------------------------------------------
1 | import cube, { square } from './math3';
2 | console.log(square(2)); // 4
3 | console.log(cube(3)); // 27
4 |
5 |
--------------------------------------------------------------------------------
/app/ch3/modules/app5.ts:
--------------------------------------------------------------------------------
1 | import { default as cube } from './math3';
2 | console.log(cube(3)); // 27
3 |
4 |
--------------------------------------------------------------------------------
/app/ch3/modules/math.ts:
--------------------------------------------------------------------------------
1 | // math.ts
2 | export function square(x) {
3 | return Math.pow(x, 2);
4 | };
5 | export function log10(x) {
6 | return Math.log10(x);
7 | };
8 | export const PI = Math.PI;
--------------------------------------------------------------------------------
/app/ch3/modules/math2.ts:
--------------------------------------------------------------------------------
1 | // math.ts
2 | function square(x) {
3 | return Math.pow(x, 2);
4 | };
5 | function log10(x) {
6 | return Math.log10(x);
7 | };
8 | const PI = Math.PI;
9 | export { square, log10, PI };
--------------------------------------------------------------------------------
/app/ch3/modules/math3.ts:
--------------------------------------------------------------------------------
1 | export default function cube(x) {
2 | return Math.pow(x, 3);
3 | };
4 | export function square(x) {
5 | return Math.pow(x, 2);
6 | };
7 |
8 |
--------------------------------------------------------------------------------
/app/ch3/object-literals/enhanced-object-literal.ts:
--------------------------------------------------------------------------------
1 | var selector = 'hello-world';
2 | var templateUrl = 'hello-world.html';
3 | var Component = {
4 | selector,
5 | templateUrl
6 | };
7 |
8 |
--------------------------------------------------------------------------------
/app/ch3/object-literals/simple-object.ts:
--------------------------------------------------------------------------------
1 | var selector = 'hello-world';
2 | var templateUrl = 'hello-world.html';
3 | var Component = {
4 | selector: selector,
5 | templateUrl: templateUrl
6 | };
7 |
8 |
--------------------------------------------------------------------------------
/app/ch4/es5/hello-world/app.js:
--------------------------------------------------------------------------------
1 | var AppComponent = ng.core.Component({
2 | selector: 'my-app',
3 | template: '
Hello {{target}}!
'
4 | })
5 | .Class({
6 | constructor: function () {
7 | this.target = 'world';
8 | }
9 | });
10 |
11 | var AppModule = ng.core.NgModule({
12 | imports: [ng.platformBrowser.BrowserModule],
13 | declarations: [AppComponent],
14 | bootstrap: [AppComponent]
15 | })
16 | .Class({
17 | constructor: function () {}
18 | });
19 |
20 | ng.platformBrowserDynamic.platformBrowserDynamic().bootstrapModule(AppModule);
21 |
22 |
--------------------------------------------------------------------------------
/app/ch4/es5/hello-world/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%= TITLE %>
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/ch4/es5/hello-world/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Hello world in ES5",
3 | "description": "Hello world in ES5",
4 | "id": 1,
5 | "presented": true
6 | }
7 |
--------------------------------------------------------------------------------
/app/ch4/es5/tabs/app.html:
--------------------------------------------------------------------------------
1 |
2 | First tab content
3 | Second tab content
4 | Third tab content
5 |
6 |
--------------------------------------------------------------------------------
/app/ch4/es5/tabs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/ch4/es5/tabs/tabs.js:
--------------------------------------------------------------------------------
1 | var Tabs = ng.core.Component({
2 | selector: 'tabs',
3 | styles: [`
4 | .tab {
5 | display: inline-block;
6 | }
7 | .tab-header {
8 | list-style: none;
9 | padding: 0;
10 | margin: 0;
11 | }
12 | .tab-header .is-active {
13 | background-color: #eee;
14 | }
15 | .tab-header li {
16 | display: inline-block;
17 | cursor: pointer;
18 | padding: 5px;
19 | border: 1px solid #ccc;
20 | }
21 | .tab-content {
22 | border: 1px solid #ccc;
23 | border-top: none;
24 | padding: 5px;
25 | }
26 | `
27 | ],
28 | template: `
29 |
37 | `
38 | })
39 | .Class({
40 | constructor: function () {
41 | this.tabs = [];
42 | },
43 | addTab: function (tab) {
44 | var total = this.tabs.length;
45 | this.tabs.push(tab);
46 | return total;
47 | },
48 | selectTab(idx) {
49 | this.tabs.forEach(function (t) {
50 | t.setActive(false);
51 | });
52 | this.tabs[idx].setActive(true);
53 | }
54 | });
55 |
56 | var Tab = ng.core.Component({
57 | selector: 'tab',
58 | inputs: ['tabTitle'],
59 | template: `
60 |
61 |
`
62 | })
63 | .Class({
64 | constructor: [[ng.core.Inject(Tabs), ng.core.Host()], function (tabs) {
65 | this.active = !tabs.addTab(this);
66 | }],
67 | setActive: function (isActive) {
68 | this.active = isActive;
69 | },
70 | isActive: function () {
71 | return this.active;
72 | }
73 | });
74 |
75 | var App = ng.core.Component({
76 | selector: 'app',
77 | templateUrl: './app.html'
78 | })
79 | .Class({
80 | constructor: function () {}
81 | });
82 |
83 |
84 | var AppModule = ng.core.NgModule({
85 | imports: [ng.platformBrowser.BrowserModule],
86 | declarations: [App, Tab, Tabs],
87 | bootstrap: [App]
88 | })
89 | .Class({
90 | constructor: function () {}
91 | });
92 |
93 | ng.platformBrowserDynamic.platformBrowserDynamic().bootstrapModule(AppModule);
94 |
95 |
--------------------------------------------------------------------------------
/app/ch4/es5/tooltip/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app/ch4/es5/tooltip/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/app/ch4/es5/tooltip/tooltip.js:
--------------------------------------------------------------------------------
1 | var Overlay = ng.core.Class({
2 | constructor: function () {
3 | var el = document.createElement('div');
4 | el.className = 'tooltip';
5 | this.el = el;
6 | },
7 | close: function () {
8 | this.el.hidden = true;
9 | },
10 | open: function (el, text) {
11 | this.el.innerHTML = text;
12 | this.el.hidden = false;
13 | var rect = el.nativeElement.getBoundingClientRect();
14 | this.el.style.left = rect.left + 'px';
15 | this.el.style.top = rect.top + 'px';
16 | },
17 | attach: function (target) {
18 | target.appendChild(this.el);
19 | },
20 | detach: function () {
21 | this.el.parentNode.removeChild(this.el);
22 | }
23 | });
24 |
25 | var Tooltip = ng.core.Directive({
26 | selector: '[tooltip]',
27 | inputs: ['tooltip'],
28 | host: {
29 | '(mouseenter)': 'onMouseEnter()',
30 | '(mouseleave)': 'onMouseLeave()'
31 | }
32 | })
33 | .Class({
34 | constructor: [ng.core.ElementRef, Overlay, function (el, overlay) {
35 | this.el = el;
36 | this.overlay = overlay;
37 | overlay.attach(this.el.nativeElement);
38 | }],
39 | onMouseEnter() {
40 | this.overlay.open(this.el, this.tooltip);
41 | },
42 | onMouseLeave() {
43 | this.overlay.close();
44 | }
45 | });
46 |
47 | var App = ng.core.Component({
48 | selector: 'app',
49 | templateUrl: './app.html',
50 | })
51 | .Class({
52 | constructor: function () {}
53 | });
54 |
55 | var AppModule = ng.core.NgModule({
56 | imports: [ng.platformBrowser.BrowserModule],
57 | declarations: [App, Tooltip],
58 | providers: [Overlay],
59 | bootstrap: [App]
60 | })
61 | .Class({
62 | constructor: function () {}
63 | });
64 |
65 | ng.platformBrowserDynamic.platformBrowserDynamic().bootstrapModule(AppModule);
66 |
67 |
--------------------------------------------------------------------------------
/app/ch4/ts/basic-tab-content-children/app.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Directive,
3 | Inject,
4 | EventEmitter,
5 | Output,
6 | Component,
7 | forwardRef,
8 | Host,
9 | Attribute,
10 | ContentChildren,
11 | QueryList,
12 | NgModule
13 | } from '@angular/core';
14 |
15 | import {BrowserModule} from '@angular/platform-browser';
16 |
17 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
18 |
19 | @Component({
20 | selector: 'tab-title',
21 | styles: [`
22 | .tab-title {
23 | display: inline-block;
24 | cursor: pointer;
25 | padding: 5px;
26 | border: 1px solid #ccc;
27 | }
28 | `],
29 | template: `
30 |
31 |
32 |
33 | `
34 | })
35 | class TabTitle {
36 | @Output('selected')
37 | tabSelected: EventEmitter = new EventEmitter();
38 | handleClick() {
39 | this.tabSelected.emit(this);
40 | }
41 | }
42 |
43 | @Component({
44 | selector: 'tab-content',
45 | styles: [`
46 | .tab-content {
47 | border: 1px solid #ccc;
48 | border-top: none;
49 | padding: 5px;
50 | }
51 | `],
52 | template: `
53 |
54 |
55 |
56 | `
57 | })
58 | class TabContent {
59 | isActive: boolean = false;
60 | }
61 |
62 | @Component({
63 | selector: 'tabs',
64 | styles: [
65 | `
66 | .tab {
67 | display: inline-block;
68 | }
69 | .tab-nav {
70 | list-style: none;
71 | padding: 0;
72 | margin: 0;
73 | }
74 | `
75 | ],
76 | template: `
77 |
83 | `
84 | })
85 | class Tabs {
86 | @Output('changed')
87 | tabChanged: EventEmitter = new EventEmitter();
88 |
89 | @ContentChildren(TabTitle)
90 | tabTitles: QueryList;
91 |
92 | @ContentChildren(TabContent)
93 | tabContents: QueryList;
94 |
95 | active: number;
96 |
97 | select(index: number) {
98 | let contents: TabContent[] = this.tabContents.toArray();
99 | contents[this.active].isActive = false;
100 | this.active = index;
101 | contents[this.active].isActive = true;
102 | this.tabChanged.emit(index);
103 | }
104 | ngAfterContentInit() {
105 | this.tabTitles
106 | .map(t => t.tabSelected)
107 | .forEach((t, i) => {
108 | t.subscribe(_ => {
109 | this.select(i)
110 | });
111 | });
112 | this.active = 0;
113 | this.select(0);
114 | }
115 | }
116 |
117 | @Component({
118 | selector: 'app',
119 | template: `
120 |
121 | Tab 1
122 | Content 1
123 | Tab 2
124 | Content 2
125 |
126 | `
127 | })
128 | class App {
129 | tabChanged(tab) {
130 | console.log(tab);
131 | }
132 | }
133 |
134 | @NgModule({
135 | declarations: [App, Tabs, TabContent, TabTitle],
136 | imports: [BrowserModule],
137 | bootstrap: [App],
138 | })
139 | class AppModule {}
140 |
141 | platformBrowserDynamic().bootstrapModule(AppModule);
142 |
143 |
--------------------------------------------------------------------------------
/app/ch4/ts/basic-tab-content-children/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= TITLE %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Loading...
15 |
16 |
17 | <%= INIT %>
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/ch4/ts/basic-tab-content-children/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Basic tab component with ViewChildren",
3 | "description": "Basic tab component with ViewChildren",
4 | "id": 9,
5 | "presented": true
6 | }
--------------------------------------------------------------------------------
/app/ch4/ts/basic-tab/app.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Directive,
3 | Inject,
4 | EventEmitter,
5 | Output,
6 | Input,
7 | Component,
8 | forwardRef,
9 | Host,
10 | NgModule
11 | } from '@angular/core';
12 |
13 | import {BrowserModule} from '@angular/platform-browser';
14 |
15 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
16 |
17 | @Component({
18 | selector: `tab`,
19 | template: `
20 |
21 |
22 |
23 | `
24 | })
25 | class Tab {
26 | isActive: boolean;
27 | @Input()
28 | public title: string;
29 | constructor(@Inject(forwardRef(() => Tabs)) @Host() private tabs: Tabs) {
30 | this.tabs.addTab(this);
31 | }
32 | }
33 |
34 | @Component({
35 | selector: 'tabs',
36 | styles: [
37 | `
38 | .tab {
39 | display: inline-block;
40 | }
41 | .tab-header {
42 | list-style: none;
43 | padding: 0;
44 | margin: 0;
45 | }
46 | .tab-header .is-active {
47 | background-color: #eee;
48 | }
49 | .tab-header li {
50 | display: inline-block;
51 | cursor: pointer;
52 | padding: 5px;
53 | border: 1px solid #ccc;
54 | }
55 | .tab-content {
56 | border: 1px solid #ccc;
57 | border-top: none;
58 | padding: 5px;
59 | }
60 | `
61 | ],
62 | template: `
63 |
74 | `
75 | })
76 | class Tabs {
77 | @Output('changed')
78 | private tabChanged: EventEmitter = new EventEmitter();
79 | private tabs: Tab[];
80 | private active: number;
81 | constructor() {
82 | this.tabs = [];
83 | this.active = 0;
84 | }
85 | addTab(tab: Tab) {
86 | if (this.tabs.length === this.active) {
87 | tab.isActive = true;
88 | }
89 | this.tabs.push(tab);
90 | }
91 | select(index) {
92 | this.tabs[this.active].isActive = false;
93 | this.active = index;
94 | this.tabs[index].isActive = true;
95 | this.tabChanged.emit(this.tabs[index]);
96 | }
97 | }
98 |
99 | @Component({
100 | selector: 'app',
101 | template: `
102 |
103 |
104 | Content 1
105 |
106 |
107 | Content 2
108 |
109 |
110 | `
111 | })
112 | class App {
113 | tabChanged(tab) {
114 | console.log(tab);
115 | }
116 | }
117 |
118 |
119 | @NgModule({
120 | declarations: [App, Tabs, Tab],
121 | imports: [BrowserModule],
122 | bootstrap: [App],
123 | })
124 | class AppModule {}
125 |
126 | platformBrowserDynamic().bootstrapModule(AppModule);
127 |
128 |
--------------------------------------------------------------------------------
/app/ch4/ts/basic-tab/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= TITLE %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Loading...
15 |
16 |
17 | <%= INIT %>
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/ch4/ts/basic-tab/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Basic tab component",
3 | "description": "Basic tab component",
4 | "id": 8,
5 | "presented": false
6 | }
--------------------------------------------------------------------------------
/app/ch4/ts/change_detection_strategy/app.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import {NgModule, Component, Input, Output, EventEmitter, ChangeDetectionStrategy} from '@angular/core';
4 | import {BrowserModule} from '@angular/platform-browser';
5 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
6 |
7 | import * as Immutable from 'immutable';
8 |
9 | interface Todo {
10 | completed: boolean;
11 | label: string;
12 | }
13 |
14 | @Component({
15 | selector: 'input-box',
16 | template: `
17 |
18 |
21 | `
22 | })
23 | class InputBox {
24 | @Input() inputPlaceholder: string;
25 | @Input() buttonLabel: string;
26 | @Output() inputText = new EventEmitter();
27 |
28 | emitText(text: string) {
29 | this.inputText.emit(text);
30 | }
31 | }
32 |
33 | @Component({
34 | selector: 'todo-list',
35 | changeDetection: ChangeDetectionStrategy.OnPush,
36 | template: `
37 |
44 | `,
45 | styles: [
46 | `ul li {
47 | list-style: none;
48 | }
49 | .completed {
50 | text-decoration: line-through;
51 | }`
52 | ]
53 | })
54 | class TodoList {
55 | @Input() todos;
56 | @Output() toggle = new EventEmitter();
57 |
58 | toggleCompletion(index: number) {
59 | this.toggle.emit(index);
60 | }
61 | }
62 |
63 | @Component({
64 | selector: 'todo-app',
65 | template: `
66 | Hello {{name}}!
67 |
68 |
69 | Add a new todo:
70 |
73 |
74 |
75 |
76 | Here's the list of pending todo items:
77 |
79 |
80 | `
81 | })
82 | class TodoApp {
83 | todos = Immutable.fromJS([{
84 | label: 'Buy milk',
85 | completed: false
86 | }, {
87 | label: 'Save the world',
88 | completed: false
89 | }]);
90 |
91 | name: string = 'John';
92 |
93 | addTodo(label: string) {
94 | this.todos = this.todos.push(Immutable.fromJS({
95 | label,
96 | completed: false
97 | }));
98 | }
99 | toggleCompletion(index: number) {
100 | this.todos = this.todos.update(index, todo => {
101 | return Immutable.fromJS({
102 | label: todo.get('label'),
103 | completed: !todo.get('completed')
104 | });
105 | });
106 | }
107 | }
108 |
109 | @NgModule({
110 | declarations: [TodoList, InputBox, TodoApp],
111 | imports: [BrowserModule],
112 | bootstrap: [TodoApp],
113 | })
114 | class TodoAppModule {}
115 |
116 | platformBrowserDynamic().bootstrapModule(TodoAppModule);
117 |
118 |
--------------------------------------------------------------------------------
/app/ch4/ts/change_detection_strategy/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= TITLE %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Loading...
15 |
16 |
17 | <%= INIT %>
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/ch4/ts/change_detection_strategy/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Change Detection strategies (OnPush)",
3 | "description": "Change Detection strategy",
4 | "id": 12,
5 | "presented": true
6 | }
--------------------------------------------------------------------------------
/app/ch4/ts/change_detection_strategy_broken/app.ts:
--------------------------------------------------------------------------------
1 | import {NgModule, Component, Input, Output, EventEmitter, ChangeDetectionStrategy} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
4 |
5 | interface Todo {
6 | completed: boolean;
7 | label: string;
8 | }
9 |
10 | @Component({
11 | selector: 'input-box',
12 | template: `
13 |
14 |
17 | `
18 | })
19 | class InputBox {
20 | @Input() inputPlaceholder: string;
21 | @Input() buttonLabel: string;
22 | @Output() inputText = new EventEmitter();
23 |
24 | emitText(text: string) {
25 | this.inputText.emit(text);
26 | }
27 | }
28 |
29 | @Component({
30 | selector: 'todo-list',
31 | template: `
32 |
39 | `,
40 | styles: [
41 | `ul li {
42 | list-style: none;
43 | }
44 | .completed {
45 | text-decoration: line-through;
46 | }`
47 | ],
48 | changeDetection: ChangeDetectionStrategy.OnPush
49 | })
50 | class TodoList {
51 | @Input() todos: Todo[];
52 | @Output() toggle = new EventEmitter();
53 |
54 | toggleCompletion(index: number) {
55 | let todo = this.todos[index];
56 | this.toggle.emit(todo);
57 | }
58 | }
59 |
60 | @Component({
61 | selector: 'todo-app',
62 | template: `
63 | Hello {{name}}!
64 |
65 |
66 | Add a new todo:
67 |
70 |
71 |
72 |
73 | Here's the list of pending todo items:
74 |
76 |
77 | `
78 | })
79 | class TodoApp {
80 | todos: Todo[] = [{
81 | label: 'Buy milk',
82 | completed: false
83 | }, {
84 | label: 'Save the world',
85 | completed: false
86 | }];
87 | name: string = 'John';
88 |
89 | addTodo(label: string) {
90 | this.todos.push({
91 | label,
92 | completed: false
93 | });
94 | }
95 |
96 | toggleCompletion(todo: Todo) {
97 | todo.completed = !todo.completed;
98 | }
99 | }
100 |
101 |
102 | @NgModule({
103 | declarations: [TodoList, InputBox, TodoApp],
104 | imports: [BrowserModule],
105 | bootstrap: [TodoApp],
106 | })
107 | class TodoAppModule {}
108 |
109 | platformBrowserDynamic().bootstrapModule(TodoAppModule);
110 |
111 |
--------------------------------------------------------------------------------
/app/ch4/ts/change_detection_strategy_broken/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= TITLE %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Loading...
15 |
16 |
17 | <%= INIT %>
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/ch4/ts/change_detection_strategy_broken/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Change Detection - not performing",
3 | "description": "Change Detection strategy",
4 | "id": 13,
5 | "presented": true
6 | }
--------------------------------------------------------------------------------
/app/ch4/ts/change_detection_strategy_order/app.ts:
--------------------------------------------------------------------------------
1 | import {NgModule, Component, Input, Output, EventEmitter, ChangeDetectionStrategy} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
4 |
5 | interface Todo {
6 | completed: boolean;
7 | label: string;
8 | }
9 |
10 | @Component({
11 | selector: 'input-box',
12 | template: `
13 |
14 |
17 | `
18 | })
19 | class InputBox {
20 | @Input() inputPlaceholder: string;
21 | @Input() buttonLabel: string;
22 | @Output() inputText = new EventEmitter();
23 | emitText(text: string) {
24 | this.inputText.emit(text);
25 | }
26 | ngDoCheck() {
27 | console.log('Change detection run in the InputBox component');
28 | }
29 | }
30 |
31 | @Component({
32 | selector: 'todo-list',
33 | template: `
34 |
41 | `,
42 | styles: [
43 | `ul li {
44 | list-style: none;
45 | }
46 | .completed {
47 | text-decoration: line-through;
48 | }`
49 | ]
50 | })
51 | class TodoList {
52 | @Input() todos: Todo[];
53 | @Output() toggle = new EventEmitter();
54 | toggleCompletion(index: number) {
55 | let todo = this.todos[index];
56 | this.toggle.emit(todo);
57 | }
58 | ngDoCheck() {
59 | console.log('Change detection run in the TodoList component');
60 | }
61 | }
62 |
63 | @Component({
64 | selector: 'todo-app',
65 | template: `
66 | Hello {{name}}!
67 |
68 |
69 | Add a new todo:
70 |
73 |
74 |
75 |
76 | Here's the list of pending todo items:
77 |
79 |
80 | `
81 | })
82 | class TodoApp {
83 | todos: Todo[] = [{
84 | label: 'Buy milk',
85 | completed: false
86 | }, {
87 | label: 'Save the world',
88 | completed: false
89 | }];
90 | name: string = 'John';
91 | addTodo(label: string) {
92 | this.todos.push({
93 | label,
94 | completed: false
95 | });
96 | }
97 | toggleCompletion(todo: Todo) {
98 | todo.completed = !todo.completed;
99 | }
100 | ngDoCheck() {
101 | console.log('Change detection run in the TodoApp component');
102 | }
103 | }
104 |
105 |
106 | @NgModule({
107 | declarations: [TodoList, InputBox, TodoApp],
108 | imports: [BrowserModule],
109 | bootstrap: [TodoApp],
110 | })
111 | class TodoAppModule {}
112 |
113 | platformBrowserDynamic().bootstrapModule(TodoAppModule);
114 |
115 |
--------------------------------------------------------------------------------
/app/ch4/ts/change_detection_strategy_order/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= TITLE %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Loading...
15 |
16 |
17 | <%= INIT %>
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/ch4/ts/change_detection_strategy_order/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Change Detection - order of execution",
3 | "description": "Change Detection - order of execution",
4 | "id": 13,
5 | "presented": true
6 | }
--------------------------------------------------------------------------------
/app/ch4/ts/custom-element/app.ts:
--------------------------------------------------------------------------------
1 | import {Component, NgModule, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
4 |
5 | @Component({
6 | selector: 'my-app',
7 | template: `
8 | Hello {{name}}
9 | The current time is
10 | `
11 | })
12 | class App {
13 | name: string = 'John Doe';
14 | }
15 |
16 | @NgModule({
17 | imports: [BrowserModule],
18 | declarations: [App],
19 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
20 | bootstrap: [App]
21 | })
22 | class AppModule {}
23 |
24 | platformBrowserDynamic().bootstrapModule(AppModule);
25 |
26 |
--------------------------------------------------------------------------------
/app/ch4/ts/custom-element/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= TITLE %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Loading...
15 |
16 |
17 | <%= INIT %>
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/ch4/ts/custom-element/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Using custom elements with Angular",
3 | "description": "Using custom elements with Angular",
4 | "id": 6,
5 | "presented": true
6 | }
7 |
--------------------------------------------------------------------------------
/app/ch4/ts/hello-world/app.html:
--------------------------------------------------------------------------------
1 | Hello {{target}}!
--------------------------------------------------------------------------------
/app/ch4/ts/hello-world/app.ts:
--------------------------------------------------------------------------------
1 | import {Component, NgModule} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
4 |
5 | @Component({
6 | selector: 'my-app',
7 | templateUrl: './app.html'
8 | })
9 | class App {
10 | target: string;
11 | constructor() {
12 | this.target = 'world';
13 | }
14 | }
15 |
16 | @NgModule({
17 | declarations: [App],
18 | imports: [BrowserModule],
19 | bootstrap: [App],
20 | })
21 | class AppModule {}
22 |
23 | platformBrowserDynamic().bootstrapModule(AppModule);
24 |
25 |
--------------------------------------------------------------------------------
/app/ch4/ts/hello-world/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= TITLE %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Loading...
15 |
16 |
17 | <%= INIT %>
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/ch4/ts/hello-world/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Hello world application in TypeScript",
3 | "description": "Hello world application in TypeScript",
4 | "id": 2,
5 | "presented": true
6 | }
--------------------------------------------------------------------------------
/app/ch4/ts/inputs-outputs/app.ts:
--------------------------------------------------------------------------------
1 | import {NgModule, Component, Input, Output, EventEmitter} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
4 |
5 | interface Todo {
6 | completed: boolean;
7 | label: string;
8 | }
9 |
10 | @Component({
11 | selector: 'input-box',
12 | template: `
13 |
14 |
17 | `
18 | })
19 | class InputBox {
20 | @Input() inputPlaceholder: string;
21 | @Input() buttonLabel: string;
22 | @Output() inputText = new EventEmitter();
23 | emitText(text: string) {
24 | this.inputText.emit(text);
25 | }
26 | }
27 |
28 | @Component({
29 | selector: 'todo-list',
30 | template: `
31 |
38 | `,
39 | styles: [
40 | `ul li {
41 | list-style: none;
42 | }
43 | .completed {
44 | text-decoration: line-through;
45 | }`
46 | ]
47 | })
48 | class TodoList {
49 | @Input() todos: Todo[];
50 | @Output() toggle = new EventEmitter();
51 | toggleCompletion(index: number) {
52 | let todo = this.todos[index];
53 | this.toggle.emit(todo);
54 | }
55 | }
56 |
57 | @Component({
58 | selector: 'todo-app',
59 | template: `
60 | Hello {{name}}!
61 |
62 |
63 | Add a new todo:
64 |
67 |
68 |
69 |
70 | Here's the list of pending todo items:
71 |
73 |
74 | `
75 | })
76 | class TodoApp {
77 | todos: Todo[] = [{
78 | label: 'Buy milk',
79 | completed: false
80 | }, {
81 | label: "Save the world",
82 | completed: false
83 | }];
84 | name: string = 'John';
85 | addTodo(label: string) {
86 | this.todos.push({
87 | label,
88 | completed: false
89 | });
90 | }
91 | toggleCompletion(todo: Todo) {
92 | todo.completed = !todo.completed;
93 | }
94 | }
95 |
96 | @NgModule({
97 | declarations: [TodoList, InputBox, TodoApp],
98 | imports: [BrowserModule],
99 | bootstrap: [TodoApp],
100 | })
101 | class TodoAppModule {}
102 |
103 | platformBrowserDynamic().bootstrapModule(TodoAppModule);
104 |
105 |
--------------------------------------------------------------------------------
/app/ch4/ts/inputs-outputs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= TITLE %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Loading...
15 |
16 |
17 |
18 |
19 | <%= INIT %>
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/ch4/ts/inputs-outputs/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Component's @Input and @Output",
3 | "description": "Todo application which demonstrates component's inputs and outputs",
4 | "id": 8,
5 | "presented": true
6 | }
--------------------------------------------------------------------------------
/app/ch4/ts/life-cycle/app.ts:
--------------------------------------------------------------------------------
1 | import {Component, NgModule, Input} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
4 |
5 | @Component({
6 | selector: 'panel',
7 | template: ''
8 | })
9 | class Panel {
10 | @Input() title: string;
11 | @Input() caption: string;
12 |
13 | ngOnChanges(changes) {
14 | console.log('On changes', changes);
15 | }
16 |
17 | ngOnInit() {
18 | console.log('Initialized');
19 | }
20 |
21 | ngDoCheck() {
22 | console.log('Do check');
23 | }
24 |
25 | ngOnDestroy() {
26 | console.log('Destroy');
27 | }
28 |
29 | ngAfterContentInit() {
30 | console.log('After content init');
31 | }
32 |
33 | ngAfterContentChecked() {
34 | console.log('After content checked');
35 | }
36 |
37 | ngAfterViewInit() {
38 | console.log('After view init');
39 | }
40 |
41 | ngAfterViewChecked() {
42 | console.log('After view checked');
43 | }
44 | }
45 |
46 | @Component({
47 | selector: 'app',
48 | template: `
49 |
50 |
53 | `
54 | })
55 | class App {
56 | counter: number = 0;
57 |
58 | toggle() {
59 | this.counter += 1;
60 | }
61 | }
62 |
63 | @NgModule({
64 | declarations: [Panel, App],
65 | imports: [BrowserModule],
66 | bootstrap: [App],
67 | })
68 | class AppModule {}
69 |
70 | platformBrowserDynamic().bootstrapModule(AppModule);
71 |
72 |
--------------------------------------------------------------------------------
/app/ch4/ts/life-cycle/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= TITLE %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Loading...
15 |
16 |
17 | <%= INIT %>
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/ch4/ts/life-cycle/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Lifecycle hooks",
3 | "description": "Demo of the lifecycle hooks of Angular 2 component",
4 | "id": 10,
5 | "presented": true
6 | }
--------------------------------------------------------------------------------
/app/ch4/ts/ng-content/app.ts:
--------------------------------------------------------------------------------
1 | import {Component, NgModule} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
4 |
5 | @Component({
6 | selector: 'fancy-button',
7 | template: ''
8 | })
9 | class FancyButton { /* Extra behavior */ }
10 |
11 | @Component({
12 | selector: 'panel',
13 | styles: [
14 | `.panel {
15 | width: auto;
16 | display: inline-block;
17 | border: 1px solid black;
18 | }
19 | .panel-title {
20 | border-bottom: 1px solid black;
21 | background-color: #eee;
22 | }
23 | .panel-content,
24 | .panel-title {
25 | padding: 5px;
26 | }`
27 | ],
28 | template: `
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
`
37 | })
38 | class Panel { }
39 |
40 | @Component({
41 | selector: 'app',
42 | template: `
43 |
44 | I will be projected
45 |
46 |
47 |
48 |
49 |
50 |
51 | `
52 | })
53 | class App {
54 | constructor() {}
55 | }
56 |
57 | @NgModule({
58 | declarations: [Panel, FancyButton, App],
59 | imports: [BrowserModule],
60 | bootstrap: [App],
61 | })
62 | class AppModule {}
63 |
64 | platformBrowserDynamic().bootstrapModule(AppModule);
65 |
66 |
--------------------------------------------------------------------------------
/app/ch4/ts/ng-content/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= TITLE %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Loading...
15 |
16 |
17 | <%= INIT %>
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/ch4/ts/ng-content/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Content projection with ng-content",
3 | "description": "Various demos of ng-content",
4 | "id": 7,
5 | "presented": true
6 | }
--------------------------------------------------------------------------------
/app/ch4/ts/ng-for/detailed-syntax/app.html:
--------------------------------------------------------------------------------
1 | Hello {{name}}!
2 |
3 | Here's list of the things you need to do:
4 |
5 |
--------------------------------------------------------------------------------
/app/ch4/ts/ng-for/detailed-syntax/app.ts:
--------------------------------------------------------------------------------
1 | import {Component, NgModule} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
4 |
5 | @Component({
6 | selector: 'app',
7 | templateUrl: './app.html',
8 | })
9 | class App {
10 | todos: string[];
11 | name: string;
12 | constructor() {
13 | this.name = 'John';
14 | this.todos = ['Buy milk', 'Save the world'];
15 | }
16 | }
17 |
18 | @NgModule({
19 | declarations: [App],
20 | imports: [BrowserModule],
21 | bootstrap: [App],
22 | })
23 | class AppModule {}
24 |
25 | platformBrowserDynamic().bootstrapModule(AppModule);
26 |
27 |
--------------------------------------------------------------------------------
/app/ch4/ts/ng-for/detailed-syntax/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= APP_TITLE %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Loading...
15 |
16 |
17 | <%= INIT %>
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/ch4/ts/ng-for/detailed-syntax/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "List of items (detailed syntax)",
3 | "description": "List of items using explicit template for ng-for",
4 | "id": 3,
5 | "presented": true
6 | }
--------------------------------------------------------------------------------
/app/ch4/ts/ng-for/syntax-sugar/app.html:
--------------------------------------------------------------------------------
1 | Hello {{name}}!
2 |
3 | Here's list of the things you need to do:
4 |
5 |
--------------------------------------------------------------------------------
/app/ch4/ts/ng-for/syntax-sugar/app.ts:
--------------------------------------------------------------------------------
1 | import {Component, NgModule} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
4 |
5 | @Component({
6 | selector: 'app',
7 | templateUrl: './app.html'
8 | })
9 | class App {
10 | todos: string[];
11 | name: string;
12 | handle() {
13 | alert(42);
14 | }
15 | constructor() {
16 | this.name = "John";
17 | this.todos = ['Buy milk', "Save the world"];
18 | }
19 | }
20 |
21 | @NgModule({
22 | declarations: [App],
23 | imports: [BrowserModule],
24 | bootstrap: [App],
25 | })
26 | class AppModule {}
27 |
28 | platformBrowserDynamic().bootstrapModule(AppModule);
29 |
30 |
--------------------------------------------------------------------------------
/app/ch4/ts/ng-for/syntax-sugar/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= APP_TITLE %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Loading...
15 |
16 |
17 | <%= INIT %>
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/ch4/ts/ng-for/syntax-sugar/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "List of items (syntax sugar)",
3 | "description": "List of items using syntax sugar *ng-for",
4 | "id": 4,
5 | "presented": true
6 | }
--------------------------------------------------------------------------------
/app/ch4/ts/template-ref/app.ts:
--------------------------------------------------------------------------------
1 | import {NgModule, Component, ContentChild, TemplateRef, Input, Output, EventEmitter} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
4 |
5 | interface Todo {
6 | completed: boolean;
7 | label: string;
8 | }
9 |
10 | @Component({
11 | selector: 'input-box',
12 | template: `
13 |
14 |
17 | `
18 | })
19 | class InputBox {
20 | @Input() inputPlaceholder: string;
21 | @Input() buttonLabel: string;
22 | @Output() inputText = new EventEmitter();
23 | emitText(text: string) {
24 | this.inputText.emit(text);
25 | }
26 | }
27 |
28 | @Component({
29 | selector: 'todo-list',
30 | template: `
31 |
35 | `
36 | })
37 | class TodoList {
38 | @Input() todos: Todo[];
39 | @Input() itemsTemplate: TemplateRef;
40 | @Output() toggle = new EventEmitter();
41 | }
42 |
43 | @Component({
44 | selector: 'todo-app',
45 | template: `
46 | Hello {{name}}!
47 |
48 |
49 | Add a new todo:
50 |
53 |
54 |
55 |
56 | Here's the list of pending todo items:
57 |
60 |
61 | `
62 | })
63 | class TodoApp {
64 | todos: Todo[] = [{
65 | label: 'Buy milk',
66 | completed: false
67 | }, {
68 | label: 'Save the world',
69 | completed: false
70 | }];
71 | name: string = 'John';
72 | @ContentChild(TemplateRef) itemsTemplate: TemplateRef;
73 |
74 | addTodo(label: string) {
75 | this.todos.push({
76 | label,
77 | completed: false
78 | });
79 | }
80 | }
81 |
82 | @Component({
83 | selector: 'app',
84 | styles: [`
85 | .completed {
86 | text-decoration: line-through;
87 | }`
88 | ],
89 | template: `
90 |
91 |
92 |
94 |
95 | {{todo.label}}
96 |
97 |
98 |
99 | `
100 | })
101 | class App {}
102 |
103 |
104 | @NgModule({
105 | declarations: [TodoList, InputBox, TodoApp, App],
106 | imports: [BrowserModule],
107 | bootstrap: [App],
108 | })
109 | class AppModule {}
110 |
111 | platformBrowserDynamic().bootstrapModule(AppModule);
112 |
113 |
--------------------------------------------------------------------------------
/app/ch4/ts/template-ref/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= APP_TITLE %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Loading...
15 |
16 |
17 | <%= INIT %>
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/ch4/ts/template-ref/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Todo application using templateRef",
3 | "description": "Simple todo application using templateRef",
4 | "id": 11,
5 | "presented": true
6 | }
--------------------------------------------------------------------------------
/app/ch4/ts/todo-app/app.html:
--------------------------------------------------------------------------------
1 | Hello {{name}}!
2 |
3 |
4 | Add a new todo:
5 |
6 |
7 |
8 |
9 | Here's the list of pending todo items:
10 |
11 |
18 |
--------------------------------------------------------------------------------
/app/ch4/ts/todo-app/app.ts:
--------------------------------------------------------------------------------
1 | import {Component, NgModule} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
4 |
5 | interface Todo {
6 | completed: boolean;
7 | label: string;
8 | }
9 |
10 | @Component({
11 | selector: 'app',
12 | templateUrl: './app.html',
13 | styles: [
14 | `ul li {
15 | list-style: none;
16 | }
17 | .completed {
18 | text-decoration: line-through;
19 | }`
20 | ]
21 | })
22 | class TodoCtrl {
23 | todos: Todo[] = [{
24 | label: 'Buy milk',
25 | completed: false
26 | }, {
27 | label: "Save the world",
28 | completed: false
29 | }];
30 |
31 | name: string = 'John';
32 |
33 | addTodo(label) {
34 | this.todos.push({
35 | label,
36 | completed: false
37 | })
38 | }
39 |
40 | removeTodo(idx) {
41 | this.todos.splice(idx, 1);
42 | }
43 |
44 | toggleCompletion(idx) {
45 | let todo = this.todos[idx];
46 | todo.completed = !todo.completed;
47 | }
48 | }
49 |
50 |
51 | @NgModule({
52 | declarations: [TodoCtrl],
53 | imports: [BrowserModule],
54 | bootstrap: [TodoCtrl],
55 | })
56 | class AppModule {}
57 |
58 | platformBrowserDynamic().bootstrapModule(AppModule);
59 |
60 |
--------------------------------------------------------------------------------
/app/ch4/ts/todo-app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= APP_TITLE %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Loading...
15 |
16 |
17 |
18 |
19 | <%= INIT %>
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/ch4/ts/todo-app/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Basic todo application",
3 | "description": "Todo application which demonstrates basic user input",
4 | "id": 6,
5 | "presented": true
6 | }
--------------------------------------------------------------------------------
/app/ch4/ts/tooltip/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app/ch4/ts/tooltip/app.ts:
--------------------------------------------------------------------------------
1 | import {NgModule, HostListener, Input, Injectable, ElementRef, Inject, Directive, Component} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
4 |
5 | class Overlay {
6 | private el: HTMLElement;
7 | constructor() {
8 | var el = document.createElement('div');
9 | el.className = 'tooltip';
10 | this.el = el;
11 | }
12 | close() {
13 | this.el.hidden = true;
14 | }
15 | open(el, text) {
16 | this.el.innerHTML = text;
17 | this.el.hidden = false;
18 | var rect = el.nativeElement.getBoundingClientRect();
19 | this.el.style.left = rect.left + 'px';
20 | this.el.style.top = rect.top + 'px';
21 | }
22 | attach(target) {
23 | target.appendChild(this.el);
24 | }
25 | detach() {
26 | this.el.parentNode.removeChild(this.el);
27 | }
28 | }
29 |
30 | class OverlayMock {
31 | constructor() {}
32 | close() {}
33 | open(el, text) {}
34 | attach(target) {}
35 | detach() {}
36 | }
37 |
38 | @Directive({
39 | selector: '[saTooltip]'
40 | })
41 | export class Tooltip {
42 | @Input()
43 | saTooltip:string;
44 |
45 | constructor(private el: ElementRef, private overlay: Overlay) {
46 | this.overlay.attach(el.nativeElement);
47 | }
48 | @HostListener('mouseenter')
49 | onMouseEnter() {
50 | this.overlay.open(this.el, this.saTooltip);
51 | }
52 | @HostListener('mouseleave')
53 | onMouseLeave() {
54 | this.overlay.close();
55 | }
56 | }
57 |
58 | @Component({
59 | selector: 'app',
60 | templateUrl: './app.html',
61 | })
62 | class App {}
63 |
64 |
65 | @NgModule({
66 | declarations: [Tooltip, App],
67 | providers: [Overlay],
68 | imports: [BrowserModule],
69 | bootstrap: [App],
70 | })
71 | class AppModule {}
72 |
73 | platformBrowserDynamic().bootstrapModule(AppModule);
74 |
75 |
--------------------------------------------------------------------------------
/app/ch4/ts/tooltip/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | <%= INIT %>
26 |
27 |
--------------------------------------------------------------------------------
/app/ch4/ts/tooltip/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Tooltip directive",
3 | "description": "Tooltip directive",
4 | "id": 5,
5 | "presented": true
6 | }
--------------------------------------------------------------------------------
/app/ch4/ts/view-child-content-child/app.ts:
--------------------------------------------------------------------------------
1 | import {NgModule, Component, Directive, ViewChildren, ContentChildren, QueryList} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
4 |
5 | @Component({
6 | selector: 'user-badge',
7 | template: 'View child
'
8 | })
9 | class UserBadge {}
10 |
11 | @Component({
12 | selector: 'user-rating',
13 | template: 'Content child
'
14 | })
15 | class UserRating {}
16 |
17 | @Component({
18 | selector: 'user-panel',
19 | template: ''
20 | })
21 | class UserPanel {
22 | @ViewChildren(UserBadge)
23 | viewChildren: QueryList;
24 |
25 | @ContentChildren(UserRating)
26 | contentChildren: QueryList;
27 |
28 | ngAfterViewInit() {
29 | // view children are initialized
30 | }
31 |
32 | ngAfterContentInit() {
33 | // content children are initialized
34 | }
35 | }
36 |
37 |
38 | @Component({
39 | selector: 'app',
40 | template: '',
41 | })
42 | class App {
43 | constructor() {}
44 | }
45 |
46 | @NgModule({
47 | declarations: [App, UserBadge, UserPanel, UserRating],
48 | imports: [BrowserModule],
49 | bootstrap: [App],
50 | })
51 | class AppModule {}
52 |
53 | platformBrowserDynamic().bootstrapModule(AppModule);
54 |
55 |
--------------------------------------------------------------------------------
/app/ch4/ts/view-child-content-child/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= TITLE %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Loading...
15 |
16 |
17 | <%= INIT %>
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/ch4/ts/view-child-content-child/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "ContentChildren vs ViewChildren simple example",
3 | "description": "ContentChildren vs ViewChildren simple example",
4 | "id": 11,
5 | "presented": true
6 | }
--------------------------------------------------------------------------------
/app/ch4/ts/zippy/app.ts:
--------------------------------------------------------------------------------
1 | import {Component, Input, NgModule} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {CommonModule} from '@angular/common';
4 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
5 |
6 | @Component({
7 | selector: 'zippy-header',
8 | template: '',
9 | styles: [
10 | `
11 | header {
12 | cursor: pointer;
13 | border-bottom: 1px solid #ccc;
14 | font-size: 1.2em;
15 | background-color: #eee;
16 | }
17 | `
18 | ]
19 | })
20 | class ZippyHeader {
21 | @Input() header: string;
22 | }
23 |
24 | @Component({
25 | selector: 'zippy',
26 | template: `
27 |
33 | `,
34 | styles: [
35 | `
36 | section {
37 | width: 300px;
38 | border: 1px solid #ccc;
39 | }
40 | `
41 | ]
42 | })
43 | class Zippy {
44 | @Input() header: string;
45 | visible = true;
46 | }
47 |
48 | @NgModule({
49 | declarations: [Zippy, ZippyHeader],
50 | imports: [CommonModule],
51 | exports: [Zippy]
52 | })
53 | class ZippyModule {}
54 |
55 | @Component({
56 | selector: 'my-app',
57 | template: `
58 |
59 |
60 | Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.
61 |
62 |
63 | `
64 | })
65 | class App {
66 | }
67 |
68 | @NgModule({
69 | imports: [BrowserModule, ZippyModule],
70 | declarations: [App],
71 | bootstrap: [App]
72 | })
73 | class AppModule {}
74 |
75 | platformBrowserDynamic().bootstrapModule(AppModule);
76 |
77 |
--------------------------------------------------------------------------------
/app/ch4/ts/zippy/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= TITLE %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Loading...
15 |
16 |
17 | <%= INIT %>
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/ch4/ts/zippy/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Reusable components",
3 | "description": "Reusable components",
4 | "id": 6,
5 | "presented": true
6 | }
7 |
--------------------------------------------------------------------------------
/app/ch5/es5/simple-example/app.js:
--------------------------------------------------------------------------------
1 | var Markdown = ng.core.Class({
2 | constructor: function () {},
3 | toHTML: function (md) {
4 | return markdown.toHTML(md);
5 | }
6 | });
7 |
8 | var MarkdownPanel = ng.core.Component({
9 | selector: 'markdown-panel',
10 | viewProviders: [Markdown],
11 | styles: [
12 | '.panel {' +
13 | 'width: auto;' +
14 | 'display: inline-block;' +
15 | 'border: 1px solid black;' +
16 | '}' +
17 | '.panel-title-wrapper {' +
18 | 'border-bottom: 1px solid black;' +
19 | 'background-color: #eee;' +
20 | '}' +
21 | '.panel-content-wrapper,' +
22 | '.panel-title-wrapper {' +
23 | ' padding: 5px;' +
24 | '}'
25 | ],
26 | template: '' +
27 | '
' +
28 | '' +
29 | '
' +
30 | '
' +
31 | '' +
32 | '
' +
33 | '
'
34 | })
35 | .Class({
36 | constructor: [[ng.core.Optional(), ng.core.Self(), Markdown],
37 | ng.core.ElementRef, function (md, el) {
38 | this.md = md;
39 | this.el = el;
40 | }],
41 | ngAfterContentInit: function () {
42 | var el = this.el.nativeElement;
43 | var title = el.querySelector('.panel-title');
44 | var content = el.querySelector('.panel-content');
45 | title.innerHTML = this.md.toHTML(title.innerHTML);
46 | content.innerHTML = this.md.toHTML(content.innerHTML);
47 | }
48 | });
49 |
50 | var App = ng.core.Component({
51 | selector: 'app',
52 | template: `
53 |
54 |
55 |
56 | ## Sample title
57 | * First point
58 | * Second point
59 |
60 |
61 | `
62 | })
63 | .Class({
64 | constructor: function () {}
65 | });
66 |
67 | var AppModule = ng.core.NgModule({
68 | imports: [ng.platformBrowser.BrowserModule],
69 | declarations: [MarkdownPanel, App],
70 | bootstrap: [App]
71 | })
72 | .Class({
73 | constructor: function () {}
74 | });
75 |
76 | ng.platformBrowserDynamic.platformBrowserDynamic().bootstrapModule(AppModule);
77 |
78 |
--------------------------------------------------------------------------------
/app/ch5/es5/simple-example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/ch5/es5/simple-example/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "DI ES5 example",
3 | "description": "DI ES5 example",
4 | "id": 2,
5 | "presented": true
6 | }
--------------------------------------------------------------------------------
/app/ch5/ts/configuring-providers/dummy-http.ts:
--------------------------------------------------------------------------------
1 | import 'reflect-metadata';
2 | import {
3 | ReflectiveInjector, Inject, Injectable,
4 | OpaqueToken, forwardRef
5 | } from '@angular/core';
6 |
7 | class Http {}
8 |
9 | class DummyHttp {}
10 |
11 | @Injectable()
12 | class UserService {
13 | constructor(private http: Http) {
14 | console.log(this.http instanceof DummyHttp);
15 | }
16 | }
17 |
18 | let injector = ReflectiveInjector.resolveAndCreate([
19 | UserService,
20 | { provide: Http, useClass: DummyHttp }
21 | ]);
22 |
23 |
24 | injector.get(UserService);
25 |
--------------------------------------------------------------------------------
/app/ch5/ts/configuring-providers/existing.ts:
--------------------------------------------------------------------------------
1 | import 'reflect-metadata';
2 | import {
3 | ReflectiveInjector, Inject, Injectable
4 | } from '@angular/core';
5 |
6 | class Http {}
7 |
8 | class DummyService {}
9 |
10 | @Injectable()
11 | class UserService {
12 | constructor(public http: Http) {}
13 | }
14 |
15 | // let injector = ReflectiveInjector.resolveAndCreate([
16 | // DummyService,
17 | // { provide: Http, useExisting: DummyService },
18 | // UserService
19 | // ]);
20 |
21 | // let us:UserService = injector.get(UserService);
22 |
23 | // console.log(us.http instanceof DummyService);
24 | let dummyHttp = {
25 | get() {},
26 | post() {}
27 | };
28 |
29 | let injector = ReflectiveInjector.resolveAndCreate([
30 | { provide: DummyService, useValue: dummyHttp },
31 | { provide: Http, useExisting: DummyService },
32 | UserService
33 | ]);
34 |
35 | console.assert(injector.get(UserService).http === dummyHttp);
36 |
--------------------------------------------------------------------------------
/app/ch5/ts/configuring-providers/factory.ts:
--------------------------------------------------------------------------------
1 | import 'reflect-metadata';
2 | import {
3 | ReflectiveInjector, Inject, Injectable, OpaqueToken
4 | } from '@angular/core';
5 |
6 | const BUFFER_SIZE = new OpaqueToken('buffer-size');
7 |
8 | class Buffer {
9 | constructor(@Inject(BUFFER_SIZE) private size: Number) {}
10 | }
11 |
12 | class Certificate {}
13 | class Crypto {}
14 | @Injectable()
15 | class Socket {
16 | isOpen: boolean;
17 | constructor(private buffer: Buffer) {}
18 | open() {
19 | this.isOpen = true;
20 | }
21 | }
22 |
23 | class TLSConnection {
24 | public socket: Socket;
25 | public crypto: Crypto;
26 | public certificate: Certificate;
27 | }
28 |
29 | let injector = ReflectiveInjector.resolveAndCreate([
30 | {
31 | provide: TLSConnection,
32 | useFactory: (socket: Socket, certificate: Certificate, crypto: Crypto) => {
33 | let connection = new TLSConnection();
34 | connection.certificate = certificate;
35 | connection.socket = socket;
36 | connection.crypto = crypto;
37 | socket.open();
38 | return connection;
39 | },
40 | deps: [Socket, Certificate, Crypto]
41 | },
42 | { provide: BUFFER_SIZE, useValue: 42 },
43 | Buffer,
44 | Socket,
45 | Certificate,
46 | Crypto
47 | ]);
48 |
49 | console.log(injector.get(TLSConnection));
50 |
--------------------------------------------------------------------------------
/app/ch5/ts/configuring-providers/multi-providers.ts:
--------------------------------------------------------------------------------
1 | import 'reflect-metadata';
2 | import {
3 | ReflectiveInjector, Inject, Injectable, OpaqueToken
4 | } from '@angular/core';
5 |
6 | const VALIDATOR = new OpaqueToken('validator');
7 |
8 | interface EmployeeValidator {
9 | (person: Employee): string;
10 | };
11 |
12 | class Employee {
13 | name: string;
14 | constructor(@Inject(VALIDATOR) private validators: EmployeeValidator[]) {}
15 | validate() {
16 | return this.validators
17 | .map(v => v(this))
18 | .filter(value => !!value);
19 | }
20 | }
21 |
22 | let injector = ReflectiveInjector.resolveAndCreate([
23 | {
24 | provide: VALIDATOR,
25 | multi: true,
26 | useValue: (person: Employee) => {
27 | if (!person.name) {
28 | return 'The name is required';
29 | }
30 | }
31 | },
32 | {
33 | provide: VALIDATOR,
34 | multi: true,
35 | useValue: (person: Employee) => {
36 | if (!person.name || person.name.length < 1) {
37 | return 'The name should be more than 1 symbol long';
38 | }
39 | }
40 | },
41 | Employee
42 | ]);
43 |
44 | console.log(injector.get(Employee).validate());
45 |
--------------------------------------------------------------------------------
/app/ch5/ts/decorators/optional.ts:
--------------------------------------------------------------------------------
1 | import 'reflect-metadata';
2 | import {
3 | ReflectiveInjector, Inject, Injectable, Optional
4 | } from '@angular/core';
5 |
6 | abstract class SortingAlgorithm {
7 | abstract sort(collection: BaseCollection): Collection;
8 | }
9 |
10 | class BaseCollection {
11 | getDefaultSort(): SortingAlgorithm {
12 | // get some generic sorting algorithm...
13 | return null;
14 | }
15 | }
16 |
17 | @Injectable()
18 | class Collection extends BaseCollection {
19 | private sort: SortingAlgorithm;
20 | constructor(@Optional() sort: SortingAlgorithm) {
21 | super();
22 | this.sort = sort || this.getDefaultSort();
23 | }
24 | }
25 |
26 | let injector = ReflectiveInjector.resolveAndCreate([
27 | Collection
28 | ]);
29 |
30 | console.log(injector.get(Collection).sort === null);
31 |
--------------------------------------------------------------------------------
/app/ch5/ts/decorators/self.ts:
--------------------------------------------------------------------------------
1 | import 'reflect-metadata';
2 | import {
3 | ReflectiveInjector, Inject, Injectable, Self
4 | } from '@angular/core';
5 |
6 | abstract class Channel {}
7 |
8 | class Http extends Channel {}
9 |
10 | class WebSocket extends Channel {}
11 |
12 | @Injectable()
13 | class UserService {
14 | constructor(@Self() public channel: Channel) {}
15 | }
16 |
17 | let parentInjector = ReflectiveInjector.resolveAndCreate([
18 | { provide: Channel, useClass: Http }
19 | ]);
20 | let childInjector = parentInjector.resolveAndCreateChild([
21 | { provide: Channel, useClass: WebSocket },
22 | UserService
23 | ]);
24 |
25 | console.log(childInjector.get(UserService).channel instanceof WebSocket);
26 |
--------------------------------------------------------------------------------
/app/ch5/ts/decorators/skip-self.ts:
--------------------------------------------------------------------------------
1 | import 'reflect-metadata';
2 | import {
3 | ReflectiveInjector, Inject, Injectable, SkipSelf
4 | } from '@angular/core';
5 |
6 | class Context {
7 | constructor(@SkipSelf() public parentContext: Context) {}
8 | }
9 |
10 | let parentInjector = ReflectiveInjector.resolveAndCreate([
11 | { provide: Context, useValue: new Context(null) }
12 | ]);
13 | let childInjector = parentInjector.resolveAndCreateChild([
14 | Context
15 | ]);
16 |
17 | console.log(childInjector.get(Context).parentContext instanceof Context);
18 |
--------------------------------------------------------------------------------
/app/ch5/ts/directives-ngmodules/app.ts:
--------------------------------------------------------------------------------
1 | import {Component, ElementRef, NgModule, ViewChild} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
4 | import * as markdown from 'markdown';
5 |
6 | let instance: Markdown;
7 |
8 | class Markdown {
9 | toHTML(md) {
10 | return markdown.toHTML(md);
11 | }
12 | }
13 |
14 | @Component({
15 | selector: 'markdown-panel',
16 | styles: [
17 | `.panel {
18 | width: auto;
19 | display: inline-block;
20 | border: 1px solid black;
21 | }
22 | .panel-title-wrapper {
23 | border-bottom: 1px solid black;
24 | background-color: #eee;
25 | }
26 | .panel-content-wrapper,
27 | .panel-title-wrapper {
28 | padding: 5px;
29 | }`
30 | ],
31 | template: `
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
`
40 | })
41 | class MarkdownPanel {
42 | constructor(private el: ElementRef, private md: Markdown) {}
43 | ngAfterContentInit() {
44 | instance = this.md;
45 | let el = this.el.nativeElement;
46 | let title = el.querySelector('.panel-title');
47 | let content = el.querySelector('.panel-content');
48 | title.innerHTML = this.md.toHTML(title.innerHTML);
49 | content.innerHTML = this.md.toHTML(content.innerHTML);
50 | }
51 | }
52 |
53 | @Component({
54 | selector: 'app',
55 | template: `
56 |
57 |
58 |
59 | ## Sample title
60 | * First point
61 | * Second point
62 |
63 |
64 | **Click me**
65 | `
66 | })
67 | class App {
68 | constructor() {}
69 | }
70 |
71 | @Component({
72 | selector: 'btn',
73 | template: ''
74 | })
75 | class Button {
76 | @ViewChild('btn') button: ElementRef;
77 |
78 | constructor(private md: Markdown) {}
79 |
80 | ngAfterContentInit() {
81 | const el = this.button.nativeElement;
82 | el.innerHTML = this.md.toHTML(el.innerHTML);
83 | console.log(instance === this.md);
84 | }
85 | }
86 |
87 | @NgModule({
88 | declarations: [Button],
89 | exports: [Button],
90 | providers: [Markdown],
91 | })
92 | class ButtonModule {}
93 |
94 | @NgModule({
95 | declarations: [App, MarkdownPanel],
96 | imports: [BrowserModule, ButtonModule],
97 | bootstrap: [App],
98 | })
99 | class AppModule {}
100 |
101 | platformBrowserDynamic().bootstrapModule(AppModule);
102 |
103 |
--------------------------------------------------------------------------------
/app/ch5/ts/directives-ngmodules/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= TITLE %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Loading...
15 |
16 |
17 |
20 | <%= INIT %>
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/ch5/ts/directives-ngmodules/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "NgModules DI",
3 | "description": "NgModules DI",
4 | "id": 2,
5 | "presented": true
6 | }
--------------------------------------------------------------------------------
/app/ch5/ts/directives-ngmodules/util.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mgechev/getting-started-with-angular/8e6c36a992b06dbd843bb0aff869ed47cd321c61/app/ch5/ts/directives-ngmodules/util.js
--------------------------------------------------------------------------------
/app/ch5/ts/directives/app.ts:
--------------------------------------------------------------------------------
1 | import {Component, ElementRef, NgModule} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
4 | import * as markdown from 'markdown';
5 |
6 | class Markdown {
7 | toHTML(md) {
8 | return markdown.toHTML(md);
9 | }
10 | }
11 |
12 | @Component({
13 | selector: 'markdown-panel',
14 | viewProviders: [Markdown],
15 | styles: [
16 | `.panel {
17 | width: auto;
18 | display: inline-block;
19 | border: 1px solid black;
20 | }
21 | .panel-title-wrapper {
22 | border-bottom: 1px solid black;
23 | background-color: #eee;
24 | }
25 | .panel-content-wrapper,
26 | .panel-title-wrapper {
27 | padding: 5px;
28 | }`
29 | ],
30 | template: `
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
`
39 | })
40 | class MarkdownPanel {
41 | constructor(private el: ElementRef, private md: Markdown) {}
42 | ngAfterContentInit() {
43 | let el = this.el.nativeElement;
44 | let title = el.querySelector('.panel-title');
45 | let content = el.querySelector('.panel-content');
46 | title.innerHTML = this.md.toHTML(title.innerHTML);
47 | content.innerHTML = this.md.toHTML(content.innerHTML);
48 | }
49 | }
50 |
51 | @Component({
52 | selector: 'app',
53 | template: `
54 |
55 |
56 |
57 | ## Sample title
58 | * First point
59 | * Second point
60 |
61 |
62 | `
63 | })
64 | class App {
65 | constructor() {}
66 | }
67 |
68 | @NgModule({
69 | declarations: [App, MarkdownPanel],
70 | imports: [BrowserModule],
71 | bootstrap: [App],
72 | })
73 | class AppModule {}
74 |
75 | platformBrowserDynamic().bootstrapModule(AppModule);
76 |
77 |
--------------------------------------------------------------------------------
/app/ch5/ts/directives/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= TITLE %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Loading...
15 |
16 |
17 |
20 | <%= INIT %>
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/ch5/ts/directives/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Components & Directives DI",
3 | "description": "Components & Directives DI",
4 | "id": 1,
5 | "presented": true
6 | }
--------------------------------------------------------------------------------
/app/ch5/ts/directives/util.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mgechev/getting-started-with-angular/8e6c36a992b06dbd843bb0aff869ed47cd321c61/app/ch5/ts/directives/util.js
--------------------------------------------------------------------------------
/app/ch5/ts/injector-basics/forward-ref.ts:
--------------------------------------------------------------------------------
1 | import 'reflect-metadata';
2 | import {
3 | ReflectiveInjector, Inject, Injectable,
4 | OpaqueToken, forwardRef
5 | } from '@angular/core';
6 |
7 | const BUFFER_SIZE = new OpaqueToken('buffer-size');
8 |
9 | @Injectable()
10 | class Socket {
11 | constructor(@Inject(forwardRef(() => Buffer)) private buffer: Buffer) {}
12 | }
13 |
14 | // undefined
15 | console.log(Buffer);
16 |
17 | class Buffer {
18 | constructor(@Inject(BUFFER_SIZE) private size:Number) {
19 | console.log(this.size);
20 | }
21 | }
22 |
23 | // [Function: Buffer]
24 | console.log(Buffer);
25 |
26 | let injector = ReflectiveInjector.resolveAndCreate([
27 | { provide: BUFFER_SIZE, useValue: 42 },
28 | Buffer,
29 | Socket
30 | ]);
31 |
32 | console.log(injector.get(Socket));
33 |
--------------------------------------------------------------------------------
/app/ch5/ts/injector-basics/injector.ts:
--------------------------------------------------------------------------------
1 | import 'reflect-metadata';
2 | import {
3 | ReflectiveInjector,
4 | Inject,
5 | Injectable,
6 | OpaqueToken
7 | } from '@angular/core';
8 |
9 | const BUFFER_SIZE = new OpaqueToken('buffer-size');
10 |
11 | class Buffer {
12 | constructor(@Inject(BUFFER_SIZE) private size: Number) {
13 | console.log(this.size);
14 | }
15 | }
16 |
17 | @Injectable()
18 | class Socket {
19 | constructor(private buffer: Buffer) {}
20 | }
21 |
22 | let injector = ReflectiveInjector.resolveAndCreate([
23 | { provide: BUFFER_SIZE, useValue: 42 },
24 | Buffer,
25 | Socket
26 | ]);
27 |
28 | injector.get(Socket);
29 |
--------------------------------------------------------------------------------
/app/ch5/ts/parent-child/simple-example.ts:
--------------------------------------------------------------------------------
1 | import 'reflect-metadata';
2 | import {
3 | ReflectiveInjector, Inject, Injectable
4 | } from '@angular/core';
5 |
6 | class Http {}
7 |
8 | @Injectable()
9 | class UserService {
10 | constructor(public http: Http) {}
11 | }
12 |
13 | let parentInjector = ReflectiveInjector.resolveAndCreate([
14 | Http
15 | ]);
16 |
17 | let childInjector = parentInjector.resolveAndCreateChild([
18 | UserService
19 | ]);
20 |
21 | console.log(childInjector.get(UserService));
22 | console.log(childInjector.get(Http) === parentInjector.get(Http));
23 |
--------------------------------------------------------------------------------
/app/ch6/ts/multi-page-template-driven/add_developer.html:
--------------------------------------------------------------------------------
1 | {{errorMessage}}
2 | {{successMessage}}
3 |
--------------------------------------------------------------------------------
/app/ch6/ts/multi-page-template-driven/add_developer.ts:
--------------------------------------------------------------------------------
1 | import {Component, Directive} from '@angular/core';
2 | import {NG_VALIDATORS} from '@angular/forms';
3 |
4 | import {Developer} from './developer';
5 | import {DeveloperCollection} from './developer_collection';
6 |
7 | function validateEmail(emailControl) {
8 | if (!emailControl.value || /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/.test(emailControl.value)) {
9 | return null;
10 | } else {
11 | return { 'invalidEmail': true };
12 | }
13 | }
14 |
15 | @Directive({
16 | selector: '[email-input]',
17 | providers: [{
18 | provide: NG_VALIDATORS,
19 | multi: true,
20 | useValue: validateEmail
21 | }]
22 | })
23 | export class EmailValidator {}
24 |
25 | @Component({
26 | selector: 'dev-add',
27 | templateUrl: './add_developer.html',
28 | styles: [
29 | `input.ng-touched.ng-invalid,
30 | select.ng-touched.ng-invalid {
31 | border: 1px solid red;
32 | }`
33 | ]
34 | })
35 | export class AddDeveloper {
36 | developer = new Developer();
37 | errorMessage: string;
38 | successMessage: string;
39 | submitted = false;
40 | technologies: string[] = [
41 | 'JavaScript',
42 | 'C',
43 | 'C#',
44 | 'Clojure'
45 | ];
46 |
47 | constructor(private developers: DeveloperCollection) {}
48 |
49 | addDeveloper() {
50 | this.developer.id = this.developers.getAll().length + 1;
51 | this.developers.addDeveloper(this.developer);
52 | this.successMessage = `Developer ${this.developer.realName} was successfully added`;
53 | this.submitted = true;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/app/ch6/ts/multi-page-template-driven/app.ts:
--------------------------------------------------------------------------------
1 | import {Component, NgModule} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
4 | import {LocationStrategy, HashLocationStrategy} from '@angular/common';
5 | import {RouterModule} from '@angular/router';
6 | import {FormsModule} from '@angular/forms';
7 | import {Home} from './home';
8 | import {DeveloperCollection} from './developer_collection';
9 | import {AddDeveloper} from './add_developer';
10 | import {ControlErrors} from './control_errors';
11 | import {EmailValidator} from './email_validator';
12 |
13 | @Component({
14 | selector: 'app',
15 | template: `
16 |
22 |
23 | `,
24 | providers: [DeveloperCollection]
25 | })
26 | class App {}
27 |
28 | const routingModule = RouterModule.forRoot([
29 | {
30 | path: '',
31 | pathMatch: 'full',
32 | redirectTo: 'home'
33 | },
34 | {
35 | component: Home,
36 | path: 'home'
37 | },
38 | {
39 | component: AddDeveloper,
40 | path: 'dev-add'
41 | },
42 | {
43 | path: 'add-dev',
44 | redirectTo: 'dev-add',
45 | }
46 | ]);
47 |
48 | @NgModule({
49 | imports: [BrowserModule, FormsModule, routingModule],
50 | declarations: [App, Home, AddDeveloper, ControlErrors, EmailValidator],
51 | providers: [{ provide: LocationStrategy, useClass: HashLocationStrategy }],
52 | bootstrap: [App]
53 | })
54 | class AppModule {}
55 |
56 | platformBrowserDynamic().bootstrapModule(AppModule);
57 |
--------------------------------------------------------------------------------
/app/ch6/ts/multi-page-template-driven/control_errors.ts:
--------------------------------------------------------------------------------
1 | import {Component, Input, Host} from '@angular/core';
2 | import {NgForm} from '@angular/forms';
3 |
4 | @Component({
5 | template: `{{currentError}}
`,
6 | selector: 'control-errors'
7 | })
8 | export class ControlErrors {
9 | @Input() errors: Object;
10 | @Input() control: string;
11 |
12 | constructor(@Host() private formDir: NgForm) {}
13 |
14 | get currentError() {
15 | let control = this.formDir.controls[this.control];
16 | let errorMessages = [];
17 | if (control && control.touched) {
18 | errorMessages = Object.keys(this.errors)
19 | .map(k => control.hasError(k) ? this.errors[k] : null)
20 | .filter(error => !!error);
21 | }
22 | return errorMessages.pop();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/ch6/ts/multi-page-template-driven/developer.ts:
--------------------------------------------------------------------------------
1 | export class Developer {
2 | public id: number;
3 | public githubHandle: string;
4 | public avatarUrl: string;
5 | public realName: string;
6 | public email: string;
7 | public technology: string;
8 | public popular: boolean;
9 | }
10 |
--------------------------------------------------------------------------------
/app/ch6/ts/multi-page-template-driven/developer_collection.ts:
--------------------------------------------------------------------------------
1 | import {Developer} from './developer';
2 |
3 | export class DeveloperCollection {
4 | private developers: Developer[] = [];
5 |
6 | getUserByGitHubHandle(username: string) {
7 | return this.developers.filter(u => u.githubHandle === username).pop();
8 | }
9 |
10 | getUserById(id: number) {
11 | return this.developers.filter(u => u.id === id).pop();
12 | }
13 |
14 | addDeveloper(dev: Developer) {
15 | this.developers.push(dev);
16 | }
17 |
18 | getAll() {
19 | return this.developers;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/ch6/ts/multi-page-template-driven/email_validator.ts:
--------------------------------------------------------------------------------
1 | import {Directive} from '@angular/core';
2 | import {NG_VALIDATORS} from '@angular/forms';
3 |
4 | function validateEmail(emailControl) {
5 | if (!emailControl.value || /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/.test(emailControl.value)) {
6 | return null;
7 | } else {
8 | return { 'invalidEmail': true };
9 | }
10 | }
11 |
12 | @Directive({
13 | selector: '[email-input]',
14 | providers: [{ provide: NG_VALIDATORS, useValue: validateEmail, multi: true }]
15 | })
16 | export class EmailValidator {}
17 |
--------------------------------------------------------------------------------
/app/ch6/ts/multi-page-template-driven/home.html:
--------------------------------------------------------------------------------
1 | 0">
2 |
3 | Email |
4 | Real name |
5 | Technology |
6 | Popular |
7 |
8 |
9 | {{dev.email}} |
10 | {{dev.realName}} |
11 | {{dev.technology}} |
12 |
13 | Yes
14 | Not yet
15 | |
16 |
17 |
18 | There are no any developers yet
19 |
--------------------------------------------------------------------------------
/app/ch6/ts/multi-page-template-driven/home.ts:
--------------------------------------------------------------------------------
1 | import {Component} from '@angular/core';
2 | import {DeveloperCollection} from './developer_collection';
3 |
4 | @Component({
5 | selector: 'home',
6 | templateUrl: './home.html'
7 | })
8 | export class Home {
9 | constructor(private developers: DeveloperCollection) {}
10 |
11 | getDevelopers() {
12 | return this.developers.getAll();
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/ch6/ts/multi-page-template-driven/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= APP_TITLE %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Loading...
17 |
18 |
19 | <%= INIT %>
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/ch6/ts/multi-page-template-driven/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Page with multiple views and template-driven form",
3 | "description": "Page with multiple views and template-driven form",
4 | "id": 1,
5 | "presented": true
6 | }
--------------------------------------------------------------------------------
/app/ch6/ts/simple-two-way-data-binding/app.ts:
--------------------------------------------------------------------------------
1 | import {Component, NgModule} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {FormsModule} from '@angular/forms';
4 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
5 |
6 | @Component({
7 | selector: 'app',
8 | template: `
9 |
10 | {{name}}
11 | `
12 | })
13 | class App {
14 | name: string;
15 | }
16 |
17 | @NgModule({
18 | imports: [BrowserModule, FormsModule],
19 | declarations: [App],
20 | bootstrap: [App]
21 | })
22 | class AppModule {}
23 |
24 | platformBrowserDynamic().bootstrapModule(AppModule);
25 |
--------------------------------------------------------------------------------
/app/ch6/ts/simple-two-way-data-binding/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%= APP_TITLE %>
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | <%= INIT %>
14 |
15 |
--------------------------------------------------------------------------------
/app/ch6/ts/simple-two-way-data-binding/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Simple two-way data-binding",
3 | "description": "Simple two-way data-binding",
4 | "id": 2,
5 | "presented": true
6 | }
--------------------------------------------------------------------------------
/app/ch6/ts/step-0/app.ts:
--------------------------------------------------------------------------------
1 | import {NgModule, Component} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
4 | import {LocationStrategy, HashLocationStrategy} from '@angular/common';
5 | import {RouterModule} from '@angular/router';
6 | // import {HomeModule} from './home';
7 | import {DeveloperCollection} from './developer_collection';
8 | // import {AddDeveloper} from './add_developer';
9 |
10 | @Component({
11 | selector: 'app',
12 | template: `...`,
13 | providers: [DeveloperCollection]
14 | })
15 | class App {}
16 |
17 | const routeModule = RouterModule.forRoot([]);
18 |
19 | @NgModule({
20 | declarations: [App],
21 | bootstrap: [App],
22 | imports: [BrowserModule, routeModule],
23 | providers: [{ provide: LocationStrategy, useClass: HashLocationStrategy }]
24 | })
25 | class AppModule {}
26 |
27 | platformBrowserDynamic().bootstrapModule(AppModule);
28 |
29 |
--------------------------------------------------------------------------------
/app/ch6/ts/step-0/developer.ts:
--------------------------------------------------------------------------------
1 | export class Developer {
2 | public id: number;
3 | public githubHandle: string;
4 | public avatarUrl: string;
5 | public realName: string;
6 | public email: string;
7 | public technology: string;
8 | public popular: boolean;
9 | }
10 |
--------------------------------------------------------------------------------
/app/ch6/ts/step-0/developer_collection.ts:
--------------------------------------------------------------------------------
1 | import {Developer} from './developer';
2 |
3 | export class DeveloperCollection {
4 | private developers: Developer[];
5 |
6 | constructor() {
7 | this.developers = [];
8 | }
9 |
10 | getUserByGitHubHandle(username: string) {
11 | return this.developers.filter(u => u.githubHandle === username).pop();
12 | }
13 |
14 | getUserById(id: number) {
15 | return this.developers.filter(u => u.id === id).pop();
16 | }
17 |
18 | addDeveloper(dev: Developer) {
19 | this.developers.push(dev);
20 | }
21 |
22 | getAll() {
23 | return this.developers;
24 | }
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/app/ch6/ts/step-0/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= TITLE %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Loading...
17 |
18 |
19 | <%= INIT %>
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/ch6/ts/step-1-async/add_developer.ts:
--------------------------------------------------------------------------------
1 | import {Component, NgModule} from '@angular/core';
2 | import {RouterModule} from '@angular/router';
3 |
4 | @Component({
5 | selector: 'dev-add',
6 | template: `Add Developer`
7 | })
8 | export class AddDeveloper {}
9 |
10 | @NgModule({
11 | declarations: [AddDeveloper],
12 | imports: [RouterModule.forChild([
13 | {
14 | path: '',
15 | component: AddDeveloper
16 | }
17 | ])]
18 | })
19 | export class AddDeveloperModule {}
20 |
--------------------------------------------------------------------------------
/app/ch6/ts/step-1-async/app.ts:
--------------------------------------------------------------------------------
1 | import {NgModule, Component} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
4 | import {LocationStrategy, HashLocationStrategy} from '@angular/common';
5 | import {RouterModule} from '@angular/router';
6 | import {DeveloperCollection} from './developer_collection';
7 |
8 | @Component({
9 | selector: 'app',
10 | template: `
11 |
17 |
18 | `
19 | })
20 | class App {}
21 |
22 | const routingModule = RouterModule.forRoot([
23 | {
24 | path: '',
25 | redirectTo: 'home',
26 | pathMatch: 'full'
27 | },
28 | {
29 | path: 'home',
30 | loadChildren: './home#HomeModule'
31 | },
32 | {
33 | path: 'dev-add',
34 | loadChildren: './add_developer#AddDeveloperModule'
35 | }
36 | ]);
37 |
38 | @NgModule({
39 | imports: [BrowserModule, routingModule],
40 | providers: [{ provide: LocationStrategy, useClass: HashLocationStrategy }, DeveloperCollection],
41 | declarations: [App],
42 | bootstrap: [App]
43 | })
44 | class AppModule {}
45 |
46 | platformBrowserDynamic().bootstrapModule(AppModule);
47 |
--------------------------------------------------------------------------------
/app/ch6/ts/step-1-async/developer.ts:
--------------------------------------------------------------------------------
1 | export class Developer {
2 | public id: number;
3 | public githubHandle: string;
4 | public avatarUrl: string;
5 | public realName: string;
6 | public email: string;
7 | public technology: string;
8 | public popular: boolean;
9 | }
10 |
--------------------------------------------------------------------------------
/app/ch6/ts/step-1-async/developer_collection.ts:
--------------------------------------------------------------------------------
1 | import {Developer} from './developer';
2 |
3 | export class DeveloperCollection {
4 | private developers: Developer[] = [];
5 |
6 | getUserByGitHubHandle(username: string) {
7 | return this.developers.filter(u => u.githubHandle === username).pop();
8 | }
9 |
10 | getUserById(id: number) {
11 | return this.developers.filter(u => u.id === id).pop();
12 | }
13 |
14 | addDeveloper(dev: Developer) {
15 | this.developers.push(dev);
16 | }
17 |
18 | getAll() {
19 | return this.developers;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/ch6/ts/step-1-async/home.ts:
--------------------------------------------------------------------------------
1 | import {Component, NgModule} from '@angular/core';
2 | import {RouterModule} from '@angular/router';
3 |
4 | @Component({
5 | selector: 'home',
6 | template: `Home`
7 | })
8 | export class Home {}
9 |
10 | @NgModule({
11 | declarations: [Home],
12 | imports: [RouterModule.forChild([
13 | {
14 | path: '',
15 | component: Home
16 | }
17 | ])]
18 | })
19 | export class HomeModule {}
20 |
--------------------------------------------------------------------------------
/app/ch6/ts/step-1-async/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | <%= TITLE %>
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | Loading...
18 |
19 |
20 | <%= INIT %>
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/ch6/ts/step-1/add_developer.ts:
--------------------------------------------------------------------------------
1 | import {Component} from '@angular/core';
2 |
3 | @Component({
4 | selector: 'dev-add',
5 | template: `Add developer`
6 | })
7 | export class AddDeveloper {}
8 |
--------------------------------------------------------------------------------
/app/ch6/ts/step-1/app.ts:
--------------------------------------------------------------------------------
1 | import {NgModule, Component} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
4 | import {LocationStrategy, HashLocationStrategy} from '@angular/common';
5 | import {RouterModule} from '@angular/router';
6 | import {Home} from './home';
7 | import {DeveloperCollection} from './developer_collection';
8 | import {AddDeveloper} from './add_developer';
9 |
10 | @Component({
11 | selector: 'app',
12 | template: `
13 |
19 |
20 | `,
21 | providers: [DeveloperCollection]
22 | })
23 | class App {}
24 |
25 | const routingModule = RouterModule.forRoot([
26 | {
27 | path: '',
28 | redirectTo: 'home',
29 | pathMatch: 'full'
30 | },
31 | {
32 | path: 'home',
33 | component: Home
34 | },
35 | {
36 | path: 'dev-add',
37 | component: AddDeveloper
38 | },
39 | {
40 | path: 'add-dev',
41 | redirectTo: 'dev-add'
42 | }
43 | ]);
44 |
45 | @NgModule({
46 | imports: [BrowserModule, routingModule],
47 | declarations: [App, Home, AddDeveloper],
48 | providers: [{ provide: LocationStrategy, useClass: HashLocationStrategy }],
49 | bootstrap: [App]
50 | })
51 | class AppModule {}
52 |
53 | platformBrowserDynamic().bootstrapModule(AppModule);
54 |
--------------------------------------------------------------------------------
/app/ch6/ts/step-1/developer.ts:
--------------------------------------------------------------------------------
1 | export class Developer {
2 | public id: number;
3 | public githubHandle: string;
4 | public avatarUrl: string;
5 | public realName: string;
6 | public email: string;
7 | public technology: string;
8 | public popular: boolean;
9 | }
10 |
--------------------------------------------------------------------------------
/app/ch6/ts/step-1/developer_collection.ts:
--------------------------------------------------------------------------------
1 | import {Developer} from './developer';
2 |
3 | export class DeveloperCollection {
4 | private developers: Developer[] = [];
5 |
6 | getUserByGitHubHandle(username: string) {
7 | return this.developers.filter(u => u.githubHandle === username).pop();
8 | }
9 |
10 | getUserById(id: number) {
11 | return this.developers.filter(u => u.id === id).pop();
12 | }
13 |
14 | addDeveloper(dev: Developer) {
15 | this.developers.push(dev);
16 | }
17 |
18 | getAll() {
19 | return this.developers;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/ch6/ts/step-1/home.ts:
--------------------------------------------------------------------------------
1 | import {Component} from '@angular/core';
2 |
3 | @Component({
4 | selector: 'home',
5 | template: `Home`
6 | })
7 | export class Home {}
8 |
--------------------------------------------------------------------------------
/app/ch6/ts/step-1/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | <%= APP_TITLE %>
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | Loading...
18 |
19 |
20 | <%= INIT %>
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/ch6/ts/step-2/add_developer.html:
--------------------------------------------------------------------------------
1 | {{errorMessage}}
2 | {{successMessage}}
3 |
29 |
--------------------------------------------------------------------------------
/app/ch6/ts/step-2/add_developer.ts:
--------------------------------------------------------------------------------
1 | import {Component} from '@angular/core';
2 | import {Developer} from './developer';
3 | import {DeveloperCollection} from './developer_collection';
4 |
5 | @Component({
6 | selector: 'dev-add',
7 | templateUrl: './add_developer.html',
8 | styles: [
9 | `input.ng-touched.ng-invalid,
10 | select.ng-touched.ng-invalid {
11 | border: 1px solid red;
12 | }`
13 | ]
14 | })
15 | export class AddDeveloper {
16 | developer = new Developer();
17 | errorMessage: string;
18 | successMessage: string;
19 | submitted = false;
20 | technologies: string[] = [
21 | 'JavaScript',
22 | 'C',
23 | 'C#',
24 | 'Clojure'
25 | ];
26 |
27 | constructor(private developers: DeveloperCollection) {}
28 |
29 | addDeveloper() {}
30 | }
31 |
--------------------------------------------------------------------------------
/app/ch6/ts/step-2/app.ts:
--------------------------------------------------------------------------------
1 | import {Component, NgModule} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
4 | import {LocationStrategy, HashLocationStrategy} from '@angular/common';
5 | import {RouterModule} from '@angular/router';
6 | import {FormsModule} from '@angular/forms';
7 | // import {Home} from './home';
8 | import {DeveloperCollection} from './developer_collection';
9 | import {AddDeveloper} from './add_developer';
10 | import {ControlErrors} from './control_errors';
11 | import {EmailValidator} from './email_validator';
12 |
13 | @Component({
14 | selector: 'app',
15 | template: `
16 |
22 |
23 | `,
24 | providers: [DeveloperCollection]
25 | })
26 | class App {}
27 |
28 | const routingModule = RouterModule.forRoot([
29 | {
30 | path: '',
31 | pathMatch: 'full',
32 | redirectTo: 'home'
33 | },
34 | // {
35 | // component: Home,
36 | // path: 'home'
37 | // },
38 | {
39 | component: AddDeveloper,
40 | path: 'dev-add'
41 | },
42 | {
43 | path: 'add-dev',
44 | redirectTo: 'dev-add'
45 | }
46 | ]);
47 |
48 | @NgModule({
49 | imports: [BrowserModule, FormsModule, routingModule],
50 | declarations: [App, /*Home*/, AddDeveloper, ControlErrors, EmailValidator],
51 | providers: [{ provide: LocationStrategy, useClass: HashLocationStrategy }],
52 | bootstrap: [App]
53 | })
54 | class AppModule {}
55 |
56 | platformBrowserDynamic().bootstrapModule(AppModule);
57 |
--------------------------------------------------------------------------------
/app/ch6/ts/step-2/control_errors.ts:
--------------------------------------------------------------------------------
1 | import {Component, Input, Host} from '@angular/core';
2 | import {NgForm} from '@angular/forms';
3 |
4 | @Component({
5 | template: `{{currentError}}
`,
6 | selector: 'control-errors'
7 | })
8 | export class ControlErrors {
9 | @Input() errors: Object;
10 | @Input() control: string;
11 |
12 | constructor(@Host() private formDir: NgForm) {}
13 |
14 | get currentError() {
15 | let control = this.formDir.controls[this.control];
16 | let errorMessages = [];
17 | if (control && control.touched) {
18 | errorMessages = Object.keys(this.errors)
19 | .map(k => control.hasError(k) ? this.errors[k] : null)
20 | .filter(error => !!error);
21 | }
22 | return errorMessages.pop();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/ch6/ts/step-2/developer.ts:
--------------------------------------------------------------------------------
1 | export class Developer {
2 | public id: number;
3 | public githubHandle: string;
4 | public avatarUrl: string;
5 | public realName: string;
6 | public email: string;
7 | public technology: string;
8 | public popular: boolean;
9 | }
10 |
--------------------------------------------------------------------------------
/app/ch6/ts/step-2/developer_collection.ts:
--------------------------------------------------------------------------------
1 | import {Developer} from './developer';
2 |
3 | export class DeveloperCollection {
4 | private developers: Developer[];
5 |
6 | constructor() {
7 | this.developers = [];
8 | }
9 |
10 | getUserByGitHubHandle(username: string) {
11 | return this.developers.filter(u => u.githubHandle === username).pop();
12 | }
13 |
14 | getUserById(id: number) {
15 | return this.developers.filter(u => u.id === id).pop();
16 | }
17 |
18 | addDeveloper(dev: Developer) {
19 | this.developers.push(dev);
20 | }
21 |
22 | getAll() {
23 | return this.developers;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/ch6/ts/step-2/email_validator.ts:
--------------------------------------------------------------------------------
1 | import {Directive} from '@angular/core';
2 | import {NG_VALIDATORS} from '@angular/forms';
3 |
4 | function validateEmail(emailControl) {
5 | if (!emailControl.value || /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/.test(emailControl.value)) {
6 | return null;
7 | } else {
8 | return { 'invalidEmail': true };
9 | }
10 | }
11 |
12 | @Directive({
13 | selector: '[email-input]',
14 | providers: [{ provide: NG_VALIDATORS, useValue: validateEmail, multi: true }]
15 | })
16 | export class EmailValidator {}
17 |
--------------------------------------------------------------------------------
/app/ch6/ts/step-2/home.html:
--------------------------------------------------------------------------------
1 | 0">
2 |
3 | Name |
4 | Email |
5 | Real name |
6 | Technology |
7 | Popular |
8 |
9 |
10 |
11 | {{dev.githubHandle}}
12 | |
13 | {{dev.email}} |
14 | {{dev.realName}} |
15 | {{dev.technology}} |
16 |
17 | Yes
18 | Not yet
19 | |
20 |
21 |
22 | There are no any developers yet
23 |
--------------------------------------------------------------------------------
/app/ch6/ts/step-2/home.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mgechev/getting-started-with-angular/8e6c36a992b06dbd843bb0aff869ed47cd321c61/app/ch6/ts/step-2/home.ts
--------------------------------------------------------------------------------
/app/ch6/ts/step-2/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= TITLE %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Loading...
17 |
18 |
19 | <%= INIT %>
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/ch7/ts/async_pipe/app.ts:
--------------------------------------------------------------------------------
1 | import {NgModule, Component, Pipe, PipeTransform} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {Observable} from 'rxjs/Observable';
4 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
5 | import {HttpModule, Http} from '@angular/http';
6 |
7 | @Component({
8 | selector: 'greeting',
9 | template: 'Hello {{ greetingPromise | async }}'
10 | })
11 | class Greeting {
12 | username: string;
13 | greetingPromise = new Promise(resolve => this.resolve = resolve);
14 | resolve: Function;
15 | constructor() {
16 | setTimeout(_ => {
17 | this.resolve('Foobar!');
18 | }, 3000);
19 | }
20 | }
21 |
22 | @Component({
23 | selector: 'timer',
24 | template: '{{ timer | async | date: "medium" }}'
25 | })
26 | class Timer {
27 | username: string;
28 | timer: Observable;
29 | constructor() {
30 | let counter = 0;
31 | this.timer = Observable.create(observer => {
32 | setInterval(() => {
33 | observer.next(new Date().getTime());
34 | }, 1000);
35 | });
36 | }
37 | }
38 |
39 | @Component({
40 | selector: 'app',
41 | template: '
'
42 | })
43 | class App {}
44 |
45 | @NgModule({
46 | imports: [HttpModule, BrowserModule],
47 | declarations: [App, Greeting, Timer],
48 | bootstrap: [App]
49 | })
50 | class AppModule {}
51 |
52 | platformBrowserDynamic().bootstrapModule(AppModule);
53 |
--------------------------------------------------------------------------------
/app/ch7/ts/async_pipe/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | <%= INIT %>
14 |
15 |
--------------------------------------------------------------------------------
/app/ch7/ts/async_pipe/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Async pipe",
3 | "description": "Async pipe demo",
4 | "id": 6,
5 | "presented": true
6 | }
--------------------------------------------------------------------------------
/app/ch7/ts/builtin_pipes/app.ts:
--------------------------------------------------------------------------------
1 | import {NgModule, Component} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
4 |
5 | @Component({
6 | selector: 'app',
7 | template: `
8 |
9 | - CurrencyPipe - {{ currencyValue | currency: 'USD' }}
10 | - DatePipe - {{ dateValue | date: 'shortTime' }}
11 | - DecimalPipe - {{ decimalValue | number: '3.1-2' }}
12 | - JsonPipe - {{ jsObject | json }}
13 | - LowerCasePipe - {{ uppercaseValue | lowercase }}
14 | - UpperCaseFilter - {{ lowercaseValue | uppercase }}
15 | - PercentPipe - {{ percentValue | percent: '2.1-2' }}
16 | - SlicePipe - {{ array | slice: 1: 2 }}
17 |
18 | `
19 | })
20 | class App {
21 | currencyValue = 42;
22 | dateValue = new Date('02/11/2010');
23 | decimalValue = 42.1618;
24 | jsObject = { foo: 'bar' };
25 | uppercaseValue = 'FOOBAR';
26 | lowercaseValue = 'foobar';
27 | percentValue = 42;
28 | array = [1, 2, 3];
29 | }
30 |
31 | @NgModule({
32 | imports: [BrowserModule],
33 | declarations: [App],
34 | bootstrap: [App]
35 | })
36 | class AppModule {}
37 |
38 | platformBrowserDynamic().bootstrapModule(AppModule);
39 |
--------------------------------------------------------------------------------
/app/ch7/ts/builtin_pipes/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | <%= INIT %>
25 |
26 |
--------------------------------------------------------------------------------
/app/ch7/ts/builtin_pipes/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Built-in pipes",
3 | "description": "Built-in pipes demo",
4 | "id": 4,
5 | "presented": true
6 | }
--------------------------------------------------------------------------------
/app/ch7/ts/multi-page-model-driven/add_developer.html:
--------------------------------------------------------------------------------
1 | {{errorMessage}}
2 | {{successMessage}}
3 |
4 |
20 |
21 |
60 |
61 |
62 |
63 |
65 |
--------------------------------------------------------------------------------
/app/ch7/ts/multi-page-model-driven/add_developer.ts:
--------------------------------------------------------------------------------
1 | import {Component, OnInit, OnDestroy} from '@angular/core';
2 | import {FormBuilder, FormGroup, Validators} from '@angular/forms';
3 | import {Response} from '@angular/http';
4 | import {GitHubGateway} from './github_gateway';
5 | import {Developer} from './developer';
6 | import {DeveloperCollection} from './developer_collection';
7 |
8 | import {Subscription} from 'rxjs/Subscription';
9 | import 'rxjs/add/operator/map';
10 | import 'rxjs/add/operator/catch';
11 |
12 | function validateEmail(emailControl) {
13 | if (!emailControl.value || /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/.test(emailControl.value)) {
14 | return null;
15 | } else {
16 | return { 'invalidEmail': true };
17 | }
18 | }
19 |
20 | @Component({
21 | selector: 'dev-add',
22 | templateUrl: './add_developer.html',
23 | styles: [
24 | `input.ng-touched.ng-invalid {
25 | border: 1px solid red;
26 | }`
27 | ],
28 | providers: [GitHubGateway]
29 | })
30 | export class AddDeveloper implements OnInit, OnDestroy {
31 | submitted = false;
32 | importDevForm: FormGroup;
33 | addDevForm: FormGroup;
34 | errorMessage: string;
35 | successMessage: string;
36 | technologies: string[] = [
37 | 'JavaScript',
38 | 'C',
39 | 'C#',
40 | 'Clojure'
41 | ];
42 |
43 | private subscription: Subscription;
44 |
45 | constructor(private githubAPI: GitHubGateway, private developers: DeveloperCollection, fb: FormBuilder) {
46 | this.importDevForm = fb.group({
47 | githubHandle: ['', Validators.required],
48 | fetchFromGitHub: [false]
49 | });
50 | this.addDevForm = fb.group({
51 | realName: ['', Validators.required],
52 | email: ['', validateEmail],
53 | technology: ['', Validators.required],
54 | popular: [false]
55 | });
56 | }
57 |
58 | ngOnDestroy() {
59 | this.subscription.unsubscribe();
60 | }
61 |
62 | get isFormValid() {
63 | return (this.importDevForm.controls['fetchFromGitHub'].value && this.importDevForm.valid) ||
64 | (!this.importDevForm.controls['fetchFromGitHub'].value && this.addDevForm.valid)
65 | }
66 |
67 | addDeveloper() {
68 | let model;
69 | if (this.importDevForm.controls['fetchFromGitHub'].value) {
70 | model = this.importDevForm.value;
71 | if (this.developers.getUserByGitHubHandle(model.githubHadle)) {
72 | this.errorMessage = `Developer with githubHandle ${model.githubHadle} already exists`;
73 | return;
74 | }
75 | this.submitted = true;
76 | this.githubAPI.getUser(model.githubHandle)
77 | // .catch((error, source) => {
78 | // console.log(error)
79 | // return error;
80 | // })
81 | .map((r: Response) => r.json())
82 | .subscribe((res: any) => {
83 | let dev = new Developer();
84 | dev.githubHandle = res.login;
85 | dev.email = res.email;
86 | dev.popular = res.followers >= 1000;
87 | dev.realName = res.name;
88 | dev.id = res.id;
89 | dev.avatarUrl = res.avatar_url;
90 | this.developers.addDeveloper(dev);
91 | this.successMessage = `Developer ${dev.githubHandle} successfully imported from GitHub`;
92 | });
93 | } else {
94 | this.submitted = true;
95 | model = this.addDevForm.value;
96 | model.id = this.developers.getAll().length + 1;
97 | let dev = new Developer();
98 | Object.assign(dev, model);
99 | this.developers.addDeveloper(dev);
100 | this.successMessage = `Developer ${model.realName} was successfully added`;
101 | }
102 | return false;
103 | }
104 |
105 | ngOnInit() {
106 | this.toggleControls(this.importDevForm.controls['fetchFromGitHub'].value);
107 | this.subscription = this.importDevForm.controls['fetchFromGitHub']
108 | .valueChanges.subscribe(this.toggleControls.bind(this));
109 | }
110 |
111 | private toggleControls(importEnabled: boolean) {
112 | const addDevControls = this.addDevForm.controls;
113 | if (importEnabled) {
114 | this.importDevForm.controls['githubHandle'].enable();
115 | Object.keys(addDevControls).forEach((c: string) => addDevControls[c].disable());
116 | } else {
117 | this.importDevForm.controls['githubHandle'].disable();
118 | Object.keys(addDevControls).forEach((c: string) => addDevControls[c].enable());
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/app/ch7/ts/multi-page-model-driven/app.ts:
--------------------------------------------------------------------------------
1 | import {Component, NgModule} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
4 | import {LocationStrategy, HashLocationStrategy} from '@angular/common';
5 | import {RouterModule} from '@angular/router';
6 | import {ReactiveFormsModule} from '@angular/forms';
7 | import {Home} from './home';
8 | import {DeveloperCollection} from './developer_collection';
9 | import {AddDeveloper} from './add_developer';
10 | import {ControlErrors} from './control_errors';
11 | import {BooleanPipe} from './boolean_pipe';
12 | import {DeveloperDetails, devDetailsRoutes} from './developer_details';
13 | import {DeveloperBasicInfo} from './developer_basic_info';
14 | import {DeveloperAdvancedInfo} from './developer_advanced_info';
15 | import {HttpModule} from '@angular/http';
16 |
17 | @Component({
18 | selector: 'app',
19 | template: `
20 |
26 |
27 | `,
28 | providers: [DeveloperCollection]
29 | })
30 | class App {}
31 |
32 | const routingModule = RouterModule.forRoot([
33 | {
34 | path: '',
35 | pathMatch: 'full',
36 | redirectTo: 'home'
37 | },
38 | {
39 | component: Home,
40 | path: 'home'
41 | },
42 | {
43 | component: AddDeveloper,
44 | path: 'dev-add'
45 | },
46 | {
47 | component: DeveloperDetails,
48 | path: 'dev-details/:id',
49 | children: devDetailsRoutes
50 | },
51 | {
52 | path: 'add-dev',
53 | redirectTo: 'dev-add',
54 | }
55 | ]);
56 |
57 | @NgModule({
58 | imports: [BrowserModule, ReactiveFormsModule, HttpModule, routingModule],
59 | declarations: [App, Home, AddDeveloper, ControlErrors, BooleanPipe, DeveloperDetails, DeveloperBasicInfo, DeveloperAdvancedInfo],
60 | providers: [{ provide: LocationStrategy, useClass: HashLocationStrategy }],
61 | bootstrap: [App]
62 | })
63 | class AppModule {}
64 |
65 | platformBrowserDynamic().bootstrapModule(AppModule);
66 |
--------------------------------------------------------------------------------
/app/ch7/ts/multi-page-model-driven/boolean_pipe.ts:
--------------------------------------------------------------------------------
1 | import {Pipe, PipeTransform} from '@angular/core';
2 |
3 | @Pipe({ name: 'boolean' })
4 | export class BooleanPipe implements PipeTransform {
5 | transform(flag: boolean, trueValue: any, falseValue: any): string {
6 | return flag ? trueValue : falseValue;
7 | }
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/app/ch7/ts/multi-page-model-driven/control_errors.ts:
--------------------------------------------------------------------------------
1 | import {Component, Input, Host} from '@angular/core';
2 | import {FormGroupDirective} from '@angular/forms';
3 |
4 |
5 | @Component({
6 | template: `{{currentError}}
`,
7 | selector: 'control-errors'
8 | })
9 | export class ControlErrors {
10 | @Input() errors: Object;
11 | @Input() control: string;
12 |
13 | constructor(@Host() private formDir: FormGroupDirective) {}
14 |
15 | get currentError() {
16 | let control = this.formDir.form.controls[this.control];
17 | let errorMessages = [];
18 | if (control && control.touched) {
19 | errorMessages = Object.keys(this.errors)
20 | .map(k => control.hasError(k) ? this.errors[k] : null)
21 | .filter(error => !!error);
22 | }
23 | return errorMessages.pop();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/ch7/ts/multi-page-model-driven/developer.ts:
--------------------------------------------------------------------------------
1 | export class Developer {
2 | public id: number;
3 | public githubHandle: string;
4 | public avatarUrl: string;
5 | public realName: string;
6 | public email: string;
7 | public technology: string;
8 | public popular: boolean;
9 | }
10 |
--------------------------------------------------------------------------------
/app/ch7/ts/multi-page-model-driven/developer_advanced_info.ts:
--------------------------------------------------------------------------------
1 | import {Component, Inject, forwardRef, Host} from '@angular/core';
2 | import {DeveloperDetails} from './developer_details';
3 | import {Developer} from './developer';
4 |
5 | @Component({
6 | selector: 'dev-details-advanced',
7 | styles: [`
8 | .row span {
9 | display: inline-block;
10 | margin-left: 5px;
11 | }
12 | `
13 | ],
14 | template: `
15 | {{dev.githubHandle}}
16 |
17 |
18 | {{dev.realName}}
19 |
20 |
21 | {{dev.technology}}
22 |
23 |
24 | {{dev.email || 'none'}}
25 |
26 |
27 | {{dev.popular ? 'Yes' : 'Not yet' }}
28 |
29 |
30 | `
31 | })
32 | export class DeveloperAdvancedInfo {
33 | dev: Developer;
34 | constructor(@Inject(forwardRef(() => DeveloperDetails)) @Host() parent: DeveloperDetails) {
35 | this.dev = parent.dev;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/ch7/ts/multi-page-model-driven/developer_basic_info.ts:
--------------------------------------------------------------------------------
1 | import {Component, Inject, forwardRef, Host} from '@angular/core';
2 | import {DeveloperDetails} from './developer_details';
3 | import {Developer} from './developer';
4 |
5 | @Component({
6 | selector: 'dev-details-basic',
7 | styles: [
8 | `.avatar {
9 | border-radius: 150px;
10 | }`
11 | ],
12 | template: `
13 | {{dev.githubHandle | uppercase}}
14 |
15 |
16 | `
17 | })
18 | export class DeveloperBasicInfo {
19 | dev: Developer;
20 | constructor(@Inject(forwardRef(() => DeveloperDetails)) @Host() parent: DeveloperDetails) {
21 | this.dev = parent.dev;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/ch7/ts/multi-page-model-driven/developer_collection.ts:
--------------------------------------------------------------------------------
1 | import {Developer} from './developer';
2 |
3 | export class DeveloperCollection {
4 | private developers: Developer[];
5 |
6 | constructor() {
7 | this.developers = [];
8 | }
9 |
10 | getUserByGitHubHandle(username: string) {
11 | return this.developers.filter(u => u.githubHandle === username).pop();
12 | }
13 |
14 | getUserById(id: number) {
15 | return this.developers.filter(u => u.id === id).pop();
16 | }
17 |
18 | addDeveloper(dev: Developer) {
19 | this.developers.push(dev);
20 | }
21 |
22 | getAll() {
23 | return this.developers;
24 | }
25 | }
--------------------------------------------------------------------------------
/app/ch7/ts/multi-page-model-driven/developer_details.ts:
--------------------------------------------------------------------------------
1 | import {Component} from '@angular/core';
2 | import {ActivatedRoute} from '@angular/router';
3 | import {Developer} from './developer';
4 | import {DeveloperCollection} from './developer_collection';
5 | import {DeveloperBasicInfo} from './developer_basic_info';
6 | import {DeveloperAdvancedInfo} from './developer_advanced_info';
7 |
8 | import 'rxjs/add/operator/take';
9 |
10 | @Component({
11 | selector: 'dev-details',
12 | template: `
13 |
20 | `,
21 | })
22 | export class DeveloperDetails {
23 | public dev: Developer;
24 |
25 | constructor(private route: ActivatedRoute, private developers: DeveloperCollection) {}
26 |
27 | ngOnInit() {
28 | this.route.params.take(1)
29 | .subscribe((params: any) => {
30 | this.dev = this.developers.getUserById(parseInt(params['id']));
31 | });
32 | }
33 | }
34 |
35 | export const devDetailsRoutes = [
36 | { path: '', redirectTo: 'dev-basic-info', pathMatch: 'full' },
37 | { component: DeveloperBasicInfo, path: 'dev-basic-info' },
38 | { component: DeveloperAdvancedInfo, path: 'dev-details-advanced' }
39 | ];
40 |
41 |
--------------------------------------------------------------------------------
/app/ch7/ts/multi-page-model-driven/github_gateway.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@angular/core';
2 | import {Http} from '@angular/http';
3 |
4 | @Injectable()
5 | export class GitHubGateway {
6 | constructor(private http: Http) {}
7 | getUser(username: string) {
8 | return this.http.get(`https://api.github.com/users/${username}`);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/app/ch7/ts/multi-page-model-driven/home.html:
--------------------------------------------------------------------------------
1 | 0">
2 |
3 | GitHub handle |
4 | Email |
5 | Real name |
6 | Technology |
7 | Popular |
8 |
9 |
10 |
11 |
12 | {{dev.githubHandle}}
13 |
14 | |
15 | {{dev.email}} |
16 | {{dev.realName}} |
17 | {{dev.technology}} |
18 | {{dev.popular | boolean: 'Yes': 'No'}} |
19 |
20 |
21 | There are no any developers yet
22 |
--------------------------------------------------------------------------------
/app/ch7/ts/multi-page-model-driven/home.ts:
--------------------------------------------------------------------------------
1 | import {Component} from '@angular/core';
2 | import {DeveloperCollection} from './developer_collection';
3 |
4 | @Component({
5 | selector: 'home',
6 | templateUrl: './home.html'
7 | })
8 | export class Home {
9 | constructor(private developers: DeveloperCollection) {}
10 | getDevelopers() {
11 | return this.developers.getAll();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/ch7/ts/multi-page-model-driven/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= APP_TITLE %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Loading...
17 |
18 |
19 | <%= INIT %>
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/ch7/ts/multi-page-model-driven/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Page with multiple views and model-driven form",
3 | "description": "Page with multiple views and model-driven form",
4 | "id": 3,
5 | "presented": true
6 | }
--------------------------------------------------------------------------------
/app/ch7/ts/statful_pipe/app.ts:
--------------------------------------------------------------------------------
1 | import {NgModule, Component, Pipe, PipeTransform} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
4 | import {NgModel} from '@angular/forms';
5 | import {FetchJsonPipe} from './fetch_json_pipe';
6 | import {HttpModule} from '@angular/http';
7 |
8 | @Pipe({
9 | name: 'objectGet'
10 | })
11 | class ObjectGetPipe {
12 | transform(obj: Object, args: string[]) {
13 | if (obj) {
14 | return obj[args[0]];
15 | }
16 | }
17 | }
18 |
19 | @Component({
20 | selector: 'app',
21 | template: `
22 |
23 |
24 |
25 |
26 | `
27 | })
28 | class App {
29 | username: string;
30 | setUsername(user: string) {
31 | this.username = user;
32 | }
33 | }
34 |
35 | @NgModule({
36 | imports: [HttpModule, BrowserModule],
37 | declarations: [App, FetchJsonPipe, ObjectGetPipe],
38 | bootstrap: [App]
39 | })
40 | class AppModule {}
41 |
42 | platformBrowserDynamic().bootstrapModule(AppModule);
43 |
--------------------------------------------------------------------------------
/app/ch7/ts/statful_pipe/fetch_json_pipe.ts:
--------------------------------------------------------------------------------
1 | import {Pipe, PipeTransform} from '@angular/core';
2 | import {Http, Response} from '@angular/http';
3 |
4 | import 'rxjs/add/operator/toPromise';
5 |
6 | @Pipe({
7 | name: 'fetchJson',
8 | pure: false
9 | })
10 | export class FetchJsonPipe implements PipeTransform {
11 | private data: any;
12 | private prevUrl: string;
13 | constructor(private http: Http) {}
14 | transform(url: string): any {
15 | if (this.prevUrl !== url) {
16 | this.http.get(url)
17 | .toPromise()
18 | .then((data: Response) => data.json())
19 | .then(result => this.data = result);
20 | this.prevUrl = url;
21 | }
22 | return this.data || {};
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/ch7/ts/statful_pipe/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | <%= INIT %>
14 |
15 |
--------------------------------------------------------------------------------
/app/ch7/ts/statful_pipe/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Developing stateful pipe",
3 | "description": "Developing stateful pipe demo",
4 | "id": 5,
5 | "presented": true
6 | }
--------------------------------------------------------------------------------
/app/ch8/ts/todo_webworkers/background_app.ts:
--------------------------------------------------------------------------------
1 | import {Component, NgModule, Input, Output, EventEmitter} from '@angular/core';
2 | import {BrowserModule} from '@angular/platform-browser';
3 | import {WorkerAppModule} from '@angular/platform-webworker';
4 | import {FormsModule} from '@angular/forms';
5 | import {platformWorkerAppDynamic} from '@angular/platform-webworker-dynamic';
6 |
7 | interface Todo {
8 | completed: boolean;
9 | label: string;
10 | }
11 |
12 | @Component({
13 | selector: 'input-box',
14 | template: `
15 |
16 |
19 | `
20 | })
21 | class InputBox {
22 | @Input() inputPlaceholder: string;
23 | @Input() buttonLabel: string;
24 | @Output() inputText = new EventEmitter();
25 | input: string;
26 |
27 | emitText() {
28 | this.inputText.emit(this.input);
29 | this.input = '';
30 | }
31 | }
32 |
33 | @Component({
34 | selector: 'todo-list',
35 | template: `
36 |
43 | `,
44 | styles: [
45 | `ul li {
46 | list-style: none;
47 | }
48 | .completed {
49 | text-decoration: line-through;
50 | }`
51 | ]
52 | })
53 | class TodoList {
54 | @Input() todos: Todo[];
55 | @Output() toggle = new EventEmitter();
56 | toggleCompletion(index: number) {
57 | let todo = this.todos[index];
58 | this.toggle.emit(todo);
59 | }
60 | }
61 |
62 | @Component({
63 | selector: 'todo-app',
64 | template: `
65 | Hello {{name}}!
66 |
67 |
68 | Add a new todo:
69 |
72 |
73 |
74 |
75 | Here's the list of pending todo items:
76 |
78 |
79 | `
80 | })
81 | export class TodoApp {
82 | todos: Todo[] = [{
83 | label: 'Buy milk',
84 | completed: false
85 | }, {
86 | label: "Save the world",
87 | completed: false
88 | }];
89 | name: string = 'John';
90 | addTodo(label: string) {
91 | this.todos.push({
92 | label,
93 | completed: false
94 | });
95 | }
96 | toggleCompletion(todo: Todo) {
97 | todo.completed = !todo.completed;
98 | }
99 | }
100 |
101 | @NgModule({
102 | imports: [WorkerAppModule, FormsModule],
103 | declarations: [TodoList, InputBox, TodoApp],
104 | bootstrap: [TodoApp]
105 | })
106 | class AppModule {}
107 |
108 | platformWorkerAppDynamic().bootstrapModule(AppModule)
109 | .catch(err => console.error(err));
110 |
111 |
--------------------------------------------------------------------------------
/app/ch8/ts/todo_webworkers/bootstrap.ts:
--------------------------------------------------------------------------------
1 | //main entry point
2 | import {bootstrapWorkerUi} from '@angular/platform-webworker';
3 |
4 | bootstrapWorkerUi('loader.js');
5 |
6 |
--------------------------------------------------------------------------------
/app/ch8/ts/todo_webworkers/config.ts:
--------------------------------------------------------------------------------
1 | System.config({
2 | baseURL: '/dist/dev/ch8/ts/todo_webworkers/',
3 | map: {
4 | 'rxjs': '/node_modules/rxjs',
5 | },
6 | paths: {
7 | bootstrap: '/dist/dev/bootstrap',
8 | '@angular/common': '/node_modules/@angular/common/bundles/common.umd.js',
9 | '@angular/compiler': '/node_modules/@angular/compiler/bundles/compiler.umd.js',
10 | '@angular/core': '/node_modules/@angular/core/bundles/core.umd.js',
11 | '@angular/forms': '/node_modules/@angular/forms/bundles/forms.umd.js',
12 | '@angular/http': '/node_modules/@angular/http/bundles/http.umd.js',
13 | '@angular/platform-browser': '/node_modules/@angular/platform-browser/bundles/platform-browser.umd.js',
14 | '@angular/platform-browser-dynamic': '/node_modules/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
15 | '@angular/router': '/node_modules/@angular/router/bundles/router.umd.js',
16 | '@angular/platform-webworker': '/node_modules/@angular/platform-webworker/bundles/platform-webworker.umd.js',
17 | '@angular/platform-webworker-dynamic': '/node_modules/@angular/platform-webworker-dynamic/bundles/platform-webworker-dynamic.umd.js',
18 |
19 | '@angular/common/testing': '/node_modules/@angular/common/bundles/common-testing.umd.js',
20 | '@angular/compiler/testing': '/node_modules/@angular/compiler/bundles/compiler-testing.umd.js',
21 | '@angular/core/testing': '/node_modules/@angular/core/bundles/core-testing.umd.js',
22 | '@angular/http/testing': '/node_modules/@angular/http/bundles/http-testing.umd.js',
23 | '@angular/platform-browser/testing':
24 | '/node_modules/@angular/platform-browser/bundles/platform-browser-testing.umd.js',
25 | '@angular/platform-browser-dynamic/testing':
26 | '/node_modules/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js',
27 | '@angular/router/testing': '/node_modules/@angular/router/bundles/router-testing.umd.js',
28 | },
29 | packages: {
30 | // '@angular/router': {
31 | // main: 'index.js',
32 | // defaultExtension: 'js'
33 | // },
34 | 'rxjs': {
35 | defaultExtension: 'js'
36 | }
37 | }
38 | });
39 |
--------------------------------------------------------------------------------
/app/ch8/ts/todo_webworkers/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= TITLE %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Loading...
15 |
16 |
17 |
18 |
19 |
20 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/ch8/ts/todo_webworkers/loader.ts:
--------------------------------------------------------------------------------
1 | importScripts('/node_modules/systemjs/dist/system.src.js',
2 | '/node_modules/reflect-metadata/Reflect.js',
3 | '/node_modules/zone.js/dist/zone.js',
4 | './config.js');
5 |
6 | System.import('./background_app.js')
7 | .then(() => console.log('The application has started successfully'),
8 | error => console.error('error loading background', error));
9 |
10 |
--------------------------------------------------------------------------------
/app/ch8/ts/todo_webworkers/meta.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Running an application in WebWorkers",
3 | "description": "Running an application in WebWorkers",
4 | "id": 1,
5 | "presented": true
6 | }
--------------------------------------------------------------------------------
/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= APP_TITLE %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | <%= EXAMPLES_LIST %>
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/system.config.js:
--------------------------------------------------------------------------------
1 | System.config({
2 | defaultJSExtensions: true
3 | });
4 |
5 |
--------------------------------------------------------------------------------
/app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "commonjs",
5 | "declaration": false,
6 | "noImplicitAny": false,
7 | "removeComments": true,
8 | "noLib": false,
9 | "emitDecoratorMetadata": true,
10 | "experimentalDecorators": true,
11 | "sourceMap": true,
12 | "lib": ["es6", "es2015", "dom"],
13 | "typeRoots": [
14 | "../../node_modules/@types",
15 | "../../node_modules"
16 | ],
17 | "types": [
18 | "express",
19 | "node",
20 | "systemjs"
21 | ]
22 | },
23 | "exclude": [
24 | "../tools"
25 | ],
26 | "compileOnSave": false
27 | }
28 |
29 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | # AppVeyor file
2 | # http://www.appveyor.com/docs/appveyor-yml
3 | # This file: cloned from https://github.com/gruntjs/grunt/blob/master/appveyor.yml
4 |
5 | # Build version format
6 | version: "{build}"
7 |
8 | # Test against this version of Node.js
9 | environment:
10 | nodejs_version: "4.1.0"
11 | # https://github.com/DefinitelyTyped/tsd#tsdrc
12 | # Token has no scope (read-only access to public information)
13 | TSD_GITHUB_TOKEN: "9b18c72997769f3867ef2ec470e626d39661795d"
14 |
15 | build: off
16 |
17 | clone_depth: 10
18 |
19 | # Fix line endings on Windows
20 | init:
21 | - git config --global core.autocrlf true
22 |
23 | install:
24 | - ps: Install-Product node $env:nodejs_version
25 | - npm install -g npm
26 | - ps: $env:path = $env:appdata + "\npm;" + $env:path
27 | - npm install
28 |
29 | test_script:
30 | # Output useful info for debugging.
31 | - node --version && npm --version
32 | # We test multiple Windows shells because of prior stdout buffering issues
33 | # filed against Grunt. https://github.com/joyent/node/issues/3584
34 | - ps: "npm --version # PowerShell" # Pass comment to PS for easier debugging
35 | - npm test
36 |
--------------------------------------------------------------------------------
/gulpfile.ts:
--------------------------------------------------------------------------------
1 | import * as gulp from 'gulp';
2 | import {runSequence, task} from './tools/utils';
3 |
4 | // --------------
5 | // Clean (override).
6 | gulp.task('clean', task('clean', 'all'));
7 | gulp.task('clean.dist', task('clean', 'dist'));
8 | gulp.task('clean.tmp', task('clean', 'tmp'));
9 |
10 | gulp.task('check.versions', task('check.versions'));
11 |
12 | // --------------
13 | // Postinstall.
14 | gulp.task('postinstall', done =>
15 | runSequence('clean',
16 | 'npm',
17 | done));
18 |
19 | // --------------
20 | // Build dev.
21 | gulp.task('build.dev', done =>
22 | runSequence('clean.dist',
23 | 'build.assets.dev',
24 | 'build.js.dev',
25 | 'build.index',
26 | done));
27 |
28 | // --------------
29 | // Watch.
30 | gulp.task('build.dev.watch', done =>
31 | runSequence('build.dev',
32 | 'watch.dev',
33 | done));
34 |
35 | // Serve.
36 | gulp.task('serve', done =>
37 | runSequence('build.dev',
38 | 'server.start',
39 | 'watch.serve',
40 | done));
41 |
42 |
--------------------------------------------------------------------------------
/img/book-ed1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mgechev/getting-started-with-angular/8e6c36a992b06dbd843bb0aff869ed47cd321c61/img/book-ed1.jpg
--------------------------------------------------------------------------------
/manual_typings/connect-livereload.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'connect-livereload' {
2 | function connectLivereload(options?: any): any;
3 | module connectLivereload {}
4 | export = connectLivereload;
5 | }
6 |
--------------------------------------------------------------------------------
/manual_typings/gulp-load-plugins.d.ts:
--------------------------------------------------------------------------------
1 | // Type definitions for gulp-load-plugins
2 | // Project: https://github.com/jackfranklin/gulp-load-plugins
3 | // Definitions by: Joe Skeen
4 | // Definitions: https://github.com/borisyankov/DefinitelyTyped
5 |
6 | // Does not support ES2015 import (import * as open from 'open').
7 |
8 | /** Loads in any gulp plugins and attaches them to an object, freeing you up from having to manually require each gulp plugin. */
9 | declare module 'gulp-load-plugins' {
10 |
11 | interface IOptions {
12 | /** the glob(s) to search for, default ['gulp-*', 'gulp.*'] */
13 | pattern?: string[];
14 | /** where to find the plugins, searched up from process.cwd(), default 'package.json' */
15 | config?: string;
16 | /** which keys in the config to look within, default ['dependencies', 'devDependencies', 'peerDependencies'] */
17 | scope?: string[];
18 | /** what to remove from the name of the module when adding it to the context, default /^gulp(-|\.)/ */
19 | replaceString?: RegExp;
20 | /** if true, transforms hyphenated plugin names to camel case, default true */
21 | camelize?: boolean;
22 | /** whether the plugins should be lazy loaded on demand, default true */
23 | lazy?: boolean;
24 | /** a mapping of plugins to rename, the key being the NPM name of the package, and the value being an alias you define */
25 | rename?: IPluginNameMappings;
26 | }
27 |
28 | interface IPluginNameMappings {
29 | [npmPackageName: string]: string;
30 | }
31 |
32 | /** Loads in any gulp plugins and attaches them to an object, freeing you up from having to manually require each gulp plugin. */
33 | function gulpLoadPlugins(options?: IOptions): T;
34 | module gulpLoadPlugins {}
35 | export = gulpLoadPlugins;
36 | }
37 |
38 | /**
39 | * Extend this interface to use Gulp plugins in your gulpfile.js
40 | */
41 | interface IGulpPlugins {
42 | }
43 |
--------------------------------------------------------------------------------
/manual_typings/jsonfile.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'jsonfile' {
2 | let jsonfile: IJsonfile;
3 | interface IJsonfile {
4 | readFileSync(filename: string): string;
5 | }
6 | export = jsonfile;
7 | }
--------------------------------------------------------------------------------
/manual_typings/karma.d.ts:
--------------------------------------------------------------------------------
1 | // Use this minimalistic definition file as bluebird dependency
2 | // generate a lot of errors.
3 |
4 | declare module 'karma' {
5 | var karma: IKarma;
6 | export = karma;
7 | interface IKarma {
8 | server: {
9 | start(options: any, callback: Function): void
10 | };
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/manual_typings/merge-stream.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'merge-stream' {
2 | function mergeStream(...streams: NodeJS.ReadWriteStream[]): MergeStream;
3 | interface MergeStream extends NodeJS.ReadWriteStream {
4 | add(stream: NodeJS.ReadWriteStream): MergeStream;
5 | }
6 | module mergeStream {}
7 | export = mergeStream;
8 | }
--------------------------------------------------------------------------------
/manual_typings/open.d.ts:
--------------------------------------------------------------------------------
1 | // https://github.com/borisyankov/DefinitelyTyped/tree/master/open
2 | // Does not support ES2015 import (import * as open from 'open').
3 |
4 | declare module 'open' {
5 | function open(target: string, app?: string): void;
6 | module open {}
7 | export = open;
8 | }
9 |
--------------------------------------------------------------------------------
/manual_typings/run-sequence.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'run-sequence' {
2 | function runSequence(...args: any[]): void;
3 | module runSequence {}
4 | export = runSequence;
5 | }
6 |
--------------------------------------------------------------------------------
/manual_typings/slash.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'slash' {
2 | function slash(path: string): string;
3 | module slash {}
4 | export = slash;
5 | }
6 |
--------------------------------------------------------------------------------
/manual_typings/systemjs-builder.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'systemjs-builder' {
2 | class Builder {
3 | constructor(configObject?: any, baseUrl?: string, configPath?: string);
4 | bundle(source: string, target: string, options?: any): Promise;
5 | buildStatic(source: string, target: string, options?: any): Promise;
6 | }
7 |
8 | module Builder {}
9 | export = Builder;
10 | }
11 |
--------------------------------------------------------------------------------
/manual_typings/tiny-lr.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'tiny-lr' {
2 | function tinylr(): ITinylr;
3 | module tinylr {}
4 | export = tinylr;
5 |
6 | interface ITinylr {
7 | changed(options: any): void;
8 | listen(port: number): void;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/manual_typings/yargs.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'yargs' {
2 | var yargs: IYargs;
3 | export = yargs;
4 |
5 | // Minimalistic but serves the usage in the seed.
6 | interface IYargs {
7 | argv: any;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular2-seed",
3 | "version": "0.0.0",
4 | "description": "Seed for Angular 2 apps",
5 | "repository": {
6 | "url": "https://github.com/mgechev/angular2-seed"
7 | },
8 | "scripts": {
9 | "build.dev": "gulp build.dev",
10 | "build.dev.watch": "gulp build.dev.watch",
11 | "build.prod": "gulp build.prod --env prod",
12 | "build.test": "gulp build.test",
13 | "build.test.watch": "gulp build.test.watch",
14 | "docs": "npm run gulp -- build.docs && npm run gulp -- serve.docs",
15 | "gulp": "gulp",
16 | "karma": "karma",
17 | "karma.start": "karma start",
18 | "lint": "gulp tslint",
19 | "postinstall": "gulp check.versions && gulp postinstall",
20 | "reinstall": "rimraf node_modules && npm cache clean && npm install",
21 | "start": "gulp serve --env dev",
22 | "serve.dev": "gulp serve --env dev",
23 | "tasks.list": "gulp --tasks-simple",
24 | "test": "gulp test"
25 | },
26 | "author": "Minko Gechev ",
27 | "license": "MIT",
28 | "devDependencies": {
29 | "@types/async": "^2.0.34",
30 | "@types/chalk": "^0.4.31",
31 | "@types/del": "^2.2.31",
32 | "@types/express": "^4.0.34",
33 | "@types/glob": "^5.0.30",
34 | "@types/gulp": "^3.8.32",
35 | "@types/gulp-util": "^3.0.29",
36 | "@types/mime": "0.0.29",
37 | "@types/minimatch": "^2.0.29",
38 | "@types/node": "^6.0.51",
39 | "@types/orchestrator": "0.0.30",
40 | "@types/q": "0.0.32",
41 | "@types/serve-static": "^1.7.31",
42 | "@types/systemjs": "^0.19.31",
43 | "@types/through2": "^2.0.32",
44 | "@types/vinyl": "^1.2.30",
45 | "async": "^1.4.2",
46 | "chalk": "^1.1.1",
47 | "connect-livereload": "^0.5.3",
48 | "del": "^1.1.1",
49 | "express": "~4.13.1",
50 | "extend": "^3.0.0",
51 | "gulp": "^3.9.0",
52 | "gulp-concat": "^2.5.2",
53 | "gulp-data": "^1.2.1",
54 | "gulp-filter": "^2.0.2",
55 | "gulp-inject": "^1.3.1",
56 | "gulp-load-plugins": "^0.10.0",
57 | "gulp-plumber": "~1.0.1",
58 | "gulp-shell": "~0.4.3",
59 | "gulp-sourcemaps": "~1.5.2",
60 | "gulp-template": "^3.0.0",
61 | "gulp-tslint": "^3.3.0",
62 | "gulp-tslint-stylish": "^1.0.4",
63 | "gulp-typescript": "~2.8.2",
64 | "gulp-uglify": "^1.2.0",
65 | "gulp-util": "^3.0.7",
66 | "gulp-watch": "^4.2.4",
67 | "jasmine-core": "~2.3.4",
68 | "jsonfile": "^2.2.3",
69 | "merge": "^1.2.0",
70 | "merge-stream": "^1.0.0",
71 | "open": "0.0.5",
72 | "rimraf": "^2.4.3",
73 | "run-sequence": "^1.1.0",
74 | "semver": "^5.0.3",
75 | "serve-static": "^1.9.2",
76 | "slash": "~1.0.0",
77 | "stream-series": "^0.1.1",
78 | "tiny-lr": "^0.2.1",
79 | "traceur": "^0.0.91",
80 | "ts-node": "^1.4.3",
81 | "typescript": "~2.1.1",
82 | "yargs": "^3.25.0"
83 | },
84 | "dependencies": {
85 | "@angular/common": "2.2.0",
86 | "@angular/compiler": "2.2.0",
87 | "@angular/core": "2.2.0",
88 | "@angular/forms": "2.2.0",
89 | "@angular/http": "2.2.0",
90 | "@angular/platform-browser": "2.2.0",
91 | "@angular/platform-browser-dynamic": "2.2.0",
92 | "@angular/platform-webworker": "2.2.0",
93 | "@angular/platform-webworker-dynamic": "2.2.0",
94 | "@angular/router": "3.2.0",
95 | "@angular/upgrade": "2.2.0",
96 | "bootstrap": "^3.3.5",
97 | "es-module-loader": "^1.3.5",
98 | "es6-shim": "^0.35.0",
99 | "immutable": "^3.7.6",
100 | "markdown": "^0.5.0",
101 | "reflect-metadata": "0.1.2",
102 | "rxjs": "5.0.0-beta.12",
103 | "systemjs": "0.19.41",
104 | "zone.js": "0.6.21"
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/tools/config.ts:
--------------------------------------------------------------------------------
1 | import {readFileSync} from 'fs';
2 | import {normalize, join} from 'path';
3 | import {argv} from 'yargs';
4 |
5 |
6 | // --------------
7 | // Configuration.
8 | export const ENV = argv['env'] || 'dev';
9 | export const DEBUG = argv['debug'] || false;
10 | export const PORT = argv['port'] || 5555;
11 | export const PROJECT_ROOT = normalize(join(__dirname, '..'));
12 | export const LIVE_RELOAD_PORT = argv['reload-port'] || 4002;
13 | export const DOCS_PORT = argv['docs-port'] || 4003;
14 | export const APP_BASE = argv['base'] || '/';
15 |
16 | export const APP_TITLE = 'Getting Started with Angular';
17 |
18 | export const APP_SRC = 'app';
19 | export const ASSETS_SRC = `${APP_SRC}/assets`;
20 |
21 | export const TOOLS_DIR = 'tools';
22 | export const TMP_DIR = 'tmp';
23 | export const TEST_DEST = 'test';
24 | export const DOCS_DEST = 'docs';
25 | export const APP_DEST = `dist/${ENV}`;
26 | export const ASSETS_DEST = `${APP_DEST}/assets`;
27 | export const BUNDLES_DEST = `${APP_DEST}/bundles`;
28 | export const CSS_DEST = `${APP_DEST}/css`;
29 | export const FONTS_DEST = `${APP_DEST}/fonts`;
30 | export const LIB_DEST = `${APP_DEST}/lib`;
31 | export const APP_ROOT = ENV === 'dev' ? `${APP_BASE}${APP_DEST}/` : `${APP_BASE}`;
32 | export const VERSION = appVersion();
33 |
34 | export const VERSION_NPM = '2.14.7';
35 | export const VERSION_NODE = '4.0.0';
36 |
37 | // Declare NPM dependencies (Note that globs should not be injected).
38 | export const NPM_DEPENDENCIES = [
39 | { src: 'systemjs/dist/system-polyfills.js', dest: LIB_DEST },
40 |
41 | { src: 'es6-shim/es6-shim.min.js', inject: 'shims', dest: LIB_DEST },
42 | { src: 'reflect-metadata/Reflect.js', inject: 'shims', dest: LIB_DEST },
43 | { src: 'systemjs/dist/system.src.js', inject: 'shims', dest: LIB_DEST },
44 | { src: 'zone.js/dist/zone.js', inject: 'shims', dest: LIB_DEST },
45 |
46 | // Faster dev page load
47 | { src: 'rxjs/bundles/Rx.min.js', inject: 'libs', dest: LIB_DEST },
48 |
49 | { src: 'bootstrap/dist/css/bootstrap.min.css', inject: true, dest: CSS_DEST }
50 | ];
51 |
52 | // Declare local files that needs to be injected
53 | export const APP_ASSETS = [
54 | { src: `${ASSETS_SRC}/main.css`, inject: true, dest: CSS_DEST }
55 | ];
56 |
57 | NPM_DEPENDENCIES
58 | .filter(d => !/\*/.test(d.src)) // Skip globs
59 | .forEach(d => d.src = require.resolve(d.src));
60 |
61 | export const DEPENDENCIES = NPM_DEPENDENCIES.concat(APP_ASSETS);
62 |
63 |
64 | // ----------------
65 | // SystemsJS Configuration.
66 | const SYSTEM_CONFIG_DEV = {
67 | defaultJSExtensions: true,
68 | paths: {
69 | 'bootstrap': `${APP_ROOT}bootstrap`,
70 | 'markdown': '/node_modules/markdown/lib/markdown',
71 | 'immutable': '/node_modules/immutable/dist/immutable.js',
72 | '@angular/common': '/node_modules/@angular/common/bundles/common.umd.js',
73 | '@angular/compiler': '/node_modules/@angular/compiler/bundles/compiler.umd.js',
74 | '@angular/core': '/node_modules/@angular/core/bundles/core.umd.js',
75 | '@angular/forms': '/node_modules/@angular/forms/bundles/forms.umd.js',
76 | '@angular/http': '/node_modules/@angular/http/bundles/http.umd.js',
77 | '@angular/platform-browser': '/node_modules/@angular/platform-browser/bundles/platform-browser.umd.js',
78 | '@angular/platform-browser-dynamic': '/node_modules/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
79 | '@angular/router': '/node_modules/@angular/router/bundles/router.umd.js',
80 | '@angular/platform-webworker': '/node_modules/@angular/platform-webworker/bundles/platform-webworker.umd.js',
81 | '@angular/platform-webworker-dynamic': '/node_modules/@angular/platform-webworker-dynamic/bundles/platform-webworker-dynamic.umd.js',
82 |
83 | '@angular/common/testing': '/node_modules/@angular/common/bundles/common-testing.umd.js',
84 | '@angular/compiler/testing': '/node_modules/@angular/compiler/bundles/compiler-testing.umd.js',
85 | '@angular/core/testing': '/node_modules/@angular/core/bundles/core-testing.umd.js',
86 | '@angular/http/testing': '/node_modules/@angular/http/bundles/http-testing.umd.js',
87 | '@angular/platform-browser/testing':
88 | '/node_modules/@angular/platform-browser/bundles/platform-browser-testing.umd.js',
89 | '@angular/platform-browser-dynamic/testing':
90 | '/node_modules/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js',
91 | '@angular/router/testing': '/node_modules/@angular/router/bundles/router-testing.umd.js',
92 | '*': '/node_modules/*'
93 | }
94 | };
95 |
96 | const SYSTEM_CONFIG_PROD = {
97 | defaultJSExtensions: true,
98 | bundles: {
99 | 'bundles/app': ['bootstrap']
100 | }
101 | };
102 |
103 | export const SYSTEM_CONFIG = ENV === 'dev' ? SYSTEM_CONFIG_DEV : SYSTEM_CONFIG_PROD;
104 |
105 | // This is important to keep clean module names as 'module name == module uri'.
106 | export const SYSTEM_CONFIG_BUILDER = {
107 | defaultJSExtensions: true,
108 | paths: {
109 | '*': `${TMP_DIR}/*`,
110 | 'rxjs/*': 'node_modules/rxjs/*'
111 | },
112 | map: {
113 | 'rxjs': '/node_modules/rxjs',
114 | '@angular': '/node_modules/@angular'
115 | },
116 | packages: {
117 | '@angular/core': {
118 | main: 'index.js',
119 | defaultExtension: 'js'
120 | },
121 | '@angular/compiler': {
122 | main: 'index.js',
123 | defaultExtension: 'js'
124 | },
125 | '@angular/common': {
126 | main: 'index.js',
127 | defaultExtension: 'js'
128 | },
129 | '@angular/platform-browser': {
130 | main: 'index.js',
131 | defaultExtension: 'js'
132 | },
133 | '@angular/platform-browser-dynamic': {
134 | main: 'index.js',
135 | defaultExtension: 'js'
136 | },
137 | '@angular/router-deprecated': {
138 | main: 'index.js',
139 | defaultExtension: 'js'
140 | },
141 | // '@angular/router': {
142 | // main: 'index.js',
143 | // defaultExtension: 'js'
144 | // },
145 | 'rxjs': {
146 | defaultExtension: 'js'
147 | }
148 | }
149 | };
150 |
151 |
152 | // --------------
153 | // Private.
154 | function appVersion(): number|string {
155 | var pkg = JSON.parse(readFileSync('package.json').toString());
156 | return pkg.version;
157 | }
158 |
--------------------------------------------------------------------------------
/tools/tasks/build.assets.dev.ts:
--------------------------------------------------------------------------------
1 | import {join} from 'path';
2 | import {APP_SRC, APP_DEST} from '../config';
3 |
4 | export = function buildImagesDev(gulp, plugins) {
5 | return function () {
6 | return gulp.src([
7 | join(APP_SRC, '**/*.gif'),
8 | join(APP_SRC, '**/*.jpg'),
9 | join(APP_SRC, '**/*.png'),
10 | join(APP_SRC, '**/*.svg'),
11 | join(APP_SRC, '**/*.html'),
12 | join(APP_SRC, '**/*.js'),
13 | join(APP_SRC, '**/*.css')
14 | ])
15 | .pipe(gulp.dest(APP_DEST));
16 | };
17 | }
18 |
--------------------------------------------------------------------------------
/tools/tasks/build.bundles.ts:
--------------------------------------------------------------------------------
1 | import {parallel} from 'async';
2 | import {join} from 'path';
3 | import * as Builder from 'systemjs-builder';
4 | import {BUNDLES_DEST, SYSTEM_CONFIG_BUILDER} from '../config';
5 |
6 | const BUNDLE_OPTS = {
7 | minify: true,
8 | sourceMaps: true,
9 | format: 'cjs'
10 | };
11 |
12 | export = function bundles(gulp, plugins) {
13 | return function (done) {
14 | let builder = new Builder(SYSTEM_CONFIG_BUILDER);
15 |
16 | parallel([
17 | bundleApp
18 | ], () => done());
19 |
20 | function bundleApp(done) {
21 | builder.bundle(
22 | 'bootstrap - angular2/*',
23 | join(BUNDLES_DEST, 'app.js'), BUNDLE_OPTS).then(done);
24 | }
25 | };
26 | };
27 |
--------------------------------------------------------------------------------
/tools/tasks/build.deps.ts:
--------------------------------------------------------------------------------
1 | import * as merge from 'merge-stream';
2 | import {DEPENDENCIES} from '../config';
3 |
4 | export = function buildDepsProd(gulp, plugins) {
5 | return function () {
6 | let stream = merge();
7 |
8 | DEPENDENCIES.forEach(dep => {
9 | stream.add(addStream(dep));
10 | });
11 |
12 | return stream;
13 |
14 | function addStream(dep) {
15 | let stream = gulp.src(dep.src);
16 | stream.pipe(gulp.dest(dep.dest));
17 | return stream;
18 | }
19 | };
20 | };
21 |
--------------------------------------------------------------------------------
/tools/tasks/build.docs.ts:
--------------------------------------------------------------------------------
1 | import {join} from 'path';
2 | import {APP_SRC, APP_TITLE, DOCS_DEST} from '../config';
3 |
4 | export = function buildDocs(gulp, plugins, option) {
5 | return function() {
6 |
7 | let src = [
8 | join(APP_SRC, '**/*.ts'),
9 | '!' + join(APP_SRC, '**/*_spec.ts')
10 | ];
11 |
12 | return gulp.src(src)
13 | .pipe(plugins.typedoc({
14 | // TypeScript options (see typescript docs)
15 | module: 'commonjs',
16 | target: 'es5',
17 | includeDeclarations: true,
18 | // Output options (see typedoc docs)
19 | out: DOCS_DEST,
20 | json: join(DOCS_DEST , 'data/docs.json' ),
21 | name: APP_TITLE,
22 | ignoreCompilerErrors: false,
23 | experimentalDecorators: true,
24 | version: true
25 | }));
26 | };
27 | }
28 |
--------------------------------------------------------------------------------
/tools/tasks/build.html_css.prod.ts:
--------------------------------------------------------------------------------
1 | import * as merge from 'merge-stream';
2 | import {join} from 'path';
3 | import {APP_SRC, TMP_DIR} from '../config';
4 |
5 | // const HTML_MINIFIER_OPTS = { empty: true };
6 |
7 | export = function buildJSDev(gulp, plugins) {
8 | return function () {
9 |
10 | return merge(minifyHtml(), minifyCss());
11 |
12 | function minifyHtml() {
13 | return gulp.src(join(APP_SRC, '**/*.html'))
14 | // .pipe(plugins.minifyHtml(HTML_MINIFIER_OPTS))
15 | .pipe(gulp.dest(TMP_DIR));
16 | }
17 |
18 | function minifyCss() {
19 | return gulp.src(join(APP_SRC, '**/*.css'))
20 | .pipe(plugins.minifyCss())
21 | .pipe(gulp.dest(TMP_DIR));
22 | }
23 | };
24 | };
25 |
--------------------------------------------------------------------------------
/tools/tasks/build.index.ts:
--------------------------------------------------------------------------------
1 | import {join, sep} from 'path';
2 | import {APP_SRC, APP_DEST, DEPENDENCIES, SYSTEM_CONFIG, ENV} from '../config';
3 | import {transformPath, templateLocals} from '../utils';
4 |
5 | export = function buildIndexDev(gulp, plugins) {
6 | return function () {
7 | return gulp.src(join(APP_SRC, '**', 'index.html'))
8 | // NOTE: There might be a way to pipe in loop.
9 | .pipe(inject())
10 | .pipe(plugins.template(
11 | require('merge')(templateLocals(), {
12 | TITLE: 'Getting started with Angular',
13 | INIT: `
14 | `
21 | })
22 | ))
23 | .pipe(gulp.dest(APP_DEST));
24 | };
25 |
26 |
27 | function inject() {
28 | return plugins.inject(gulp.src(getInjectablesDependenciesRef(), { read: false }), {
29 | transform: transformPath(plugins, 'dev')
30 | });
31 | }
32 |
33 | function getInjectablesDependenciesRef() {
34 | let shims = DEPENDENCIES.filter(dep => dep['inject'] && dep['inject'] === 'shims');
35 | let libs = DEPENDENCIES.filter(dep => dep['inject'] && dep['inject'] === 'libs');
36 | let all = DEPENDENCIES.filter(dep => dep['inject'] && dep['inject'] === true);
37 | return shims.concat(libs).concat(all).map(mapPath);
38 | }
39 |
40 | function mapPath(dep) {
41 | let prodPath = join(dep.dest, dep.src.split(sep).pop());
42 | return ('prod' === ENV ? prodPath : dep.src );
43 | }
44 | };
45 |
--------------------------------------------------------------------------------
/tools/tasks/build.js.dev.ts:
--------------------------------------------------------------------------------
1 | import {join} from 'path';
2 | import {APP_SRC, APP_DEST, PROJECT_ROOT} from '../config';
3 | import {templateLocals, tsProjectFn, relativePath} from '../utils';
4 |
5 | export = function buildJSDev(gulp, plugins) {
6 | let tsProject = tsProjectFn(plugins);
7 | return function () {
8 | let src = [
9 | join(APP_SRC, '**/*.ts'),
10 | '!' + join(APP_SRC, '**/*_spec.ts')
11 | ];
12 |
13 | let result = gulp.src(src)
14 | .pipe(plugins.plumber())
15 | // Won't be required for non-production build after the change
16 | // .pipe(plugins.inlineNg2Template({ base: APP_SRC }))
17 | .pipe(plugins.sourcemaps.init())
18 | .pipe(plugins.typescript(tsProject));
19 |
20 | return result.js
21 | .pipe(plugins.sourcemaps.write())
22 | .pipe(require('gulp-data')(file => {
23 | return require('merge')({
24 | currentPath: relativePath(file.path)
25 | }, templateLocals());
26 | }))
27 | .pipe(plugins.template())
28 | .pipe(gulp.dest(APP_DEST));
29 | };
30 | };
31 |
--------------------------------------------------------------------------------
/tools/tasks/build.js.prod.ts:
--------------------------------------------------------------------------------
1 | import {join} from 'path';
2 | import {APP_SRC, TMP_DIR} from '../config';
3 | import {templateLocals, tsProjectFn} from '../utils';
4 |
5 | export = function buildJSDev(gulp, plugins) {
6 | return function () {
7 | let tsProject = tsProjectFn(plugins);
8 | let src = [
9 | join(APP_SRC, '**/*.ts'),
10 | '!' + join(APP_SRC, '**/*_spec.ts')
11 | ];
12 |
13 | let result = gulp.src(src)
14 | .pipe(plugins.plumber())
15 | .pipe(plugins.inlineNg2Template({ base: TMP_DIR }))
16 | .pipe(plugins.typescript(tsProject));
17 |
18 | return result.js
19 | .pipe(plugins.template(templateLocals()))
20 | .pipe(gulp.dest(TMP_DIR));
21 | };
22 | };
23 |
--------------------------------------------------------------------------------
/tools/tasks/build.test.ts:
--------------------------------------------------------------------------------
1 | import {join} from 'path';
2 | import {APP_SRC, TEST_DEST} from '../config';
3 | import {tsProjectFn} from '../utils';
4 |
5 | export = function buildTest(gulp, plugins) {
6 | return function () {
7 | let tsProject = tsProjectFn(plugins);
8 | let src = [
9 | join(APP_SRC, '**/*.ts'),
10 | '!' + join(APP_SRC, 'bootstrap.ts')
11 | ];
12 |
13 | let result = gulp.src(src)
14 | .pipe(plugins.plumber())
15 | .pipe(plugins.inlineNg2Template({ base: APP_SRC }))
16 | .pipe(plugins.typescript(tsProject));
17 |
18 | return result.js
19 | .pipe(gulp.dest(TEST_DEST));
20 | };
21 | };
22 |
--------------------------------------------------------------------------------
/tools/tasks/check.versions.ts:
--------------------------------------------------------------------------------
1 | import {VERSION_NPM, VERSION_NODE} from '../config';
2 |
3 | function reportError(message: string) {
4 | console.error(require('chalk').white.bgRed.bold(message));
5 | process.exit(1);
6 | }
7 |
8 | module.exports = function check(gulp, plugins) {
9 | return function () {
10 | let exec = require('child_process').exec;
11 | let semver = require('semver');
12 |
13 | exec('npm --version',
14 | function (error, stdout, stderr) {
15 | if (error !== null) {
16 | reportError('npm preinstall error: ' + error + stderr);
17 | }
18 |
19 | if (!semver.gte(stdout, VERSION_NPM)) {
20 | reportError('NPM is not in required version! Required is ' + VERSION_NPM + ' and you\'re using ' + stdout);
21 | }
22 | });
23 |
24 | exec('node --version',
25 | function (error, stdout, stderr) {
26 | if (error !== null) {
27 | reportError('npm preinstall error: ' + error + stderr);
28 | }
29 |
30 | if (!semver.gte(stdout, VERSION_NODE)) {
31 | reportError('NODE is not in required version! Required is ' + VERSION_NODE + ' and you\'re using ' + stdout);
32 | }
33 | });
34 | };
35 | };
36 |
--------------------------------------------------------------------------------
/tools/tasks/clean.ts:
--------------------------------------------------------------------------------
1 | import * as async from 'async';
2 | import * as del from 'del';
3 | import {APP_DEST, TEST_DEST, TMP_DIR} from '../config';
4 |
5 | export = function clean(gulp, plugins, option) {
6 | return function (done) {
7 |
8 | switch(option) {
9 | case 'all' : cleanAll(done); break;
10 | case 'dist' : cleanDist(done); break;
11 | case 'test' : cleanTest(done); break;
12 | case 'tmp' : cleanTmp(done); break;
13 | default: done();
14 | }
15 |
16 | };
17 | };
18 |
19 | function cleanAll(done) {
20 | async.parallel([
21 | cleanDist,
22 | cleanTest,
23 | cleanTmp
24 | ], done);
25 | }
26 | function cleanDist(done) {
27 | del(APP_DEST, done);
28 | }
29 | function cleanTest(done) {
30 | del(TEST_DEST, done);
31 | }
32 | function cleanTmp(done) {
33 | del(TMP_DIR, done);
34 | }
35 |
--------------------------------------------------------------------------------
/tools/tasks/karma.start.ts:
--------------------------------------------------------------------------------
1 | import * as karma from 'karma';
2 | import {join} from 'path';
3 |
4 | export = function karmaStart() {
5 | return function (done) {
6 | new (karma).Server({
7 | configFile: join(process.cwd(), 'karma.conf.js'),
8 | singleRun: true
9 | }).start(done);
10 | };
11 | };
12 |
--------------------------------------------------------------------------------
/tools/tasks/npm.ts:
--------------------------------------------------------------------------------
1 | export = function npm(gulp, plugins) {
2 | return plugins.shell.task([
3 | 'npm prune'
4 | ]);
5 | };
6 |
--------------------------------------------------------------------------------
/tools/tasks/serve.docs.ts:
--------------------------------------------------------------------------------
1 | import {serveDocs} from '../utils';
2 |
3 | export = function serverStart(gulp, plugins) {
4 | return function () {
5 | serveDocs();
6 | };
7 | };
8 |
--------------------------------------------------------------------------------
/tools/tasks/server.start.ts:
--------------------------------------------------------------------------------
1 | import {serveSPA} from '../utils';
2 |
3 | export = function serverStart(gulp, plugins) {
4 | return function () {
5 | serveSPA();
6 | };
7 | };
8 |
--------------------------------------------------------------------------------
/tools/tasks/tsd.ts:
--------------------------------------------------------------------------------
1 | export = function tsd(gulp, plugins) {
2 | return plugins.shell.task([
3 | 'tsd reinstall --clean',
4 | 'tsd link',
5 | 'tsd rebundle'
6 | ]);
7 | };
8 |
--------------------------------------------------------------------------------
/tools/tasks/tslint.ts:
--------------------------------------------------------------------------------
1 | import {join} from 'path';
2 | import {APP_SRC, TOOLS_DIR} from '../config';
3 |
4 | export = function tslint(gulp, plugins) {
5 | return function () {
6 | let src = [
7 | join(APP_SRC, '**/*.ts'),
8 | '!' + join(APP_SRC, '**/*.d.ts'),
9 | join(TOOLS_DIR, '**/*.ts'),
10 | '!' + join(TOOLS_DIR, '**/*.d.ts')
11 | ];
12 |
13 | return gulp.src(src)
14 | .pipe(plugins.tslint())
15 | .pipe(plugins.tslint.report(plugins.tslintStylish, {
16 | emitError: false,
17 | sort: true,
18 | bell: true
19 | }));
20 | };
21 | };
22 |
--------------------------------------------------------------------------------
/tools/tasks/watch.dev.ts:
--------------------------------------------------------------------------------
1 | import {join} from 'path';
2 | import {APP_SRC} from '../config';
3 |
4 | export = function watchDev(gulp, plugins) {
5 | return function () {
6 | plugins.watch(join(APP_SRC, '**/*'), () => gulp.start('build.dev'));
7 | };
8 | };
9 |
--------------------------------------------------------------------------------
/tools/tasks/watch.serve.ts:
--------------------------------------------------------------------------------
1 | import * as runSequence from 'run-sequence';
2 | import {join} from 'path';
3 | import {APP_SRC} from '../config';
4 | import {notifyLiveReload} from '../utils';
5 |
6 | export = function watchServe(gulp, plugins) {
7 | return function () {
8 | plugins.watch(join(APP_SRC, '**'), e =>
9 | runSequence('build.dev', () => notifyLiveReload(e))
10 | );
11 | };
12 | };
13 |
--------------------------------------------------------------------------------
/tools/tasks/watch.test.ts:
--------------------------------------------------------------------------------
1 | import {join} from 'path';
2 | import {APP_SRC} from '../config';
3 |
4 | export = function watchTest(gulp, plugins) {
5 | return function () {
6 | plugins.watch(join(APP_SRC, '**/*.ts'), () => gulp.start('build.test'));
7 | };
8 | };
9 |
--------------------------------------------------------------------------------
/tools/utils.ts:
--------------------------------------------------------------------------------
1 | export * from './utils/template_injectables';
2 | export * from './utils/template_locals';
3 | export * from './utils/server';
4 | export * from './utils/tasks_tools';
5 | import * as path from 'path';
6 | import {APP_DEST} from './config';
7 |
8 | export function relativePath(fileLocation) {
9 | fileLocation = path.dirname(fileLocation);
10 | var parentDir = __dirname.replace(path.join('tools'), '');
11 | var result = path.join(path.sep, fileLocation.replace(path.join(parentDir, 'app'), APP_DEST), path.sep);
12 | return result;
13 | }
14 |
15 | export function tsProjectFn(plugins) {
16 | const tsconfig = __dirname + '/../app/tsconfig.json';
17 | return plugins.typescript.createProject(tsconfig, {
18 | typescript: require('typescript')
19 | });
20 | }
21 |
--------------------------------------------------------------------------------
/tools/utils/server.ts:
--------------------------------------------------------------------------------
1 | import * as connectLivereload from 'connect-livereload';
2 | import * as express from 'express';
3 | import * as tinylrFn from 'tiny-lr';
4 | import * as openResource from 'open';
5 | import * as serveStatic from 'serve-static';
6 | import {resolve} from 'path';
7 | import {APP_BASE, APP_DEST, DOCS_DEST, LIVE_RELOAD_PORT, DOCS_PORT, PORT} from '../config';
8 |
9 | let tinylr = tinylrFn();
10 |
11 |
12 | export function serveSPA() {
13 | let server = express();
14 | tinylr.listen(LIVE_RELOAD_PORT);
15 |
16 | server.use(
17 | APP_BASE,
18 | connectLivereload({ port: LIVE_RELOAD_PORT }),
19 | express.static(process.cwd())
20 | );
21 |
22 | server.listen(PORT, () =>
23 | openResource('http://localhost:' + PORT + APP_BASE + APP_DEST)
24 | );
25 | }
26 |
27 | export function notifyLiveReload(e) {
28 | let fileName = e.path;
29 | tinylr.changed({
30 | body: { files: [fileName] }
31 | });
32 | }
33 |
34 | export function serveDocs() {
35 | let server = express();
36 |
37 | server.use(
38 | APP_BASE,
39 | serveStatic(resolve(process.cwd(), DOCS_DEST))
40 | );
41 |
42 | server.listen(DOCS_PORT, () =>
43 | openResource('http://localhost:' + DOCS_PORT + APP_BASE)
44 | );
45 | }
46 |
--------------------------------------------------------------------------------
/tools/utils/tasks_tools.ts:
--------------------------------------------------------------------------------
1 | import * as gulp from 'gulp';
2 | import * as util from 'gulp-util';
3 | import * as chalk from 'chalk';
4 | import * as gulpLoadPlugins from 'gulp-load-plugins';
5 | import * as _runSequence from 'run-sequence';
6 | import {readdirSync, existsSync, lstatSync} from 'fs';
7 | import {join} from 'path';
8 | import {TOOLS_DIR} from '../config';
9 |
10 | const TASKS_PATH = join(TOOLS_DIR, 'tasks');
11 |
12 | // NOTE: Remove if no issues with runSequence function below.
13 | // export function loadTasks(): void {
14 | // scanDir(TASKS_PATH, (taskname) => registerTask(taskname));
15 | // }
16 |
17 | export function task(taskname: string, option?: string) {
18 | util.log('Loading task', chalk.yellow(taskname, option || ''));
19 | return require(join('..', 'tasks', taskname))(gulp, gulpLoadPlugins(), option);
20 | }
21 |
22 | export function runSequence(...sequence: any[]) {
23 | let tasks = [];
24 | let _sequence = sequence.slice(0);
25 | sequence.pop();
26 |
27 | scanDir(TASKS_PATH, taskname => tasks.push(taskname));
28 |
29 | sequence.forEach(task => {
30 | if (tasks.indexOf(task) > -1) { registerTask(task); }
31 | });
32 |
33 | return _runSequence(..._sequence);
34 | }
35 |
36 | // ----------
37 | // Private.
38 |
39 | function registerTask(taskname: string, filename?: string, option: string = ''): void {
40 | gulp.task(taskname, task(filename || taskname, option));
41 | }
42 |
43 | // TODO: add recursive lookup ? or enforce pattern file + folder (ie ./tools/utils & ./tools/utils.ts )
44 | function scanDir(root: string, cb: (taskname: string) => void) {
45 | if (!existsSync(root)) return;
46 |
47 | walk(root);
48 |
49 | function walk(path) {
50 | let files = readdirSync(path);
51 | for (let i = 0; i < files.length; i += 1) {
52 | let file = files[i];
53 | let curPath = join(path, file);
54 | // if (lstatSync(curPath).isDirectory()) { // recurse
55 | // path = file;
56 | // walk(curPath);
57 | // }
58 | if (lstatSync(curPath).isFile() && /\.ts$/.test(file)) {
59 | let taskname = file.replace(/(\.ts)/, '');
60 | cb(taskname);
61 | }
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/tools/utils/template_injectables.ts:
--------------------------------------------------------------------------------
1 | import * as slash from 'slash';
2 | import {join} from 'path';
3 | import {APP_BASE, APP_DEST, ENV} from '../config';
4 |
5 | let injectables: string[] = [];
6 |
7 | export function injectableAssetsRef() {
8 | return injectables;
9 | }
10 |
11 | export function registerInjectableAssetsRef(paths: string[], target: string = '') {
12 | injectables = injectables.concat(
13 | paths
14 | .filter(path => !/(\.map)$/.test(path))
15 | .map(path => join(target, slash(path).split('/').pop()))
16 | );
17 | }
18 |
19 | export function transformPath(plugins, env) {
20 | return function (filepath) {
21 | filepath = ENV === 'prod' ? filepath.replace(`/${APP_DEST}`, '') : filepath;
22 | arguments[0] = join(APP_BASE, filepath);
23 | return slash(plugins.inject.transform.apply(plugins.inject.transform, arguments));
24 | };
25 | }
26 |
--------------------------------------------------------------------------------
/tools/utils/template_locals.ts:
--------------------------------------------------------------------------------
1 | import {APP_BASE, APP_DEST, APP_ROOT, APP_SRC, APP_TITLE, SYSTEM_CONFIG, VERSION} from '../config';
2 | import * as fs from 'fs';
3 | import {join, dirname, sep} from 'path';
4 | import * as jsonfile from 'jsonfile';
5 |
6 | const CHAPTERS_MAP = {
7 | ch3: 'Chapter 3',
8 | ch4: 'Chapter 4',
9 | ch5: 'Chapter 5',
10 | ch6: 'Chapter 6',
11 | ch7: 'Chapter 7',
12 | ch8: 'Chapter 8'
13 | };
14 |
15 | function listMetadataStrategy(data) {
16 | return `
17 | ${data.chapter}, ${data.meta.title}
18 | ${!data.meta.presented ? '(Not presented in the book\'s content)' : ''}
19 | `;
20 | }
21 |
22 | function readMetadata(current, appRoot) {
23 | var result = [];
24 | fs.readdirSync(current).forEach(function (file) {
25 | if (file === 'meta.json') {
26 | file = join('.', sep, current, file);
27 | let currentChapterAbr = file.match(/(ch\d+)/)[0];
28 | let chapter = CHAPTERS_MAP[currentChapterAbr];
29 | result.push({
30 | chapter,
31 | file: current,
32 | meta: jsonfile.readFileSync(file)
33 | });
34 | } else if (fs.lstatSync(join(current, file)).isDirectory()) {
35 | result = result.concat(readMetadata(join(current, file), appRoot));
36 | }
37 | });
38 | return result;
39 | }
40 |
41 | function getMetadata(appRoot) {
42 | let metadata = readMetadata(appRoot, appRoot);
43 | metadata = metadata.sort((a, b) => {
44 | let chapterA = parseInt(a.chapter.match(/\d+/)[0]);
45 | let chapterB = parseInt(b.chapter.match(/\d+/)[0]);
46 | if (chapterA < chapterB) {
47 | return -1;
48 | } else if (chapterA > chapterB) {
49 | return 1;
50 | }
51 | if (a.meta.id < b.meta.id) {
52 | return -1;
53 | } else {
54 | return 1;
55 | }
56 | });
57 | let items = metadata.map(listMetadataStrategy);
58 | return '' + items.join('\n') + '
';
59 | }
60 |
61 | function transformPath(plugins, env) {
62 | return function (filepath) {
63 | var filename = filepath.replace(sep + APP_DEST, '');
64 | arguments[0] = join(APP_BASE, filename);
65 | return plugins.inject.transform.apply(plugins.inject.transform, arguments);
66 | };
67 | }
68 |
69 | function relativePath(fileLocation) {
70 | fileLocation = dirname(fileLocation);
71 | var parentDir = __dirname.replace(join('tools'), '');
72 | var result = join(sep, fileLocation.replace(join(parentDir, 'app'), ''), sep);
73 | return result;
74 | }
75 |
76 | // TODO: Add an interface to register more template locals.
77 | export function templateLocals() {
78 | return {
79 | APP_BASE,
80 | APP_DEST,
81 | APP_ROOT,
82 | APP_TITLE,
83 | SYSTEM_CONFIG,
84 | VERSION,
85 | EXAMPLES_LIST: getMetadata(join(APP_SRC))
86 | };
87 | }
88 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "commonjs",
5 | "declaration": false,
6 | "noImplicitAny": false,
7 | "removeComments": true,
8 | "noLib": false,
9 | "emitDecoratorMetadata": true,
10 | "experimentalDecorators": true,
11 | "lib": ["dom", "es6", "es2015"],
12 | "sourceMap": true,
13 | "typeRoots": [
14 | "./node_modules/@types"
15 | ],
16 | "types": [
17 | "node"
18 | ]
19 | },
20 | "exclude": [
21 | "dist",
22 | "node_modules"
23 | ],
24 | "compileOnSave": false
25 | }
26 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "class-name": true,
4 | "curly": false,
5 | "eofline": true,
6 | "indent": "spaces",
7 | "max-line-length": [true, 140],
8 | "member-ordering": [true,
9 | "public-before-private",
10 | "static-before-instance",
11 | "variables-before-functions"
12 | ],
13 | "no-arg": true,
14 | "no-construct": true,
15 | "no-duplicate-key": true,
16 | "no-duplicate-variable": true,
17 | "no-empty": true,
18 | "no-eval": true,
19 | "no-trailing-comma": true,
20 | "no-trailing-whitespace": true,
21 | "no-unused-expression": true,
22 | "no-unused-variable": true,
23 | "no-unreachable": true,
24 | "no-use-before-declare": true,
25 | "one-line": [true,
26 | "check-open-brace",
27 | "check-catch",
28 | "check-else",
29 | "check-whitespace"
30 | ],
31 | "quotemark": [true, "single"],
32 | "semicolon": true,
33 | "triple-equals": true,
34 | "variable-name": false
35 | }
36 | }
37 |
--------------------------------------------------------------------------------