├── media ├── awesome-angular2.png └── awesome-angular2.psd ├── .travis.yml ├── .gitignore ├── features ├── Pipes.md ├── HTTP.md ├── ChangeDetection.md ├── Annotations.md ├── DI.md ├── View.md ├── Templates.md └── WebWorkers.md ├── package.json ├── contributing.md ├── stylesheets ├── github-light.css ├── stylesheet.css └── normalize.css ├── LICENSE ├── params.json ├── index.html └── README.md /media/awesome-angular2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeborahK/awesome-angular2/HEAD/media/awesome-angular2.png -------------------------------------------------------------------------------- /media/awesome-angular2.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DeborahK/awesome-angular2/HEAD/media/awesome-angular2.psd -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.2 4 | 5 | # Travis does not build the gh-pages branch by default, so we add it to the safelist 6 | # https://docs.travis-ci.com/user/customizing-the-build/#Safelisting-or-blocklisting-branches 7 | branches: 8 | only: 9 | gh-pages 10 | 11 | install: 12 | - gem install awesome_bot 13 | 14 | script: 15 | - awesome_bot README.md 16 | 17 | notifications: 18 | email: 19 | on_success: never 20 | -------------------------------------------------------------------------------- /.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 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | 29 | 30 | ## WS 31 | .idea -------------------------------------------------------------------------------- /features/Pipes.md: -------------------------------------------------------------------------------- 1 | # Pipes 2 | ----------------------------------- 3 | Pipes can be appended on the end of the expressions to translate the value to a different format. Typically used 4 | to control the stringification of numbers, dates, and other data, but can also be used for ordering, mapping, and 5 | reducing arrays. Pipes can be chained. 6 | 7 | **NOTE:** Pipes are known as filters in Angular v1. 8 | 9 | Pipes syntax is: 10 | 11 | ``` javascript 12 |
13 |

14 | {{ model.getValue('copy') | async | uppercase }} 15 |

16 |
25 | ``` 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "awesome-angular2", 3 | "version": "0.0.0", 4 | "description": "A curated list of awesome Angular 2 resources", 5 | "main": "readme.md", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "angular2", 11 | "angular 2", 12 | "AngularClass", 13 | "awesome", 14 | "awesome list", 15 | "awesome angular 2" 16 | ], 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/AngularClass/awesome-angular2.git" 20 | }, 21 | "author": "AngularClass", 22 | "license": "CC0", 23 | "bugs": { 24 | "url": "https://github.com/AngularClass/awesome-angular2/issues" 25 | }, 26 | "homepage": "https://github.com/AngularClass/awesome-angular2#readme" 27 | } 28 | -------------------------------------------------------------------------------- /features/HTTP.md: -------------------------------------------------------------------------------- 1 | # HTTP 2 | ----------------------------------- 3 | 4 | Http is available as an injectable class, with methods to perform http requests. Calling request returns an *EventEmitter* which will emit a single Response when a response is received. 5 | 6 | The `toRx()` method of *EventEmitter* needs to be called in order to get the RxJS Subject. *EventEmitter* does not provide combinators like map, and has different semantics for subscribing/observing. 7 | 8 | ## Usage 9 | 10 | ``` TypeScript 11 | import {Http, HTTP_BINDINGS} from 'angular2/http'; 12 | 13 | @Component({selector: 'http-app', viewBindings: [HTTP_BINDINGS]}) 14 | @View({templateUrl: 'people.html'}) 15 | class PeopleComponent { 16 | constructor(http: Http) { 17 | http.get('people.json') 18 | //Get the RxJS Subject 19 | .toRx() 20 | // Call map on the response observable to get the parsed people object 21 | .map(res => res.json()) 22 | // Subscribe to the observable to get the parsed people object and attach it to the 23 | // component 24 | .subscribe(people => this.people = people); 25 | } 26 | } 27 | ``` 28 | 29 | To use the *EventEmitter* returned by Http, simply pass a generator (See "interface Generator" in the Async Generator spec: https://github.com/jhusain/asyncgenerator) to the observer method of the returned emitter, with optional methods of next, throw, and return. 30 | 31 | ``` js 32 | http.get('people.json').observer({next: (value) => this.people = people}); 33 | ``` 34 | 35 | The default construct used to perform requests, *XMLHttpRequest*, is abstracted as a "Backend" ( XHRBackend in this case), which could be mocked with dependency injection by replacing the XHRBackend binding, as in the following example: 36 | 37 | ``` TypeScript 38 | import {MockBackend, BaseRequestOptions, Http} from 'angular2/http'; 39 | 40 | var injector = Injector.resolveAndCreate([ 41 | BaseRequestOptions, 42 | MockBackend, 43 | bind(Http).toFactory( 44 | function(backend, defaultOptions) { 45 | return new Http(backend, defaultOptions); 46 | }, 47 | [MockBackend, BaseRequestOptions]) 48 | ]); 49 | var http = injector.get(Http); 50 | http.get('request-from-mock-backend.json').toRx().subscribe((res:Response) => doSomething(res)); 51 | ``` 52 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | ## Adding to this list 4 | 5 | Please ensure your pull request adheres to the following guidelines: 6 | 7 | - Search previous suggestions before making a new one, as yours may be a duplicate. 8 | - Make sure the list is useful before submitting. That implies it has enough content and every item has a good succinct description. 9 | - Make an individual pull request for each suggestion. 10 | - Titles should be [capitalized](http://grammar.yourdictionary.com/capitalization/rules-for-capitalization-in-titles.html). 11 | - Use the following format: `[List Name](link)` 12 | - Link additions should be added to the bottom of the relevant category. 13 | - New categories or improvements to the existing categorization are welcome. 14 | - Check your spelling and grammar. 15 | - Make sure your text editor is set to remove trailing whitespace. 16 | - The pull request and commit should have a useful title. 17 | 18 | Thank you for your suggestions! 19 | 20 | ## Creating your own awesome list 21 | 22 | To create your own list, check out the [instructions](create-list.md). 23 | 24 | ## Adding something to an Awesome list 25 | 26 | If you have something awesome to contribute to an awesome list, this is how you do it. 27 | 28 | You'll need a [GitHub account](https://github.com/join)! 29 | 30 | 1. Access the awesome list's GitHub page. For example: https://github.com/sindresorhus/awesome 31 | 2. Click on the `readme.md` file: ![Step 2 Click on Readme.md](https://cloud.githubusercontent.com/assets/170270/9402920/53a7e3ea-480c-11e5-9d81-aecf64be55eb.png) 32 | 3. Now click on the edit icon. ![Step 3 - Click on Edit](https://cloud.githubusercontent.com/assets/170270/9402927/6506af22-480c-11e5-8c18-7ea823530099.png) 33 | 4. You can start editing the text of the file in the in-browser editor. Make sure you follow guidelines above. You can use [GitHub Flavored Markdown](https://help.github.com/articles/github-flavored-markdown/). ![Step 4 - Edit the file](https://cloud.githubusercontent.com/assets/170270/9402932/7301c3a0-480c-11e5-81f5-7e343b71674f.png) 34 | 5. Say why you're proposing the changes, and then click on "Propose file change". ![Step 5 - Propose Changes](https://cloud.githubusercontent.com/assets/170270/9402937/7dd0652a-480c-11e5-9138-bd14244593d5.png) 35 | 6. Submit the [pull request](https://help.github.com/articles/using-pull-requests/)! 36 | -------------------------------------------------------------------------------- /stylesheets/github-light.css: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 GitHub, Inc. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | .pl-c /* comment */ { 27 | color: #969896; 28 | } 29 | 30 | .pl-c1 /* constant, variable.other.constant, support, meta.property-name, support.constant, support.variable, meta.module-reference, markup.raw, meta.diff.header */, 31 | .pl-s .pl-v /* string variable */ { 32 | color: #0086b3; 33 | } 34 | 35 | .pl-e /* entity */, 36 | .pl-en /* entity.name */ { 37 | color: #795da3; 38 | } 39 | 40 | .pl-smi /* variable.parameter.function, storage.modifier.package, storage.modifier.import, storage.type.java, variable.other */, 41 | .pl-s .pl-s1 /* string source */ { 42 | color: #333; 43 | } 44 | 45 | .pl-ent /* entity.name.tag */ { 46 | color: #63a35c; 47 | } 48 | 49 | .pl-k /* keyword, storage, storage.type */ { 50 | color: #a71d5d; 51 | } 52 | 53 | .pl-s /* string */, 54 | .pl-pds /* punctuation.definition.string, string.regexp.character-class */, 55 | .pl-s .pl-pse .pl-s1 /* string punctuation.section.embedded source */, 56 | .pl-sr /* string.regexp */, 57 | .pl-sr .pl-cce /* string.regexp constant.character.escape */, 58 | .pl-sr .pl-sre /* string.regexp source.ruby.embedded */, 59 | .pl-sr .pl-sra /* string.regexp string.regexp.arbitrary-repitition */ { 60 | color: #183691; 61 | } 62 | 63 | .pl-v /* variable */ { 64 | color: #ed6a43; 65 | } 66 | 67 | .pl-id /* invalid.deprecated */ { 68 | color: #b52a1d; 69 | } 70 | 71 | .pl-ii /* invalid.illegal */ { 72 | color: #f8f8f8; 73 | background-color: #b52a1d; 74 | } 75 | 76 | .pl-sr .pl-cce /* string.regexp constant.character.escape */ { 77 | font-weight: bold; 78 | color: #63a35c; 79 | } 80 | 81 | .pl-ml /* markup.list */ { 82 | color: #693a17; 83 | } 84 | 85 | .pl-mh /* markup.heading */, 86 | .pl-mh .pl-en /* markup.heading entity.name */, 87 | .pl-ms /* meta.separator */ { 88 | font-weight: bold; 89 | color: #1d3e81; 90 | } 91 | 92 | .pl-mq /* markup.quote */ { 93 | color: #008080; 94 | } 95 | 96 | .pl-mi /* markup.italic */ { 97 | font-style: italic; 98 | color: #333; 99 | } 100 | 101 | .pl-mb /* markup.bold */ { 102 | font-weight: bold; 103 | color: #333; 104 | } 105 | 106 | .pl-md /* markup.deleted, meta.diff.header.from-file */ { 107 | color: #bd2c00; 108 | background-color: #ffecec; 109 | } 110 | 111 | .pl-mi1 /* markup.inserted, meta.diff.header.to-file */ { 112 | color: #55a532; 113 | background-color: #eaffea; 114 | } 115 | 116 | .pl-mdr /* meta.diff.range */ { 117 | font-weight: bold; 118 | color: #795da3; 119 | } 120 | 121 | .pl-mo /* meta.output */ { 122 | color: #1d3e81; 123 | } 124 | 125 | -------------------------------------------------------------------------------- /features/ChangeDetection.md: -------------------------------------------------------------------------------- 1 | # Change detection 2 | ----------------------------------- 3 | 4 | ![Change detection](http://36.media.tumblr.com/70d4551eef20b55b195c3232bf3d4e1b/tumblr_njb2puhhEa1qc0howo2_1280.png) 5 | Every component gets a change detector responsible for checking the bindings defined in its template. Examples of bindings: `{{todo.text}}` and `[todo]="t"`. Change detectors propagate bindings[c] from the root to leaves in the depth first order. 6 | 7 | By default the change detection goes through every node of the tree to see if it changed, and it does it on every browser event. Although it may seem terribly inefficient, the Angular 2 change detection system can go through hundreds of thousands of simple checks (the number are platform dependent) in a few milliseconds. 8 | 9 | ## Immutable Objects 10 | If a component depends only on its bindings, and the bindings are immutable, then this component can change if and only if one of its bindings changes. Therefore, we can skip the component’s subtree in the change detection tree until such an event occurs. When it happens, we can check the subtree once, and then disable it until the next change (gray boxes indicate disabled change detectors). 11 | ![CD - Immutable Objects](http://40.media.tumblr.com/0f43874fd6b8967f777ac9384122b589/tumblr_njb2puhhEa1qc0howo4_1280.png) 12 | 13 | To implement this just set the change detection strategy to `ON_PUSH`. 14 | 15 | ``` javascript 16 | @Component({changeDetection:ON_PUSH}) 17 | class ImmutableTodoCmp { 18 | todo:Todo; 19 | } 20 | ``` 21 | 22 | ## Observable Objects 23 | If a component depends only on its bindings, and the bindings are observable, then this component can change if and only if one of its bindings emits an event. Therefore, we can skip the component’s subtree in the change detection tree until such an event occurs. When it happens, we can check the subtree once, and then disable it until the next change. 24 | 25 | **NOTE:** If you have a tree of components with immutable bindings, a change has to go through all the components starting from the root. This is not the case when dealing with observables. 26 | 27 | ``` javascript 28 | type ObservableTodo = Observable; 29 | type ObservableTodos = Observable>; 30 | 31 | @Component({selector:’todos’}) 32 | class ObservableTodosCmp { 33 | todos:ObservableTodos; 34 | //... 35 | } 36 | ``` 37 | 38 | ``` javascript 39 | 40 | ``` 41 | 42 | ``` javascript 43 | @Component({selector:'todo'}) 44 | class ObservableTodoCmp { 45 | todo:ObservableTodo; 46 | //... 47 | } 48 | ``` 49 | 50 | Say the application uses only observable objects. When it boots, Angular will check all the objects. 51 | 52 | ![CD - Ob 1](http://40.media.tumblr.com/b9a743a15d23c3db9f910f4c7566b928/tumblr_njb2puhhEa1qc0howo5_1280.png) 53 | 54 | After the first pass will look as follows: 55 | 56 | ![CD - Ob 2](http://40.media.tumblr.com/5f4ba2e53fb3de05f9c199199f4aae77/tumblr_njb2puhhEa1qc0howo6_1280.png) 57 | 58 | Let’s say the first todo observable fires an event. The system will switch to the following state: 59 | 60 | ![CD - Ob 3](http://40.media.tumblr.com/cb54aedb3479e1b0578ae2c6c8c7ccc2/tumblr_njb2puhhEa1qc0howo7_1280.png) 61 | 62 | And after checking `App_ChangeDetector`, `Todos_ChangeDetector`, and the first `Todo_ChangeDetector`, it will go back to this state. 63 | 64 | ![CD - Ob 4](http://40.media.tumblr.com/5f4ba2e53fb3de05f9c199199f4aae77/tumblr_njb2puhhEa1qc0howo6_1280.png) 65 | 66 | Assuming that changes happen rarely and the components form a balanced tree, using observables changes the complexity of change detection from `O(N)` to `O(logN)`, where N is the number of bindings in the system. 67 | 68 | **REMEMBER** 69 | - An Angular 2 application is a reactive system. 70 | - The change detection system propagates bindings from the root to leaves. 71 | - Unlike Angular 1.x, the change detection graph is a directed tree. As a result, the system is more performant and predictable. 72 | - By default, the change detection system walks the whole tree. But if you use immutable objects or observables, you can take advantage of them and check parts of the tree only if they "really change". 73 | - These optimizations compose and do not break the guarantees the change detection provides. 74 | -------------------------------------------------------------------------------- /features/Annotations.md: -------------------------------------------------------------------------------- 1 | # Annotations 2 | -------------- 3 | ## Directives 4 | Directives allow you to attach behavior to elements in the DOM. 5 | 6 | Directives are classes which get instantiated as a response to a particular DOM structure. By controlling the DOM structure, what directives are imported, and their selectors, the developer can use the [composition pattern](http://en.wikipedia.org/wiki/Object_composition) to get a desirable application behavior. 7 | 8 | Directives are the cornerstone of an Angular application. We use Directives to break complex problems into smaller more reusable components. Directives allow the developer to turn HTML into a DSL and then control the application assembly process. 9 | 10 | A directive consists of a single directive annotation and a controller class. When the directive's `selector` matches elements in the DOM, the following steps occur: 11 | 12 | * For each directive, the *ElementInjector* attempts to resolve the directive's constructor arguments. 13 | * Angular instantiates directives for each matched element using *ElementInjector* in a depth-first order, as declared in the HTML. 14 | 15 | Here is a trivial example of a tooltip decorator. The directive will log a tooltip into the console on every time mouse enters a region: 16 | 17 | ``` javascript 18 | @Directive({ 19 | selector: '[tooltip]', | CSS Selector which triggers the decorator 20 | inputs: [ | List which properties need to be bound 21 | 'text: tooltip' | - DOM element tooltip property should be 22 | ], | mapped to the directive text property. 23 | host: { | List which events need to be mapped. 24 | '(mouseover)': 'show()' | - Invoke the show() method every time 25 | } | the mouseover event is fired. 26 | }) | 27 | class Form { | Directive controller class, instantiated 28 | | when CSS matches. 29 | text:string; | text property on the Directive Controller. 30 | | 31 | show(event) { | Show method which implements the show action. 32 | console.log(this.text); | 33 | } 34 | } 35 | ``` 36 | 37 | Example of usage: 38 | 39 | ``` html 40 | Some text here. 41 | ``` 42 | 43 | The developer of an application can now freely use the `tooltip` attribute wherever the behavior is needed. The code above has taught the browser a new reusable and declarative behavior. 44 | 45 | Notice that data binding will work with this decorator with no further effort as shown below. 46 | 47 | ``` html 48 | Some text here. 49 | ``` 50 | 51 | **NOTE:** Angular applications do not have a main method. Instead they have a root Component. Dependency Injection then assembles the directives into a working Angular application. 52 | 53 | ## Components 54 | A component is a directive which uses shadow DOM to create encapsulate visual behavior. Components are typically used to create UI widgets or to break up the application into smaller components. 55 | 56 | * Only one component can be present per DOM element. 57 | * Component's CSS selectors usually trigger on element names. (Best practice) 58 | * Component has its own shadow view which is attached to the element as a Shadow DOM. 59 | * Shadow view context is the component instance. (i.e. template expressions are evaluated against the component instance.) 60 | 61 | Each Angular component requires a single `@Component` and at least one `@View` annotation. The `@Component` annotation specifies when a component is instantiated, and which properties and hostListeners it binds to. 62 | 63 | When a component is instantiated, Angular 64 | 65 | * Creates a shadow DOM for the component. 66 | * Loads the selected template into the shadow DOM. 67 | * Creates a child Injector which is configured with the appInjector for the Component. 68 | 69 | Example of a component: 70 | 71 | ``` javascript 72 | @Component({ | Component annotation 73 | selector: 'pane', | CSS selector on element 74 | inputs: [ | List which property need to be bound 75 | 'title', | - title mapped to component title 76 | 'open' | - open attribute mapped to component open property 77 | ], | 78 | }) | 79 | @View({ | View annotation 80 | templateUrl: 'pane.html' | - URL of template HTML 81 | }) | 82 | class Pane { | Component controller class 83 | title:string; | - title property 84 | open:boolean; 85 | 86 | constructor() { 87 | this.title = ''; 88 | this.open = true; 89 | } 90 | 91 | // Public API 92 | toggle() => this.open = !this.open; 93 | open() => this.open = true; 94 | close() => this.open = false; 95 | } 96 | ``` 97 | 98 | `pane.html`: 99 | ``` html 100 |
101 |

{{title}}

102 |
103 | 104 |
105 |
106 | ``` 107 | 108 | `pane.css`: 109 | ``` css 110 | .outer, .inner { border: 1px solid blue;} 111 | .h1 {background-color: blue;} 112 | ``` 113 | 114 | Example of usage: 115 | ``` html 116 | 117 | Some text to wrap. 118 | 119 | 120 | 121 | ``` 122 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | CC0 1.0 Universal 2 | 3 | Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an "owner") of an original work of 8 | authorship and/or a database (each, a "Work"). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific 12 | works ("Commons") that the public can reliably and without fear of later 13 | claims of infringement build upon, modify, incorporate in other works, reuse 14 | and redistribute as freely as possible in any form whatsoever and for any 15 | purposes, including without limitation commercial purposes. These owners may 16 | contribute to the Commons to promote the ideal of a free culture and the 17 | further production of creative, cultural and scientific works, or to gain 18 | reputation or greater distribution for their Work in part through the use and 19 | efforts of others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation 22 | of additional consideration or compensation, the person associating CC0 with a 23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work 25 | and publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights ("Copyright and 31 | Related Rights"). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 34 | i. the right to reproduce, adapt, distribute, perform, display, communicate, 35 | and translate a Work; 36 | 37 | ii. moral rights retained by the original author(s) and/or performer(s); 38 | 39 | iii. publicity and privacy rights pertaining to a person's image or likeness 40 | depicted in a Work; 41 | 42 | iv. rights protecting against unfair competition in regards to a Work, 43 | subject to the limitations in paragraph 4(a), below; 44 | 45 | v. rights protecting the extraction, dissemination, use and reuse of data in 46 | a Work; 47 | 48 | vi. database rights (such as those arising under Directive 96/9/EC of the 49 | European Parliament and of the Council of 11 March 1996 on the legal 50 | protection of databases, and under any national implementation thereof, 51 | including any amended or successor version of such directive); and 52 | 53 | vii. other similar, equivalent or corresponding rights throughout the world 54 | based on applicable law or treaty, and any national implementations thereof. 55 | 56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright 59 | and Related Rights and associated claims and causes of action, whether now 60 | known or unknown (including existing as well as future claims and causes of 61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 62 | duration provided by applicable law or treaty (including future time 63 | extensions), (iii) in any current or future medium and for any number of 64 | copies, and (iv) for any purpose whatsoever, including without limitation 65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes 66 | the Waiver for the benefit of each member of the public at large and to the 67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver 68 | shall not be subject to revocation, rescission, cancellation, termination, or 69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work 70 | by the public as contemplated by Affirmer's express Statement of Purpose. 71 | 72 | 3. Public License Fallback. Should any part of the Waiver for any reason be 73 | judged legally invalid or ineffective under applicable law, then the Waiver 74 | shall be preserved to the maximum extent permitted taking into account 75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver 76 | is so judged Affirmer hereby grants to each affected person a royalty-free, 77 | non transferable, non sublicensable, non exclusive, irrevocable and 78 | unconditional license to exercise Affirmer's Copyright and Related Rights in 79 | the Work (i) in all territories worldwide, (ii) for the maximum duration 80 | provided by applicable law or treaty (including future time extensions), (iii) 81 | in any current or future medium and for any number of copies, and (iv) for any 82 | purpose whatsoever, including without limitation commercial, advertising or 83 | promotional purposes (the "License"). The License shall be deemed effective as 84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the 85 | License for any reason be judged legally invalid or ineffective under 86 | applicable law, such partial invalidity or ineffectiveness shall not 87 | invalidate the remainder of the License, and in such case Affirmer hereby 88 | affirms that he or she will not (i) exercise any of his or her remaining 89 | Copyright and Related Rights in the Work or (ii) assert any associated claims 90 | and causes of action with respect to the Work, in either case contrary to 91 | Affirmer's express Statement of Purpose. 92 | 93 | 4. Limitations and Disclaimers. 94 | 95 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 96 | surrendered, licensed or otherwise affected by this document. 97 | 98 | b. Affirmer offers the Work as-is and makes no representations or warranties 99 | of any kind concerning the Work, express, implied, statutory or otherwise, 100 | including without limitation warranties of title, merchantability, fitness 101 | for a particular purpose, non infringement, or the absence of latent or 102 | other defects, accuracy, or the present or absence of errors, whether or not 103 | discoverable, all to the greatest extent permissible under applicable law. 104 | 105 | c. Affirmer disclaims responsibility for clearing rights of other persons 106 | that may apply to the Work or any use thereof, including without limitation 107 | any person's Copyright and Related Rights in the Work. Further, Affirmer 108 | disclaims responsibility for obtaining any necessary consents, permissions 109 | or other rights required for any use of the Work. 110 | 111 | d. Affirmer understands and acknowledges that Creative Commons is not a 112 | party to this document and has no duty or obligation with respect to this 113 | CC0 or use of the Work. 114 | 115 | For more information, please see 116 | 117 | 118 | -------------------------------------------------------------------------------- /stylesheets/stylesheet.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; } 3 | 4 | body { 5 | padding: 0; 6 | margin: 0; 7 | font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 8 | font-size: 16px; 9 | line-height: 1.5; 10 | color: #606c71; } 11 | 12 | a { 13 | color: rgb(25,118,210); 14 | text-decoration: none; } 15 | a:hover { 16 | text-decoration: underline; 17 | text-decoration-skip: ink; 18 | } 19 | 20 | .btn { 21 | display: inline-block; 22 | margin-bottom: 1rem; 23 | color: rgba(255, 255, 255, 0.7); 24 | background-color: rgba(255, 255, 255, 0.08); 25 | border-color: rgba(255, 255, 255, 0.2); 26 | border-style: solid; 27 | border-width: 1px; 28 | border-radius: 0.3rem; 29 | transition: color 0.2s, background-color 0.2s, border-color 0.2s; } 30 | .btn + .btn { 31 | margin-left: 1rem; } 32 | 33 | .btn:hover { 34 | color: rgba(255, 255, 255, 0.8); 35 | text-decoration: none; 36 | background-color: rgba(255, 255, 255, 0.2); 37 | border-color: rgba(255, 255, 255, 0.3); } 38 | 39 | @media screen and (min-width: 64em) { 40 | .btn { 41 | padding: 0.75rem 1rem; } } 42 | 43 | @media screen and (min-width: 42em) and (max-width: 64em) { 44 | .btn { 45 | padding: 0.6rem 0.9rem; 46 | font-size: 0.9rem; } } 47 | 48 | @media screen and (max-width: 42em) { 49 | .btn { 50 | display: block; 51 | width: 100%; 52 | padding: 0.75rem; 53 | font-size: 0.9rem; } 54 | .btn + .btn { 55 | margin-top: 1rem; 56 | margin-left: 0; } } 57 | 58 | .page-header { 59 | color: #fff; 60 | text-align: center; 61 | background-color: rgb(25,118,210); 62 | background-image: linear-gradient(120deg, rgb(25,118,210), rgb(0, 129, 255)); } 63 | 64 | .page-header a { 65 | color: #fff; } 66 | 67 | @media screen and (min-width: 64em) { 68 | .page-header { 69 | padding: 5rem 6rem; } 70 | .page-header a { 71 | color: #fff; } } 72 | 73 | @media screen and (min-width: 42em) and (max-width: 64em) { 74 | .page-header { 75 | padding: 3rem 4rem; } 76 | .page-header a { 77 | color: #fff; } } 78 | 79 | @media screen and (max-width: 42em) { 80 | .page-header { 81 | padding: 2rem 1rem; } 82 | .page-header a { 83 | color: #fff; } } 84 | 85 | .project-name { 86 | margin-top: 0; 87 | margin-bottom: 0.1rem; } 88 | 89 | @media screen and (min-width: 64em) { 90 | .project-name { 91 | font-size: 3.25rem; } } 92 | 93 | @media screen and (min-width: 42em) and (max-width: 64em) { 94 | .project-name { 95 | font-size: 2.25rem; } } 96 | 97 | @media screen and (max-width: 42em) { 98 | .project-name { 99 | font-size: 1.75rem; } } 100 | 101 | .project-tagline { 102 | margin-bottom: 2rem; 103 | font-weight: normal; 104 | opacity: 0.7; } 105 | 106 | @media screen and (min-width: 64em) { 107 | .project-tagline { 108 | font-size: 1.25rem; } } 109 | 110 | @media screen and (min-width: 42em) and (max-width: 64em) { 111 | .project-tagline { 112 | font-size: 1.15rem; } } 113 | 114 | @media screen and (max-width: 42em) { 115 | .project-tagline { 116 | font-size: 1rem; } } 117 | 118 | .main-content :first-child { 119 | margin-top: 0; } 120 | .main-content img { 121 | max-width: 100%; } 122 | .main-content h1, .main-content h2, .main-content h3, .main-content h4, .main-content h5, .main-content h6 { 123 | margin-top: 2rem; 124 | margin-bottom: 1rem; 125 | font-weight: normal; 126 | color: rgb(1,66,162); } 127 | .main-content p { 128 | margin-bottom: 1em; } 129 | .main-content code { 130 | padding: 2px 4px; 131 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; 132 | font-size: 0.9rem; 133 | color: #383e41; 134 | background-color: #f3f6fa; 135 | border-radius: 0.3rem; } 136 | .main-content pre { 137 | padding: 0.8rem; 138 | margin-top: 0; 139 | margin-bottom: 1rem; 140 | font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; 141 | color: #567482; 142 | word-wrap: normal; 143 | background-color: #f3f6fa; 144 | border: solid 1px #dce6f0; 145 | border-radius: 0.3rem; } 146 | .main-content pre > code { 147 | padding: 0; 148 | margin: 0; 149 | font-size: 0.9rem; 150 | color: #567482; 151 | word-break: normal; 152 | white-space: pre; 153 | background: transparent; 154 | border: 0; } 155 | .main-content .highlight { 156 | margin-bottom: 1rem; } 157 | .main-content .highlight pre { 158 | margin-bottom: 0; 159 | word-break: normal; } 160 | .main-content .highlight pre, .main-content pre { 161 | padding: 0.8rem; 162 | overflow: auto; 163 | font-size: 0.9rem; 164 | line-height: 1.45; 165 | border-radius: 0.3rem; } 166 | .main-content pre code, .main-content pre tt { 167 | display: inline; 168 | max-width: initial; 169 | padding: 0; 170 | margin: 0; 171 | overflow: initial; 172 | line-height: inherit; 173 | word-wrap: normal; 174 | background-color: transparent; 175 | border: 0; } 176 | .main-content pre code:before, .main-content pre code:after, .main-content pre tt:before, .main-content pre tt:after { 177 | content: normal; } 178 | .main-content ul, .main-content ol { 179 | margin-top: 0; } 180 | .main-content blockquote { 181 | padding: 0 1rem; 182 | margin-left: 0; 183 | color: #819198; 184 | border-left: 0.3rem solid #dce6f0; } 185 | .main-content blockquote > :first-child { 186 | margin-top: 0; } 187 | .main-content blockquote > :last-child { 188 | margin-bottom: 0; } 189 | .main-content table { 190 | display: block; 191 | width: 100%; 192 | overflow: auto; 193 | word-break: normal; 194 | word-break: keep-all; } 195 | .main-content table th { 196 | font-weight: bold; } 197 | .main-content table th, .main-content table td { 198 | padding: 0.5rem 1rem; 199 | border: 1px solid #e9ebec; } 200 | .main-content dl { 201 | padding: 0; } 202 | .main-content dl dt { 203 | padding: 0; 204 | margin-top: 1rem; 205 | font-size: 1rem; 206 | font-weight: bold; } 207 | .main-content dl dd { 208 | padding: 0; 209 | margin-bottom: 1rem; } 210 | .main-content hr { 211 | height: 2px; 212 | padding: 0; 213 | margin: 1rem 0; 214 | background-color: #eff0f1; 215 | border: 0; } 216 | 217 | @media screen and (min-width: 64em) { 218 | .main-content { 219 | max-width: 64rem; 220 | padding: 2rem 6rem; 221 | margin: 0 auto; 222 | font-size: 1.1rem; } } 223 | 224 | @media screen and (min-width: 42em) and (max-width: 64em) { 225 | .main-content { 226 | padding: 2rem 4rem; 227 | font-size: 1.1rem; } } 228 | 229 | @media screen and (max-width: 42em) { 230 | .main-content { 231 | padding: 2rem 1rem; 232 | font-size: 1rem; } } 233 | 234 | .site-footer { 235 | padding-top: 2rem; 236 | margin-top: 2rem; 237 | border-top: solid 1px #eff0f1; } 238 | 239 | .site-footer-owner { 240 | display: block; 241 | font-weight: bold; } 242 | 243 | .site-footer-credits { 244 | color: #819198; } 245 | 246 | @media screen and (min-width: 64em) { 247 | .site-footer { 248 | font-size: 1rem; } } 249 | 250 | @media screen and (min-width: 42em) and (max-width: 64em) { 251 | .site-footer { 252 | font-size: 1rem; } } 253 | 254 | @media screen and (max-width: 42em) { 255 | .site-footer { 256 | font-size: 0.9rem; } } 257 | -------------------------------------------------------------------------------- /features/DI.md: -------------------------------------------------------------------------------- 1 | # Dependency Injection 2 | ----------------------------------- 3 | 4 | ## Core Abstractions 5 | 6 | The library is built on top of the following core abstractions: `Injector`, `Binding`, and `Dependency`. 7 | 8 | * An injector is created from a set of bindings. 9 | * An injector resolves dependencies and creates objects. 10 | * A binding maps a token, such as a string or class, to a factory function and a list of dependencies. So a binding defines how to create an object. 11 | * A dependency points to a token and contains extra information on how the object corresponding to that token should be injected. 12 | 13 | ``` 14 | [Injector] 15 | | 16 | | 17 | |* 18 | [Binding] 19 | |----------|-----------------| 20 | | | |* 21 | [Token] [FactoryFn] [Dependency] 22 | |---------| 23 | | | 24 | [Token] [Flags] 25 | ``` 26 | 27 | ## Example 28 | 29 | ``` javascript 30 | class Engine { 31 | } 32 | 33 | class Car { 34 | constructor(@Inject(Engine) engine) { 35 | } 36 | } 37 | 38 | var inj = Injector.resolveAndCreate([ 39 | bind(Car).toClass(Car), 40 | bind(Engine).toClass(Engine) 41 | ]); 42 | var car = inj.get(Car); 43 | ``` 44 | 45 | In this example we create two bindings: one for Car and one for Engine. `@Inject(Engine)` declares a dependency on Engine. 46 | 47 | 48 | 49 | ## Injector 50 | 51 | An injector instantiates objects lazily, only when asked for, and then caches them. 52 | 53 | Compare 54 | 55 | ``` javascript 56 | var car = inj.get(Car); //instantiates both an Engine and a Car 57 | ``` 58 | 59 | with 60 | 61 | ``` javascript 62 | var engine = inj.get(Engine); //instantiates an Engine 63 | var car = inj.get(Car); //instantiates a Car (reuses Engine) 64 | ``` 65 | 66 | and with 67 | 68 | ``` javascript 69 | var car = inj.get(Car); //instantiates both an Engine and a Car 70 | var engine = inj.get(Engine); //reads the Engine from the cache 71 | ``` 72 | 73 | To avoid bugs make sure the registered objects have side-effect-free constructors. In this case, an injector acts like a hash map, where the order in which the objects got created does not matter. 74 | 75 | 76 | ## Child Injectors and Dependencies 77 | 78 | Injectors are hierarchical. 79 | 80 | ``` js 81 | var parent = Injector.resolveAndCreate([ 82 | bind(Engine).toClass(TurboEngine) 83 | ]); 84 | var child = parent.resolveAndCreateChild([Car]); 85 | 86 | var car = child.get(Car); // uses the Car binding from the child injector and Engine from the parent injector. 87 | ``` 88 | 89 | Injectors form a tree. 90 | 91 | ``` 92 | GrandParentInjector 93 | / \ 94 | Parent1Injector Parent2Injector 95 | | 96 | ChildInjector 97 | ``` 98 | 99 | The dependency resolution algorithm works as follows: 100 | 101 | ``` js 102 | // this is pseudocode. 103 | var inj = this; 104 | while (inj) { 105 | if (inj.hasKey(requestedKey)) { 106 | return inj.get(requestedKey); 107 | } else { 108 | inj = inj.parent; 109 | } 110 | } 111 | throw new NoProviderError(requestedKey); 112 | ``` 113 | 114 | So in the following example 115 | 116 | ``` js 117 | class Car { 118 | constructor(e: Engine){} 119 | } 120 | ``` 121 | 122 | DI will start resolving `Engine` in the same injector where the `Car` binding is defined. It will check whether that injector has the `Engine` binding. If it is the case, it will return that instance. If not, the injector will ask its parent whether it has an instance of `Engine`. The process continues until either an instance of `Engine` has been found, or we have reached the root of the injector tree. 123 | 124 | ### Constraints 125 | 126 | You can put upper and lower bound constraints on a dependency. For instance, the `@Self` decorator tells DI to look for `Engine` only in the same injector where `Car` is defined. So it will not walk up the tree. 127 | 128 | ``` js 129 | class Car { 130 | constructor(@Self() e: Engine){} 131 | } 132 | ``` 133 | 134 | A more realistic example is having two bindings that have to be provided together (e.g., NgModel and NgRequiredValidator.) 135 | 136 | The `@Host` decorator tells DI to look for `Engine` in this injector, its parent, until it reaches a host (see the section on hosts.) 137 | 138 | ``` js 139 | class Car { 140 | constructor(@Host() e: Engine){} 141 | } 142 | ``` 143 | 144 | The `@SkipSelf` decorator tells DI to look for `Engine` in the whole tree starting from the parent injector. 145 | 146 | ``` js 147 | class Car { 148 | constructor(@SkipSelf() e: Engine){} 149 | } 150 | ``` 151 | 152 | #### DI Does Not Walk Down 153 | 154 | 155 | Dependency resolution only walks up the tree. The following will throw because DI will look for an instance of `Engine` starting from `parent`. 156 | 157 | ``` js 158 | var parent = Injector.resolveAndCreate([Car]); 159 | var child = parent.resolveAndCreateChild([ 160 | bind(Engine).toClass(TurboEngine) 161 | ]); 162 | 163 | parent.get(Car); // will throw NoProviderError 164 | ``` 165 | 166 | 167 | ## Bindings 168 | 169 | You can bind to a class, a value, or a factory. It is also possible to alias existing bindings. 170 | 171 | ``` js 172 | var inj = Injector.resolveAndCreate([ 173 | bind(Car).toClass(Car), 174 | bind(Engine).toClass(Engine) 175 | ]); 176 | 177 | var inj = Injector.resolveAndCreate([ 178 | Car, // syntax sugar for bind(Car).toClass(Car) 179 | Engine 180 | ]); 181 | 182 | var inj = Injector.resolveAndCreate([ 183 | bind(Car).toValue(new Car(new Engine())) 184 | ]); 185 | 186 | var inj = Injector.resolveAndCreate([ 187 | bind(Car).toFactory((e) => new Car(e), [Engine]), 188 | bind(Engine).toFactory(() => new Engine()) 189 | ]); 190 | ``` 191 | 192 | You can bind any token. 193 | 194 | ``` js 195 | var inj = Injector.resolveAndCreate([ 196 | bind(Car).toFactory((e) => new Car(), ["engine!"]), 197 | bind("engine!").toClass(Engine) 198 | ]); 199 | ``` 200 | 201 | If you want to alias an existing binding, you can do so using `toAlias`: 202 | 203 | ``` js 204 | var inj = Injector.resolveAndCreate([ 205 | bind(Engine).toClass(Engine), 206 | bind("engine!").toAlias(Engine) 207 | ]); 208 | ``` 209 | which implies `inj.get(Engine) === inj.get("engine!")`. 210 | 211 | Note that tokens and factory functions are decoupled. 212 | 213 | ``` js 214 | bind("some token").toFactory(someFactory); 215 | ``` 216 | 217 | The `someFactory` function does not have to know that it creates an object for `some token`. 218 | 219 | ### Resolved Bindings 220 | 221 | When DI receives `bind(Car).toClass(Car)`, it needs to do a few things before it can create an instance of `Car`: 222 | 223 | - It needs to reflect on `Car` to create a factory function. 224 | - It needs to normalize the dependencies (e.g., calculate lower and upper bounds). 225 | 226 | The result of these two operations is a `ResolvedBinding`. 227 | 228 | The `resolveAndCreate` and `resolveAndCreateChild` functions resolve passed-in bindings before creating an injector. But you can resolve bindings yourself using `Injector.resolve([bind(Car).toClass(Car)])`. Creating an injector from pre-resolved bindings is faster, and may be needed for performance sensitive areas. 229 | 230 | You can create an injector using a list of resolved bindings. 231 | 232 | ``` js 233 | var listOfResolvingBindings = Injector.resolve([Binding1, Binding2]); 234 | var inj = Injector.fromResolvedBindings(listOfResolvingBindings); 235 | inj.createChildFromResolvedBindings(listOfResolvedBindings); 236 | ``` 237 | 238 | 239 | ### Transient Dependencies 240 | 241 | An injector has only one instance created by each registered binding. 242 | 243 | ``` js 244 | inj.get(MyClass) === inj.get(MyClass); //always holds 245 | ``` 246 | 247 | If we need a transient dependency, something that we want a new instance of every single time, we have two options. 248 | 249 | We can create a child injector for each new instance: 250 | 251 | ``` js 252 | var child = inj.resolveAndCreateChild([MyClass]); 253 | child.get(MyClass); 254 | ``` 255 | 256 | Or we can register a factory function: 257 | 258 | ``` js 259 | var inj = Injector.resolveAndCreate([ 260 | bind('MyClassFactory').toFactory(dep => () => new MyClass(dep), [SomeDependency]) 261 | ]); 262 | 263 | var factory = inj.get('MyClassFactory'); 264 | var instance1 = factory(), instance2 = factory(); 265 | // Depends on the implementation of MyClass, but generally holds. 266 | expect(instance1).not.toBe(instance2); 267 | ``` 268 | -------------------------------------------------------------------------------- /stylesheets/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ 2 | 3 | /** 4 | * 1. Set default font family to sans-serif. 5 | * 2. Prevent iOS text size adjust after orientation change, without disabling 6 | * user zoom. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; /* 1 */ 11 | -ms-text-size-adjust: 100%; /* 2 */ 12 | -webkit-text-size-adjust: 100%; /* 2 */ 13 | } 14 | 15 | /** 16 | * Remove default margin. 17 | */ 18 | 19 | body { 20 | margin: 0; 21 | } 22 | 23 | /* HTML5 display definitions 24 | ========================================================================== */ 25 | 26 | /** 27 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 29 | * and Firefox. 30 | * Correct `block` display not defined for `main` in IE 11. 31 | */ 32 | 33 | article, 34 | aside, 35 | details, 36 | figcaption, 37 | figure, 38 | footer, 39 | header, 40 | hgroup, 41 | main, 42 | menu, 43 | nav, 44 | section, 45 | summary { 46 | display: block; 47 | } 48 | 49 | /** 50 | * 1. Correct `inline-block` display not defined in IE 8/9. 51 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 52 | */ 53 | 54 | audio, 55 | canvas, 56 | progress, 57 | video { 58 | display: inline-block; /* 1 */ 59 | vertical-align: baseline; /* 2 */ 60 | } 61 | 62 | /** 63 | * Prevent modern browsers from displaying `audio` without controls. 64 | * Remove excess height in iOS 5 devices. 65 | */ 66 | 67 | audio:not([controls]) { 68 | display: none; 69 | height: 0; 70 | } 71 | 72 | /** 73 | * Address `[hidden]` styling not present in IE 8/9/10. 74 | * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. 75 | */ 76 | 77 | [hidden], 78 | template { 79 | display: none; 80 | } 81 | 82 | /* Links 83 | ========================================================================== */ 84 | 85 | /** 86 | * Remove the gray background color from active links in IE 10. 87 | */ 88 | 89 | a { 90 | background-color: transparent; 91 | } 92 | 93 | /** 94 | * Improve readability when focused and also mouse hovered in all browsers. 95 | */ 96 | 97 | a:active, 98 | a:hover { 99 | outline: 0; 100 | } 101 | 102 | /* Text-level semantics 103 | ========================================================================== */ 104 | 105 | /** 106 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 107 | */ 108 | 109 | abbr[title] { 110 | border-bottom: 1px dotted; 111 | } 112 | 113 | /** 114 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 115 | */ 116 | 117 | b, 118 | strong { 119 | font-weight: bold; 120 | } 121 | 122 | /** 123 | * Address styling not present in Safari and Chrome. 124 | */ 125 | 126 | dfn { 127 | font-style: italic; 128 | } 129 | 130 | /** 131 | * Address variable `h1` font-size and margin within `section` and `article` 132 | * contexts in Firefox 4+, Safari, and Chrome. 133 | */ 134 | 135 | h1 { 136 | font-size: 2em; 137 | margin: 0.67em 0; 138 | } 139 | 140 | /** 141 | * Address styling not present in IE 8/9. 142 | */ 143 | 144 | mark { 145 | background: #ff0; 146 | color: #000; 147 | } 148 | 149 | /** 150 | * Address inconsistent and variable font size in all browsers. 151 | */ 152 | 153 | small { 154 | font-size: 80%; 155 | } 156 | 157 | /** 158 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 159 | */ 160 | 161 | sub, 162 | sup { 163 | font-size: 75%; 164 | line-height: 0; 165 | position: relative; 166 | vertical-align: baseline; 167 | } 168 | 169 | sup { 170 | top: -0.5em; 171 | } 172 | 173 | sub { 174 | bottom: -0.25em; 175 | } 176 | 177 | /* Embedded content 178 | ========================================================================== */ 179 | 180 | /** 181 | * Remove border when inside `a` element in IE 8/9/10. 182 | */ 183 | 184 | img { 185 | border: 0; 186 | } 187 | 188 | /** 189 | * Correct overflow not hidden in IE 9/10/11. 190 | */ 191 | 192 | svg:not(:root) { 193 | overflow: hidden; 194 | } 195 | 196 | /* Grouping content 197 | ========================================================================== */ 198 | 199 | /** 200 | * Address margin not present in IE 8/9 and Safari. 201 | */ 202 | 203 | figure { 204 | margin: 1em 40px; 205 | } 206 | 207 | /** 208 | * Address differences between Firefox and other browsers. 209 | */ 210 | 211 | hr { 212 | box-sizing: content-box; 213 | height: 0; 214 | } 215 | 216 | /** 217 | * Contain overflow in all browsers. 218 | */ 219 | 220 | pre { 221 | overflow: auto; 222 | } 223 | 224 | /** 225 | * Address odd `em`-unit font size rendering in all browsers. 226 | */ 227 | 228 | code, 229 | kbd, 230 | pre, 231 | samp { 232 | font-family: monospace, monospace; 233 | font-size: 1em; 234 | } 235 | 236 | /* Forms 237 | ========================================================================== */ 238 | 239 | /** 240 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 241 | * styling of `select`, unless a `border` property is set. 242 | */ 243 | 244 | /** 245 | * 1. Correct color not being inherited. 246 | * Known issue: affects color of disabled elements. 247 | * 2. Correct font properties not being inherited. 248 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 249 | */ 250 | 251 | button, 252 | input, 253 | optgroup, 254 | select, 255 | textarea { 256 | color: inherit; /* 1 */ 257 | font: inherit; /* 2 */ 258 | margin: 0; /* 3 */ 259 | } 260 | 261 | /** 262 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 263 | */ 264 | 265 | button { 266 | overflow: visible; 267 | } 268 | 269 | /** 270 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 271 | * All other form control elements do not inherit `text-transform` values. 272 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 273 | * Correct `select` style inheritance in Firefox. 274 | */ 275 | 276 | button, 277 | select { 278 | text-transform: none; 279 | } 280 | 281 | /** 282 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 283 | * and `video` controls. 284 | * 2. Correct inability to style clickable `input` types in iOS. 285 | * 3. Improve usability and consistency of cursor style between image-type 286 | * `input` and others. 287 | */ 288 | 289 | button, 290 | html input[type="button"], /* 1 */ 291 | input[type="reset"], 292 | input[type="submit"] { 293 | -webkit-appearance: button; /* 2 */ 294 | cursor: pointer; /* 3 */ 295 | } 296 | 297 | /** 298 | * Re-set default cursor for disabled elements. 299 | */ 300 | 301 | button[disabled], 302 | html input[disabled] { 303 | cursor: default; 304 | } 305 | 306 | /** 307 | * Remove inner padding and border in Firefox 4+. 308 | */ 309 | 310 | button::-moz-focus-inner, 311 | input::-moz-focus-inner { 312 | border: 0; 313 | padding: 0; 314 | } 315 | 316 | /** 317 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 318 | * the UA stylesheet. 319 | */ 320 | 321 | input { 322 | line-height: normal; 323 | } 324 | 325 | /** 326 | * It's recommended that you don't attempt to style these elements. 327 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 328 | * 329 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 330 | * 2. Remove excess padding in IE 8/9/10. 331 | */ 332 | 333 | input[type="checkbox"], 334 | input[type="radio"] { 335 | box-sizing: border-box; /* 1 */ 336 | padding: 0; /* 2 */ 337 | } 338 | 339 | /** 340 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 341 | * `font-size` values of the `input`, it causes the cursor style of the 342 | * decrement button to change from `default` to `text`. 343 | */ 344 | 345 | input[type="number"]::-webkit-inner-spin-button, 346 | input[type="number"]::-webkit-outer-spin-button { 347 | height: auto; 348 | } 349 | 350 | /** 351 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 352 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome 353 | * (include `-moz` to future-proof). 354 | */ 355 | 356 | input[type="search"] { 357 | -webkit-appearance: textfield; /* 1 */ /* 2 */ 358 | box-sizing: content-box; 359 | } 360 | 361 | /** 362 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 363 | * Safari (but not Chrome) clips the cancel button when the search input has 364 | * padding (and `textfield` appearance). 365 | */ 366 | 367 | input[type="search"]::-webkit-search-cancel-button, 368 | input[type="search"]::-webkit-search-decoration { 369 | -webkit-appearance: none; 370 | } 371 | 372 | /** 373 | * Define consistent border, margin, and padding. 374 | */ 375 | 376 | fieldset { 377 | border: 1px solid #c0c0c0; 378 | margin: 0 2px; 379 | padding: 0.35em 0.625em 0.75em; 380 | } 381 | 382 | /** 383 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 384 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 385 | */ 386 | 387 | legend { 388 | border: 0; /* 1 */ 389 | padding: 0; /* 2 */ 390 | } 391 | 392 | /** 393 | * Remove default vertical scrollbar in IE 8/9/10/11. 394 | */ 395 | 396 | textarea { 397 | overflow: auto; 398 | } 399 | 400 | /** 401 | * Don't inherit the `font-weight` (applied by a rule above). 402 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 403 | */ 404 | 405 | optgroup { 406 | font-weight: bold; 407 | } 408 | 409 | /* Tables 410 | ========================================================================== */ 411 | 412 | /** 413 | * Remove most spacing between table cells. 414 | */ 415 | 416 | table { 417 | border-collapse: collapse; 418 | border-spacing: 0; 419 | } 420 | 421 | td, 422 | th { 423 | padding: 0; 424 | } 425 | -------------------------------------------------------------------------------- /features/View.md: -------------------------------------------------------------------------------- 1 | # View 2 | ----------------------------------- 3 | ## Overview 4 | A View is a core primitive used by angular to render the DOM tree. 5 | A ViewContainer is location in a View which can accept child Views. 6 | Every ViewContainer has an associated ViewContainerRef than can contain any number of child Views. 7 | Views form a tree structure which mimics the DOM tree. 8 | 9 | * View is a core rendering construct. A running application is just a collection of Views which are 10 | nested in a tree like structure. The View tree is a simplified version of the DOM tree. A View can 11 | have a single DOM Element or large DOM structures. The key is that the DOM tree in the View can 12 | not undergo structural changes (only property changes). 13 | * Views represent a running instance of a DOM View. This implies that while elements in a View 14 | can change properties, they can not change structurally. (Structural changes such as, adding or 15 | removing elements requires adding or removing child Views into ViewContainers). 16 | * View can have zero or more ViewContainers. A ViewContainer is a marker in the DOM which allows 17 | the insertion of child Views. 18 | * Views are created from a ProtoView. A ProtoView is a compiled DOM View which is efficient at 19 | creating Views. 20 | * View contains a context object. The context represents the object instance against which all 21 | expressions are evaluated. 22 | * View contains a ChangeDetector for looking for detecting changes to the model. 23 | * View contains ElementInjector for creating Directives. 24 | 25 | ## Simple View 26 | 27 | Let's examine a simple View and all of its parts in detail. 28 | 29 | Assume the following Component: 30 | 31 | ``` javascript 32 | class Greeter { 33 | greeting:string; 34 | 35 | constructor() { 36 | this.greeting = 'Hello'; 37 | } 38 | } 39 | ``` 40 | 41 | And assume following HTML View: 42 | 43 | ``` html 44 |
45 | Your name: 46 | 47 |
48 | {{greeting}} {{name.value}}! 49 |
50 | ``` 51 | 52 | The above template is compiled by the Compiler to create a ProtoView. The ProtoView is then used to 53 | create an instance of the View. The instantiation process involves cloning the above template and 54 | locating all of the elements which contain bindings and finally instantiating the Directives 55 | associated with the template. (See compilation for more details.) 56 | 57 | ``` html 58 |
| viewA(greeter) 59 | Your name: | viewA(greeter) 60 | | viewA(greeter): local variable 'name' 61 |
| viewA(greeter) 62 | {{greeting}} {{name.value}}! | viewA(greeter): binding expression 'greeting' & 'name.value' 63 |
| viewA(greeter) 64 | ``` 65 | 66 | The resulting View instance looks something like this (simplified pseudo code): 67 | ``` javascript 68 | viewA = new View({ 69 | template: ..., 70 | context: new Greeter(), 71 | localVars: ['name'], 72 | watchExp: ['greeting', 'name.value'] 73 | }); 74 | ``` 75 | 76 | Note: 77 | * View uses instance of `Greeter` as the evaluation context. 78 | * View knows of local variables `name`. 79 | * View knows which expressions need to be watched. 80 | * View knows what needs to be updated if the watched expression changes. 81 | * All DOM elements are owned by single instance of the view. 82 | * The structure of the DOM can not change during runtime. To allow structural changes to the DOM we need 83 | to understand Composed View. 84 | 85 | ## Composed View 86 | 87 | An important part of an application is to be able to change the DOM structure to render data for the 88 | user. In Angular this is done by inserting child views into the ViewContainer. 89 | 90 | Let's start with a View such as: 91 | 92 | ``` html 93 |
    94 |
  • {{person}}
  • 95 |
96 | ``` 97 | 98 | During the compilation process the Compiler breaks the HTML template into these two ProtoViews: 99 | 100 | ``` html 101 |
  • {{person}}
  • | protoViewB(Locals) 102 | ``` 103 | 104 | and 105 | 106 | ``` html 107 |
      | protoViewA(someContext) 108 | | protoViewA(someContext): protoViewB 109 |
    | protoViewA(someContext) 110 | ``` 111 | 112 | 113 | The next step is to compose these two ProtoViews into an actual view which is rendered to the user. 114 | 115 | *Step 1:* Instantiate `viewA` 116 | 117 | ``` html 118 |
      | viewA(someContext) 119 | | viewA(someContext): new NgFor(new ViewContainer(protoViewB)) 120 |
    | viewA(someContext) 121 | ``` 122 | 123 | *Step2:* Instantiate `NgFor` directive which will receive the `ViewContainerRef`. (The ViewContainerRef 124 | has a reference to `protoViewA`). 125 | 126 | 127 | *Step3:* As the `NgFor` directive unrolls it asks the `ViewContainerRef` to instantiate `protoViewB` and insert 128 | it after the `ViewContainer` anchor. This is repeated for each `person` in `people`. Notice that 129 | 130 | ``` html 131 |
      | viewA(someContext) 132 | | viewA(someContext): new NgFor(new ViewContainer(protoViewB)) 133 |
    • {{person}}
    • | viewB0(locals0(someContext)) 134 |
    • {{person}}
    • | viewB1(locals0(someContext)) 135 |
    | viewA(someContext) 136 | ``` 137 | 138 | *Step4:* All of the bindings in the child Views are updated. Notice that in the case of `NgFor` 139 | the evaluation context for the `viewB0` and `viewB1` are `locals0` and `locals1` respectively. 140 | Locals allow the introduction of new local variables visible only within the scope of the View, and 141 | delegate any unknown references to the parent context. 142 | 143 | ``` html 144 |
      | viewA 145 | | viewA: new NgFor(new ViewContainer(protoViewB)) 146 |
    • Alice
    • | viewB0 147 |
    • Bob
    • | viewB1 148 |
    | viewA 149 | ``` 150 | 151 | Each View can have zero or more ViewContainers. By inserting and removing child Views to and from the 152 | ViewContainers, the application can mutate the DOM structure to any desirable state. A View may contain 153 | individual nodes or a complex DOM structure. The insertion points for the child Views, known as 154 | ViewContainers, contain a DOM element which acts as an anchor. The anchor is either a `template` or 155 | a `script` element depending on your browser. It is used to identify where the child Views will be 156 | inserted. 157 | 158 | ## Component Views 159 | 160 | A View can also contain Components. Components contain Shadow DOM for encapsulating their internal 161 | rendering state. Unlike ViewContainers which can contain zero or more Views, the Component always contains 162 | exactly one Shadow View. 163 | 164 | ``` html 165 |
    | viewA 166 | | viewA 167 | #SHADOW_ROOT | (encapsulation boundary) 168 |
    | viewB 169 | encapsulated rendering | viewB 170 |
    | viewB 171 |
    | viewA 172 |
    | viewA 173 | ``` 174 | 175 | ## Evaluation Context 176 | 177 | Each View acts as a context for evaluating its expressions. There are two kinds of contexts: 178 | 179 | 1. A component controller instance and 180 | 2. a `Locals` context for introducing local variables into the View. 181 | 182 | Let's assume following component: 183 | 184 | ``` javascript 185 | class Greeter { 186 | greeting:string; 187 | 188 | constructor() { 189 | this.greeting = 'Hello'; 190 | } 191 | } 192 | ``` 193 | 194 | And assume the following HTML View: 195 | 196 | ``` html 197 |
    | viewA(greeter) 198 | Your name: | viewA(greeter) 199 | | viewA(greeter) 200 |
    | viewA(greeter) 201 | {{greeting}} {{name.value}}! | viewA(greeter) 202 |
    | viewA(greeter) 203 | ``` 204 | 205 | The above UI is built using a single View, and hence a single context `greeter`. It can be expressed 206 | in this pseudo-code. 207 | 208 | ``` javascript 209 | var greeter = new Greeter(); 210 | ``` 211 | 212 | The View contains two bindings: 213 | 214 | 1. `greeting`: This is bound to the `greeting` property on the `Greeter` instance. 215 | 2. `name.value`: This poses a problem. There is no `name` property on the `Greeter` instance. To solve 216 | this we wrap the `Greeter` instance in the `Local` instance like so: 217 | 218 | ``` javascript 219 | var greeter = new Locals(new Greeter(), {name: ref_to_input_element }) 220 | ``` 221 | 222 | 223 | By wrapping the `Greeter` instance into the `Locals` we allow the view to introduce variables which 224 | are in addition to the `Greeter` instance. During the resolution of the expressions we first check 225 | the locals, and then the `Greeter` instance. 226 | 227 | ## View LifeCycle (Hydration and Dehydration) 228 | 229 | Views transition through a particular set of states: 230 | 231 | 1. View is created from the ProtoView. 232 | 2. View can be attached to an existing ViewContainerRef. 233 | 3. Upon attaching View to the ViewContainerRef the View needs to be hydrated. The hydration process 234 | involves instantiating all of the Directives associated with the current View. 235 | 4. At this point the view is ready and renderable. Multiple changes can be delivered to the 236 | Directives from the ChangeDetection. 237 | 5. At some point the View can be removed. At this point all of the directives are destroyed during 238 | the dehydration process and the view becomes inactive. 239 | 6. The View has to wait until it is detached from the DOM. The delay in detaching could be caused 240 | because an animation is animating the view away. 241 | 7. After the View is detached from the DOM it is ready to be reused. The view reuse allows the 242 | application to be faster in subsequent renderings. 243 | -------------------------------------------------------------------------------- /features/Templates.md: -------------------------------------------------------------------------------- 1 | # Templates 2 | ----------------------------------- 3 | Templates are markup which is added to HTML to declaratively describe how the application model should be 4 | projected to DOM as well as which DOM events should invoke which methods on the controller. Templates contain 5 | syntaxes which are core to Angular and allows for data-binding, event-binding, template-instantiation. 6 | 7 | The design of the template syntax has these properties: 8 | 9 | 10 | * All data-binding expressions are easily identifiable. (i.e. there is never an ambiguity whether the value should be 11 | interpreted as string literal or as an expression.) 12 | * All events and their statements are easily identifiable. 13 | * All places of DOM instantiation are easily identifiable. 14 | * All places of variable declaration are easily identifiable. 15 | 16 | The above properties guarantee that the templates are easy to parse by tools (such as IDEs) and reason about by people. 17 | At no point is it necessary to understand which directives are active or what their semantics are in order to reason 18 | about the template runtime characteristics. 19 | 20 | ## Property bindings 21 | 22 | Binding application model data to the UI is the most common kind of bindings in an Angular application. The bindings 23 | are always in the form of `property-name` which is assigned an `expression`. The generic form is: 24 | 25 | **Short form:** `` 26 | 27 | **Canonical form:** `` 28 | 29 | Where: 30 | * `some-element` can be any existing DOM element. 31 | * `some-property` (escaped with `[]` or `bind-`) is the name of the property on `some-element`. In this case the 32 | dash-case is converted into camel-case `someProperty`. 33 | * `expression` is a valid expression (as defined in section below). 34 | 35 | Example: 36 | ``` html 37 |
    38 | ``` 39 | 40 | In the above example the `title` property of the `div` element will be updated whenever the `user.firstName` changes 41 | its value. 42 | 43 | Key points: 44 | * The binding is to the element property not the element attribute. 45 | * To prevent custom element from accidentally reading the literal `expression` on the title element, the attribute name 46 | is escaped. In our case the `title` is escaped to `[title]` through the addition of square brackets `[]`. 47 | * A binding value (in this case `user.firstName`) will always be an expression, never a string literal. 48 | 49 | **NOTE:** Angular 2 binds to properties of elements rather than attributes of elements. This is 50 | done to better support custom elements, and to allow binding for values other than strings. 51 | 52 | **NOTE:** Some editors/server side pre-processors may have trouble generating `[]` around the attribute name. For this 53 | reason Angular also supports a canonical version which is prefixed using `bind-`. 54 | 55 | ## Binding Events 56 | 57 | Binding events allows wiring events from DOM (or other components) to the Angular controller. 58 | 59 | **Short form:** `` 60 | 61 | **Canonical form:** `` 62 | 63 | Where: 64 | * `some-element` Any element which can generate DOM events (or has an angular directive which generates the event). 65 | * `some-event` (escaped with `()` or `on-`) is the name of the event `some-event`. In this case the 66 | dash-case is converted into camel-case `someEvent`. 67 | * `statement` is a valid statement (as defined in section below). 68 | If the execution of the statement returns `false`, then `preventDefault`is applied on the DOM event. 69 | 70 | Angular listens to bubbled DOM events (as in the case of clicking on any child), as shown below: 71 | 72 | **Short form:** `` 73 | 74 | **Canonical form:** `` 75 | 76 | Example: 77 | ``` js 78 | @Component(...) 79 | class Example { 80 | submit() { 81 | // do something when button is clicked 82 | } 83 | } 84 | 85 | 86 | ``` 87 | 88 | In the above example, when clicking on the submit button angular will invoke the `submit` method on the surrounding 89 | component's controller. 90 | 91 | 92 | **NOTE:** Unlike Angular v1, Angular v2 treats event bindings as core constructs not as directives. This means that there 93 | is no need to create an event directive for each kind of event. This makes it possible for Angular v2 to easily 94 | bind to custom events of Custom Elements, whose event names are not known ahead of time. 95 | 96 | ## String Interpolation 97 | 98 | Property bindings are the only data bindings which Angular supports, but for convenience Angular supports an interpolation 99 | syntax which is just a short hand for the data binding syntax. 100 | 101 | ``` html 102 | Hello {{name}}! 103 | ``` 104 | 105 | is a short hand for: 106 | 107 | ``` html 108 | _ 109 | ``` 110 | 111 | The above says to bind the `'Hello ' + stringify(name) + '!'` expression to the zero-th child of the `span`'s `text` 112 | property. The index is necessary in case there are more than one text nodes, or if the text node we wish to bind to 113 | is not the first one. 114 | 115 | Similarly the same rules apply to interpolation inside attributes. 116 | 117 | ``` html 118 | 119 | ``` 120 | 121 | is a short hand for: 122 | 123 | ``` html 124 | 125 | ``` 126 | 127 | **NOTE:** `stringify()` is a built in implicit function which converts its argument to a string representation, while 128 | keeping `null` and `undefined` as empty strings. 129 | 130 | ## Inline Templates 131 | Data binding allows updating the DOM's properties, but it does not allow for changing of the DOM structure. To change 132 | DOM structure we need the ability to define child templates, and then instantiate these templates into Views. The 133 | Views then can be inserted and removed as needed to change the DOM structure. 134 | 135 | **Short form:** 136 | ``` html 137 | Hello {{user}}! 138 |
    139 | ...administrator menu here... 140 |
    141 | ``` 142 | 143 | **Canonical form:** 144 | ``` html 145 | Hello {{user}}! 146 | 151 | ``` 152 | 153 | Where: 154 | * `template` defines a child template and designates the anchor where Views (instances of the template) will be 155 | inserted. The template can be defined implicitly with `template` attribute, which turns the current element into 156 | a template, or explicitly with `