├── README.md ├── week1 ├── 1 │ ├── README.md │ └── todoApp │ │ ├── .editorconfig │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ │ ├── CHANGELOG.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── bs-config.e2e.json │ │ ├── bs-config.json │ │ ├── e2e │ │ ├── app.e2e-spec.ts │ │ └── tsconfig.json │ │ ├── karma-test-shim.js │ │ ├── karma.conf.js │ │ ├── non-essential-files.txt │ │ ├── package.json │ │ ├── protractor.config.js │ │ ├── src │ │ ├── app │ │ │ ├── add.component │ │ │ │ ├── add.component.css │ │ │ │ ├── add.component.html │ │ │ │ └── add.component.ts │ │ │ ├── app.component.spec.ts │ │ │ ├── app.component.ts │ │ │ ├── app.module.ts │ │ │ ├── enums │ │ │ │ └── filter.ts │ │ │ ├── filter.component │ │ │ │ ├── filter.component.css │ │ │ │ ├── filter.component.html │ │ │ │ └── filter.component.ts │ │ │ ├── list.component │ │ │ │ ├── list.component.css │ │ │ │ ├── list.component.html │ │ │ │ └── list.component.ts │ │ │ └── todo.model.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.ts │ │ ├── styles.css │ │ ├── systemjs.config.extras.js │ │ ├── systemjs.config.js │ │ └── tsconfig.json │ │ └── tslint.json └── 2 │ ├── README.md │ └── solutionsApp │ ├── .editorconfig │ ├── .gitignore │ ├── .travis.yml │ ├── .vscode │ ├── launch.json │ └── tasks.json │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── bs-config.e2e.json │ ├── bs-config.json │ ├── e2e │ ├── app.e2e-spec.ts │ └── tsconfig.json │ ├── karma-test-shim.js │ ├── karma.conf.js │ ├── non-essential-files.txt │ ├── package.json │ ├── protractor.config.js │ ├── src │ ├── app │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── clock.component │ │ │ └── clock.component.ts │ │ └── rxjs.practice.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ ├── styles.css │ ├── systemjs.config.extras.js │ ├── systemjs.config.js │ └── tsconfig.json │ └── tslint.json ├── week2 ├── 1 │ ├── README.md │ └── tasks │ │ ├── .editorconfig │ │ ├── .gitignore │ │ ├── .vscode │ │ ├── settings.json │ │ └── tasks.json │ │ ├── README.md │ │ ├── angular-cli.json │ │ ├── e2e │ │ ├── app.e2e-spec.ts │ │ ├── app.po.ts │ │ └── tsconfig.json │ │ ├── karma.conf.js │ │ ├── package.json │ │ ├── protractor.conf.js │ │ ├── src │ │ ├── app │ │ │ ├── app.component.css │ │ │ ├── app.component.html │ │ │ ├── app.component.spec.ts │ │ │ ├── app.component.ts │ │ │ ├── app.module.ts │ │ │ └── directives │ │ │ │ ├── directives.module.ts │ │ │ │ ├── grid │ │ │ │ ├── grid.component.css │ │ │ │ ├── grid.component.html │ │ │ │ ├── grid.component.spec.ts │ │ │ │ └── grid.component.ts │ │ │ │ ├── pager.directive.spec.ts │ │ │ │ └── pager.directive.ts │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── environments │ │ │ ├── environment.prod.ts │ │ │ └── environment.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.ts │ │ ├── polyfills.ts │ │ ├── styles.css │ │ ├── test.ts │ │ └── tsconfig.json │ │ └── tslint.json └── 2 │ └── README.md ├── week3 ├── 1 │ ├── README.md │ └── ngrxTodoApp │ │ ├── .editorconfig │ │ ├── .gitignore │ │ ├── .vscode │ │ └── tasks.json │ │ ├── README.md │ │ ├── angular-cli.json │ │ ├── e2e │ │ ├── app.e2e-spec.ts │ │ ├── app.po.ts │ │ └── tsconfig.json │ │ ├── karma.conf.js │ │ ├── package.json │ │ ├── protractor.conf.js │ │ ├── src │ │ ├── app │ │ │ ├── app.component.css │ │ │ ├── app.component.html │ │ │ ├── app.component.spec.ts │ │ │ ├── app.component.ts │ │ │ ├── app.module.ts │ │ │ ├── filter-state.enum.ts │ │ │ ├── store │ │ │ │ ├── app.actions.ts │ │ │ │ ├── app.model.ts │ │ │ │ ├── app.reducer.ts │ │ │ │ ├── index.ts │ │ │ │ └── reducer-factory.ts │ │ │ ├── todo-edit │ │ │ │ ├── todo-edit.component.css │ │ │ │ ├── todo-edit.component.html │ │ │ │ ├── todo-edit.component.spec.ts │ │ │ │ └── todo-edit.component.ts │ │ │ ├── todo-filter │ │ │ │ ├── todo-filter.component.css │ │ │ │ ├── todo-filter.component.html │ │ │ │ ├── todo-filter.component.spec.ts │ │ │ │ └── todo-filter.component.ts │ │ │ ├── todo-list │ │ │ │ ├── todo-list.component.css │ │ │ │ ├── todo-list.component.html │ │ │ │ ├── todo-list.component.spec.ts │ │ │ │ └── todo-list.component.ts │ │ │ └── todo.model.ts │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── environments │ │ │ ├── environment.prod.ts │ │ │ └── environment.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.ts │ │ ├── polyfills.ts │ │ ├── styles.css │ │ ├── test.ts │ │ └── tsconfig.json │ │ └── tslint.json └── 2 │ └── README.md ├── week4 ├── 1 │ └── README.md └── 2 │ └── README.md └── week5 ├── 1 └── README.md └── 2 └── README.md /README.md: -------------------------------------------------------------------------------- 1 | # Angular 2 Course, Volume 2.0 2 | 3 | ### The second edition of the Angular 2 course in HackBulgaria. 4 | 5 | This course is NOT for beginners - JavaScript experience is required. 6 | 7 | Throughout the course we'll be learning Angular 2 fundamentals - setup, components, services, routes, deployment, etc. 8 | 9 | TypeScript will be the language of choice when we're building our applications. 10 | 11 | ## Course Program 12 | 13 | The duration of the course is 5 weeks including 10 lectures: 14 | 15 | 01. Introduction: Concepts of Angular 2. Introduction to TypeScript, Component Basics and Build-in Directives. 16 | 02. Advanced Components: Inputs, Outputs, Content Projection, ViewChildren, ContentChildren. 17 | 03. Deep dive into Directives: Dependency Injection (Providers and Injectors), TemplateRef, ViewContainerRef. 18 | 04. Change Detection and Component / Directive Lifecycle. 19 | 05. Functional Programming, Reactive Extensions for JS. Optimizing Anglar Change Detection using RxJS. 20 | 06. The Controlled State Container: ngrx/store (Reactive Redux implementation for Angular 2) and ngrx/effects. Optimizing Change Detection using ngrx. 21 | 07. Forms and validations. 22 | 08. Multi-providers. Template driven custom validators and Custom Controls. 23 | 09. Routing and Navigation. 24 | 10. Protecting Routes. Lazy Loading. Pipes. Angular 4 and beyond. 25 | 26 | You can apply here: https://hackbulgaria.com/courses/angular2-volume2/ 27 | -------------------------------------------------------------------------------- /week1/1/README.md: -------------------------------------------------------------------------------- 1 | # Week 1.1 2 | ### Lecture 3 | [Click here](https://speakerdeck.com/iliaidakiev/1-ng-introduction) 4 | ### Tasks 5 | 1. Install node & npm 6 | 2. Install git 7 | 3. Install Visual Stuido Code, TSLint & Trailing Space Plugins (Optional) 8 | 4. Clone [quickstart repo](http://github.com/angular/quickstart) 9 | 5. Todos app: Create at least two (presentational) components - one for listing the todos and another for adding them. Use angular bindings to share state between the components. Create functionality for completing and deleting a todo. By default list component should show only the uncompleted todos. Create a filter that provides a way to manipulate the list by showing only the completed and showing all - completed and uncompleted (use styling to show the active filter state). Don't forget to separate code into different files and make good use of TypeScript. -------------------------------------------------------------------------------- /week1/1/todoApp/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | 12 | [*.md] 13 | max_line_length = 0 14 | trim_trailing_whitespace = false 15 | 16 | # Indentation override 17 | #[lib/**.js] 18 | #[{package.json,.travis.yml}] 19 | #[**/**.js] 20 | -------------------------------------------------------------------------------- /week1/1/todoApp/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | jspm_packages 4 | npm-debug.log 5 | debug.log 6 | src/**/*.js 7 | !src/systemjs.config.extras.js 8 | !src/systemjs.config.js 9 | *.js.map 10 | e2e/**/*.js 11 | e2e/**/*.js.map 12 | _test-output 13 | _temp 14 | -------------------------------------------------------------------------------- /week1/1/todoApp/.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: required 3 | language: node_js 4 | node_js: 5 | - "5" 6 | os: 7 | - linux 8 | env: 9 | global: 10 | - DBUS_SESSION_BUS_ADDRESS=/dev/null 11 | - DISPLAY=:99.0 12 | - CHROME_BIN=chromium-browser 13 | before_script: 14 | - sh -e /etc/init.d/xvfb start 15 | install: 16 | - npm install 17 | script: 18 | - npm run lint 19 | - npm run test:once 20 | - npm run e2e 21 | -------------------------------------------------------------------------------- /week1/1/todoApp/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible Node.js debug attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "program": "${workspaceRoot}/serve\"", 12 | "cwd": "${workspaceRoot}", 13 | "outFiles": [] 14 | }, 15 | { 16 | "type": "node", 17 | "request": "attach", 18 | "name": "Attach to Process", 19 | "port": 5858, 20 | "outFiles": [] 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /week1/1/todoApp/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "0.1.0", 5 | "command": "echo", 6 | "isShellCommand": true, 7 | "args": ["Hello World"], 8 | "showOutput": "never" 9 | } -------------------------------------------------------------------------------- /week1/1/todoApp/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Angular Documentation QuickStart Changelog 2 | Upgraders: for a fresh start, consider running these commands 3 | * `git clean -xdf` 4 | * `npm install` 5 | 6 | 7 | # 0.2.22 (2017-01-05) 8 | * Add `non-essential-files.txt` and instructions to use it to README 9 | 10 | 11 | # 0.2.21 (2016-12-14) 12 | * Update to in-memory-web-api v.0.2.1 13 | 14 | 15 | # 0.2.20 (2016-12-07) 16 | * Update to Angular 2.3.0 17 | 18 | 19 | # 0.2.19 (2016-11-30) 20 | * remove upgrade mappings from `systemjs.config.js` PR #301 21 | 22 | 23 | # 0.2.18 (2016-11-30) 24 | * remove `exclude` clause from `tsconfig.json`; it was just confusing people 25 | * karma.config + karma-test-shim can handle multiple spec source paths (issue #294) 26 | * cosmetic `app.component.spec.ts` changes 27 | * cosmetic `karma.config.js` changes 28 | 29 | 30 | # 0.2.17 (2016-11-16) 31 | * Conform to updated QuickStart advice 32 | * removed docker everywhere (was nice but not necessary) 33 | * removed wallaby 34 | * shrink styles.css 35 | * refine tsconfig.json 36 | * `AppComponent` uses interpolation 37 | 38 | 39 | # 0.2.16 (2016-11-14) 40 | * Update to Angular 2.2.0 41 | 42 | 43 | # 0.2.15 (2016-10-29) 44 | * Revert to Jasmine 2.4.1 because bug in 2.5.x (see [jasmine issue #1231](https://github.com/jasmine/jasmine/issues/1231)) 45 | 46 | 47 | # 0.2.14 (2016-10-29) 48 | * Remove bootstrap.css install 49 | * Angular v2.1.2 50 | 51 | 52 | # 0.2.13 (2016-10-20) 53 | * Protractor 4 54 | * Move from `typings` to `@types`. See `tsconfig.json` changes. 55 | * Angular v2.1.1 56 | 57 | 58 | # 0.2.12 (2016-10-06) 59 | * Angular v2.1.0 60 | 61 | 62 | # 0.2.11 (2016-10-06) 63 | * Angular v2.0.2 64 | * License is MIT 65 | * Current testing configuration 66 | * No code changes 67 | 68 | 69 | # 0.2.10 (2016-09-19) 70 | * All "Angular 2" references become just "Angular" 71 | * No code changes 72 | 73 | 74 | # 0.2.9 (2016-09-14) 75 | * Angular 2.0.0 version 76 | * Update to Typescript 2.0.2 77 | * Fix e2e test missing dir 78 | 79 | 80 | # 0.2.8 (2016-09-01) 81 | * remove @angular test libraries from system.js (now in shim) 82 | * update test related files 83 | * wallaby doesn't completely work. Researching. 84 | 85 | 86 | # 0.2.7 (2016-08-31) 87 | * Angular 2 RC6 version 88 | * Updated new forms, router, angular2-in-memory-web-api, karma, core-js, rxjs and zone.js packages 89 | * Removed router-deprecated package 90 | * Updated karma.conf.js and systemjs.config.js 91 | 92 | 93 | # 0.2.6 (2016-08-09) 94 | * Angular 2 RC5 version 95 | * Updated new forms, router and angular2-in-memory-web-api 96 | 97 | 98 | # 0.2.5 (2016-06-30) 99 | * Angular 2 RC4 version 100 | * Updated new forms and router 101 | 102 | 103 | # 0.2.4 (2016-06-21) 104 | * Angular 2 RC3 version 105 | * Add new forms and router 106 | * Add support for TS e2e tests 107 | 108 | 109 | # 0.2.3 (2016-06-15) 110 | * Angular 2 RC2 version 111 | 112 | 113 | # 0.2.2 (2016-05-21) 114 | * Update to Typings 1.x 115 | 116 | 117 | # 0.2.1 (2016-05-03) 118 | * Angular 2 RC01 version 119 | 120 | 121 | # 0.2.0 (2016-05-02) 122 | * Angular 2 RC0 version 123 | 124 | 125 | # 0.1.17 (2016-04-29) 126 | * update packages 127 | * Angular 2 beta 17 128 | * RxJS 5.0.0-beta.6 129 | * a2-in-memory-web-api 0.1.17 130 | 131 | 132 | # 0.1.16 (2016-04-26) 133 | * update packages 134 | * Angular 2 beta 16 135 | * a2-in-memory-web-api 0.1.6 136 | * protractor 3.3.0 137 | * typings 0.8.1 138 | * zone.js 0.6.12 139 | 140 | * added favicon.ico 141 | 142 | * testing 143 | - updated wallaby.js and karma.conf.js 144 | - updated app.component.spec.ts 145 | 146 | 147 | 148 | # 0.1.15 (2016-04-13) 149 | * Add testing support 150 | * npm scripts 151 | * karma/jasmine 152 | * protractor 153 | 154 | * update packages 155 | * Angular 2 beta 15 156 | * lite-server 2.2.0 157 | * systemjs 0.19.26 158 | * typescript 1.8.10 159 | * typings 0.7.12 160 | 161 | * add run packages 162 | * a2-in-memory-web-api 163 | 164 | * add testing dev-dependency packages 165 | * canonical-path: 0.0.2, 166 | * http-server: ^0.9.0, 167 | * jasmine-core: ~2.4.1, 168 | * karma: ^0.13.22, 169 | * karma-chrome-launcher: ^0.2.3, 170 | * karma-cli: ^0.1.2, 171 | * karma-htmlfile-reporter: ^0.2.2, 172 | * karma-jasmine: ^0.3.8, 173 | * protractor: ^3.2.2, 174 | * rimraf: ^2.5.2 175 | 176 | 177 | # 0.1.14 (2016-04-07) 178 | * update packages 179 | * Angular 2 beta 14 180 | * lite-server 2.2.0 181 | * typings 0.7.12 182 | 183 | 184 | # 0.1.13 (2016-03-31) 185 | * update packages 186 | * Angular 2 beta 13 187 | 188 | 189 | # 0.1.12 (2016-03-23) 190 | * update packages 191 | * Angular 2 beta 12 192 | * zones 0.6.6 193 | * remove es6-promise because no longer needed. 194 | 195 | 196 | # 0.1.11 (2016-03-18) 197 | * update packages 198 | * Angular 2 beta 11 199 | * zones 0.6.4 200 | * typescript 1.8.9 201 | * typings 0.7.9 202 | -------------------------------------------------------------------------------- /week1/1/todoApp/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2014-2016 Google, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /week1/1/todoApp/README.md: -------------------------------------------------------------------------------- 1 | # Very Simple Todo App 2 | 3 | * add todos 4 | * click on the todo to set it as complete or uncomplete 5 | * use filter to filter out todos 6 | 7 | # This app is build with angular QuickStart. QuickStart info bellow: 8 | 9 | # Angular QuickStart Source 10 | [![Build Status][travis-badge]][travis-badge-url] 11 | 12 | This repository holds the TypeScript source code of the [angular.io quickstart](https://angular.io/docs/ts/latest/quickstart.html), 13 | the foundation for most of the documentation samples and potentially a good starting point for your application. 14 | 15 | It's been extended with testing support so you can start writing tests immediately. 16 | 17 | **This is not the perfect arrangement for your application. It is not designed for production. 18 | It exists primarily to get you started quickly with learning and prototyping in Angular** 19 | 20 | We are unlikely to accept suggestions about how to grow this QuickStart into something it is not. 21 | Please keep that in mind before posting issues and PRs. 22 | 23 | ## Updating to a newer version of the Quickstart Repo 24 | 25 | From time to time the QuickStart will add be enhanced with support for new features or to reflect 26 | changes to the [official Style Guide](https://angular.io/docs/ts/latest/guide/style-guide.html). 27 | 28 | You can update your existing project to an up-to-date QuickStart by following these instructions: 29 | - Create a new project using the [instructions below](#create-a-new-project-based-on-the-quickstart) 30 | - Copy the code you have in your project's `main.ts` file onto `src/app/main.ts` in the new project 31 | - Copy your old `app` folder into `src/app` 32 | - Delete `src/app/main.ts` if you have one (we now use `src/main.ts` instead) 33 | - Copy your old `index.html`, `styles.css` and `tsconfig.json` into `src/` 34 | - Install all your third party dependencies 35 | - Copy your old `e2e/` folder into `e2e/` 36 | - Copy over any other files you added to your project 37 | - Copy your old `.git` folder into your new project's root 38 | 39 | Now you can continue working on the new project. 40 | 41 | ## Prerequisites 42 | 43 | Node.js and npm are essential to Angular development. 44 | 45 | 46 | Get it now if it's not already installed on your machine. 47 | 48 | **Verify that you are running at least node `v4.x.x` and npm `3.x.x`** 49 | by running `node -v` and `npm -v` in a terminal/console window. 50 | Older versions produce errors. 51 | 52 | We recommend [nvm](https://github.com/creationix/nvm) for managing multiple versions of node and npm. 53 | 54 | ## Create a new project based on the QuickStart 55 | 56 | Clone this repo into new project folder (e.g., `my-proj`). 57 | ```shell 58 | git clone https://github.com/angular/quickstart my-proj 59 | cd my-proj 60 | ``` 61 | 62 | We have no intention of updating the source on `angular/quickstart`. 63 | Discard the `.git` folder.. 64 | ```shell 65 | rm -rf .git # OS/X (bash) 66 | rd .git /S/Q # windows 67 | ``` 68 | ### Delete _non-essential_ files (optional) 69 | 70 | You can quickly delete the _non-essential_ files that concern testing and QuickStart repository maintenance 71 | (***including all git-related artifacts*** such as the `.git` folder and `.gitignore`!) 72 | by entering the following commands while in the project folder: 73 | 74 | ##### OS/X (bash) 75 | ```shell 76 | xargs -a non-essential-files.txt rm -rf 77 | rm app/*.spec*.ts 78 | rm non-essential-files.txt 79 | ``` 80 | 81 | ##### Windows 82 | ```shell 83 | for /f %i in (non-essential-files.txt) do del %i /F /S /Q 84 | rd .git /s /q 85 | rd e2e /s /q 86 | ``` 87 | 88 | ### Create a new git repo 89 | You could [start writing code](#start-development) now and throw it all away when you're done. 90 | If you'd rather preserve your work under source control, consider taking the following steps. 91 | 92 | Initialize this project as a *local git repo* and make the first commit: 93 | ```shell 94 | git init 95 | git add . 96 | git commit -m "Initial commit" 97 | ``` 98 | 99 | >Recover the deleted `.gitignore` from the QuickStart repository 100 | if you lost it in the _Delete non-essential files_ step. 101 | 102 | Create a *remote repository* for this project on the service of your choice. 103 | 104 | Grab its address (e.g. *`https://github.com//my-proj.git`*) and push the *local repo* to the *remote*. 105 | ```shell 106 | git remote add origin 107 | git push -u origin master 108 | ``` 109 | ## Install npm packages 110 | 111 | > See npm and nvm version notes above 112 | 113 | Install the npm packages described in the `package.json` and verify that it works: 114 | 115 | ```shell 116 | npm install 117 | npm start 118 | ``` 119 | 120 | >Doesn't work in _Bash for Windows_ which does not support servers as of January, 2017. 121 | 122 | The `npm start` command first compiles the application, 123 | then simultaneously re-compiles and runs the `lite-server`. 124 | Both the compiler and the server watch for file changes. 125 | 126 | Shut it down manually with `Ctrl-C`. 127 | 128 | You're ready to write your application. 129 | 130 | ### npm scripts 131 | 132 | We've captured many of the most useful commands in npm scripts defined in the `package.json`: 133 | 134 | * `npm start` - runs the compiler and a server at the same time, both in "watch mode". 135 | * `npm run build` - runs the TypeScript compiler once. 136 | * `npm run build:w` - runs the TypeScript compiler in watch mode; the process keeps running, awaiting changes to TypeScript files and re-compiling when it sees them. 137 | * `npm run serve` - runs the [lite-server](https://www.npmjs.com/package/lite-server), a light-weight, static file server, written and maintained by 138 | [John Papa](https://github.com/johnpapa) and 139 | [Christopher Martin](https://github.com/cgmartin) 140 | with excellent support for Angular apps that use routing. 141 | 142 | Here are the test related scripts: 143 | * `npm test` - compiles, runs and watches the karma unit tests 144 | * `npm run e2e` - compiles and run protractor e2e tests, written in Typescript (*e2e-spec.ts) 145 | 146 | ## Testing 147 | 148 | The QuickStart documentation doesn't discuss testing. 149 | This repo adds both karma/jasmine unit test and protractor end-to-end testing support. 150 | 151 | These tools are configured for specific conventions described below. 152 | 153 | *It is unwise and rarely possible to run the application, the unit tests, and the e2e tests at the same time. 154 | We recommend that you shut down one before starting another.* 155 | 156 | ### Unit Tests 157 | TypeScript unit-tests are usually in the `src/app` folder. Their filenames must end in `.spec.ts`. 158 | 159 | Look for the example `src/app/app.component.spec.ts`. 160 | Add more `.spec.ts` files as you wish; we configured karma to find them. 161 | 162 | Run it with `npm test` 163 | 164 | That command first compiles the application, then simultaneously re-compiles and runs the karma test-runner. 165 | Both the compiler and the karma watch for (different) file changes. 166 | 167 | Shut it down manually with `Ctrl-C`. 168 | 169 | Test-runner output appears in the terminal window. 170 | We can update our app and our tests in real-time, keeping a weather eye on the console for broken tests. 171 | Karma is occasionally confused and it is often necessary to shut down its browser or even shut the command down (`Ctrl-C`) and 172 | restart it. No worries; it's pretty quick. 173 | 174 | ### End-to-end (E2E) Tests 175 | 176 | E2E tests are in the `e2e` directory, side by side with the `src` folder. 177 | Their filenames must end in `.e2e-spec.ts`. 178 | 179 | Look for the example `e2e/app.e2e-spec.ts`. 180 | Add more `.e2e-spec.js` files as you wish (although one usually suffices for small projects); 181 | we configured Protractor to find them. 182 | 183 | Thereafter, run them with `npm run e2e`. 184 | 185 | That command first compiles, then simultaneously starts the `lite-server` at `localhost:8080` 186 | and launches Protractor. 187 | 188 | The pass/fail test results appear at the bottom of the terminal window. 189 | A custom reporter (see `protractor.config.js`) generates a `./_test-output/protractor-results.txt` file 190 | which is easier to read; this file is excluded from source control. 191 | 192 | Shut it down manually with `Ctrl-C`. 193 | 194 | [travis-badge]: https://travis-ci.org/angular/quickstart.svg?branch=master 195 | [travis-badge-url]: https://travis-ci.org/angular/quickstart 196 | -------------------------------------------------------------------------------- /week1/1/todoApp/bs-config.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "open": false, 3 | "logLevel": "silent", 4 | "port": 8080, 5 | "server": { 6 | "baseDir": "src", 7 | "routes": { 8 | "/node_modules": "node_modules" 9 | }, 10 | "middleware": { 11 | "0": null 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /week1/1/todoApp/bs-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": { 3 | "baseDir": "src", 4 | "routes": { 5 | "/node_modules": "node_modules" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /week1/1/todoApp/e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { browser, element, by } from 'protractor'; 2 | 3 | describe('QuickStart E2E Tests', function () { 4 | 5 | let expectedMsg = 'Hello Angular'; 6 | 7 | beforeEach(function () { 8 | browser.get(''); 9 | }); 10 | 11 | it('should display: ' + expectedMsg, function () { 12 | expect(element(by.css('h1')).getText()).toEqual(expectedMsg); 13 | }); 14 | 15 | }); 16 | -------------------------------------------------------------------------------- /week1/1/todoApp/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "sourceMap": true, 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true, 9 | "lib": [ "es2015", "dom" ], 10 | "noImplicitAny": true, 11 | "suppressImplicitAnyIndexErrors": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /week1/1/todoApp/karma-test-shim.js: -------------------------------------------------------------------------------- 1 | // /*global jasmine, __karma__, window*/ 2 | Error.stackTraceLimit = 0; // "No stacktrace"" is usually best for app testing. 3 | 4 | // Uncomment to get full stacktrace output. Sometimes helpful, usually not. 5 | // Error.stackTraceLimit = Infinity; // 6 | 7 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000; 8 | 9 | // builtPaths: root paths for output ("built") files 10 | // get from karma.config.js, then prefix with '/base/' (default is 'src/') 11 | var builtPaths = (__karma__.config.builtPaths || ['src/']) 12 | .map(function(p) { return '/base/'+p;}); 13 | 14 | __karma__.loaded = function () { }; 15 | 16 | function isJsFile(path) { 17 | return path.slice(-3) == '.js'; 18 | } 19 | 20 | function isSpecFile(path) { 21 | return /\.spec\.(.*\.)?js$/.test(path); 22 | } 23 | 24 | // Is a "built" file if is JavaScript file in one of the "built" folders 25 | function isBuiltFile(path) { 26 | return isJsFile(path) && 27 | builtPaths.reduce(function(keep, bp) { 28 | return keep || (path.substr(0, bp.length) === bp); 29 | }, false); 30 | } 31 | 32 | var allSpecFiles = Object.keys(window.__karma__.files) 33 | .filter(isSpecFile) 34 | .filter(isBuiltFile); 35 | 36 | System.config({ 37 | // Base URL for System.js calls. 'base/' is where Karma serves files from. 38 | baseURL: 'base/src', 39 | // Extend usual application package list with test folder 40 | packages: { 'testing': { main: 'index.js', defaultExtension: 'js' } }, 41 | 42 | // Assume npm: is set in `paths` in systemjs.config 43 | // Map the angular testing umd bundles 44 | map: { 45 | '@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js', 46 | '@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js', 47 | '@angular/compiler/testing': 'npm:@angular/compiler/bundles/compiler-testing.umd.js', 48 | '@angular/platform-browser/testing': 'npm:@angular/platform-browser/bundles/platform-browser-testing.umd.js', 49 | '@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js', 50 | '@angular/http/testing': 'npm:@angular/http/bundles/http-testing.umd.js', 51 | '@angular/router/testing': 'npm:@angular/router/bundles/router-testing.umd.js', 52 | '@angular/forms/testing': 'npm:@angular/forms/bundles/forms-testing.umd.js', 53 | }, 54 | }); 55 | 56 | System.import('systemjs.config.js') 57 | .then(importSystemJsExtras) 58 | .then(initTestBed) 59 | .then(initTesting); 60 | 61 | /** Optional SystemJS configuration extras. Keep going w/o it */ 62 | function importSystemJsExtras(){ 63 | return System.import('systemjs.config.extras.js') 64 | .catch(function(reason) { 65 | console.log( 66 | 'Warning: System.import could not load the optional "systemjs.config.extras.js". Did you omit it by accident? Continuing without it.' 67 | ); 68 | console.log(reason); 69 | }); 70 | } 71 | 72 | function initTestBed(){ 73 | return Promise.all([ 74 | System.import('@angular/core/testing'), 75 | System.import('@angular/platform-browser-dynamic/testing') 76 | ]) 77 | 78 | .then(function (providers) { 79 | var coreTesting = providers[0]; 80 | var browserTesting = providers[1]; 81 | 82 | coreTesting.TestBed.initTestEnvironment( 83 | browserTesting.BrowserDynamicTestingModule, 84 | browserTesting.platformBrowserDynamicTesting()); 85 | }) 86 | } 87 | 88 | // Import all spec files and start karma 89 | function initTesting () { 90 | return Promise.all( 91 | allSpecFiles.map(function (moduleName) { 92 | return System.import(moduleName); 93 | }) 94 | ) 95 | .then(__karma__.start, __karma__.error); 96 | } 97 | -------------------------------------------------------------------------------- /week1/1/todoApp/karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | 3 | var appBase = 'src/'; // transpiled app JS and map files 4 | var appSrcBase = appBase; // app source TS files 5 | 6 | // Testing helpers (optional) are conventionally in a folder called `testing` 7 | var testingBase = 'testing/'; // transpiled test JS and map files 8 | var testingSrcBase = 'testing/'; // test source TS files 9 | 10 | config.set({ 11 | basePath: '', 12 | frameworks: ['jasmine'], 13 | 14 | plugins: [ 15 | require('karma-jasmine'), 16 | require('karma-chrome-launcher'), 17 | require('karma-jasmine-html-reporter') 18 | ], 19 | 20 | client: { 21 | builtPaths: [appBase, testingBase], // add more spec base paths as needed 22 | clearContext: false // leave Jasmine Spec Runner output visible in browser 23 | }, 24 | 25 | customLaunchers: { 26 | // From the CLI. Not used here but interesting 27 | // chrome setup for travis CI using chromium 28 | Chrome_travis_ci: { 29 | base: 'Chrome', 30 | flags: ['--no-sandbox'] 31 | } 32 | }, 33 | 34 | files: [ 35 | // System.js for module loading 36 | 'node_modules/systemjs/dist/system.src.js', 37 | 38 | // Polyfills 39 | 'node_modules/core-js/client/shim.js', 40 | 41 | // zone.js 42 | 'node_modules/zone.js/dist/zone.js', 43 | 'node_modules/zone.js/dist/long-stack-trace-zone.js', 44 | 'node_modules/zone.js/dist/proxy.js', 45 | 'node_modules/zone.js/dist/sync-test.js', 46 | 'node_modules/zone.js/dist/jasmine-patch.js', 47 | 'node_modules/zone.js/dist/async-test.js', 48 | 'node_modules/zone.js/dist/fake-async-test.js', 49 | 50 | // RxJs 51 | { pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false }, 52 | { pattern: 'node_modules/rxjs/**/*.js.map', included: false, watched: false }, 53 | 54 | // Paths loaded via module imports: 55 | // Angular itself 56 | { pattern: 'node_modules/@angular/**/*.js', included: false, watched: false }, 57 | { pattern: 'node_modules/@angular/**/*.js.map', included: false, watched: false }, 58 | 59 | { pattern: appBase + '/systemjs.config.js', included: false, watched: false }, 60 | { pattern: appBase + '/systemjs.config.extras.js', included: false, watched: false }, 61 | 'karma-test-shim.js', // optionally extend SystemJS mapping e.g., with barrels 62 | 63 | // transpiled application & spec code paths loaded via module imports 64 | { pattern: appBase + '**/*.js', included: false, watched: true }, 65 | { pattern: testingBase + '**/*.js', included: false, watched: true }, 66 | 67 | 68 | // Asset (HTML & CSS) paths loaded via Angular's component compiler 69 | // (these paths need to be rewritten, see proxies section) 70 | { pattern: appBase + '**/*.html', included: false, watched: true }, 71 | { pattern: appBase + '**/*.css', included: false, watched: true }, 72 | 73 | // Paths for debugging with source maps in dev tools 74 | { pattern: appBase + '**/*.ts', included: false, watched: false }, 75 | { pattern: appBase + '**/*.js.map', included: false, watched: false }, 76 | { pattern: testingSrcBase + '**/*.ts', included: false, watched: false }, 77 | { pattern: testingBase + '**/*.js.map', included: false, watched: false} 78 | ], 79 | 80 | // Proxied base paths for loading assets 81 | proxies: { 82 | // required for modules fetched by SystemJS 83 | '/base/src/node_modules/': '/base/node_modules/' 84 | }, 85 | 86 | exclude: [], 87 | preprocessors: {}, 88 | reporters: ['progress', 'kjhtml'], 89 | 90 | port: 9876, 91 | colors: true, 92 | logLevel: config.LOG_INFO, 93 | autoWatch: true, 94 | browsers: ['Chrome'], 95 | singleRun: false 96 | }) 97 | } 98 | -------------------------------------------------------------------------------- /week1/1/todoApp/non-essential-files.txt: -------------------------------------------------------------------------------- 1 | .git 2 | .gitignore 3 | .travis.yml 4 | *.spec*.ts 5 | CHANGELOG.md 6 | e2e 7 | favicon.ico 8 | karma.conf.js 9 | karma-test-shim.js 10 | LICENSE 11 | non-essential-files.txt 12 | protractor.config.js 13 | README.md 14 | -------------------------------------------------------------------------------- /week1/1/todoApp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-quickstart", 3 | "version": "1.0.0", 4 | "description": "QuickStart package.json from the documentation, supplemented with testing support", 5 | "scripts": { 6 | "build": "tsc -p src/", 7 | "build:watch": "tsc -p src/ -w", 8 | "build:e2e": "tsc -p e2e/", 9 | "serve": "lite-server -c=bs-config.json", 10 | "serve:e2e": "lite-server -c=bs-config.e2e.json", 11 | "prestart": "npm run build", 12 | "start": "concurrently \"npm run build:watch\" \"npm run serve\"", 13 | "pree2e": "npm run build:e2e", 14 | "e2e": "concurrently \"npm run serve:e2e\" \"npm run protractor\" --kill-others --success first", 15 | "preprotractor": "webdriver-manager update", 16 | "protractor": "protractor protractor.config.js", 17 | "pretest": "npm run build", 18 | "test": "concurrently \"npm run build:watch\" \"karma start karma.conf.js\"", 19 | "pretest:once": "npm run build", 20 | "test:once": "karma start karma.conf.js --single-run", 21 | "lint": "tslint ./src/**/*.ts -t verbose" 22 | }, 23 | "keywords": [], 24 | "author": "", 25 | "license": "MIT", 26 | "dependencies": { 27 | "@angular/common": "~2.4.0", 28 | "@angular/compiler": "~2.4.0", 29 | "@angular/core": "~2.4.0", 30 | "@angular/forms": "~2.4.0", 31 | "@angular/http": "~2.4.0", 32 | "@angular/platform-browser": "~2.4.0", 33 | "@angular/platform-browser-dynamic": "~2.4.0", 34 | "@angular/router": "~3.4.0", 35 | 36 | "angular-in-memory-web-api": "~0.2.4", 37 | "systemjs": "0.19.40", 38 | "core-js": "^2.4.1", 39 | "rxjs": "5.0.1", 40 | "zone.js": "^0.7.4" 41 | }, 42 | "devDependencies": { 43 | "concurrently": "^3.1.0", 44 | "lite-server": "^2.2.2", 45 | "typescript": "~2.0.10", 46 | 47 | "canonical-path": "0.0.2", 48 | "tslint": "^3.15.1", 49 | "lodash": "^4.16.4", 50 | "jasmine-core": "~2.4.1", 51 | "karma": "^1.3.0", 52 | "karma-chrome-launcher": "^2.0.0", 53 | "karma-cli": "^1.0.1", 54 | "karma-jasmine": "^1.0.2", 55 | "karma-jasmine-html-reporter": "^0.2.2", 56 | "protractor": "~4.0.14", 57 | "rimraf": "^2.5.4", 58 | 59 | "@types/node": "^6.0.46", 60 | "@types/jasmine": "^2.5.36" 61 | }, 62 | "repository": {} 63 | } 64 | -------------------------------------------------------------------------------- /week1/1/todoApp/protractor.config.js: -------------------------------------------------------------------------------- 1 | // FIRST TIME ONLY- run: 2 | // ./node_modules/.bin/webdriver-manager update 3 | // 4 | // Try: `npm run webdriver:update` 5 | // 6 | // AND THEN EVERYTIME ... 7 | // 1. Compile with `tsc` 8 | // 2. Make sure the test server (e.g., lite-server: localhost:8080) is running. 9 | // 3. ./node_modules/.bin/protractor protractor.config.js 10 | // 11 | // To do all steps, try: `npm run e2e` 12 | 13 | var fs = require('fs'); 14 | var path = require('canonical-path'); 15 | var _ = require('lodash'); 16 | 17 | 18 | exports.config = { 19 | directConnect: true, 20 | 21 | // Capabilities to be passed to the webdriver instance. 22 | capabilities: { 23 | 'browserName': 'chrome' 24 | }, 25 | 26 | // Framework to use. Jasmine is recommended. 27 | framework: 'jasmine', 28 | 29 | // Spec patterns are relative to this config file 30 | specs: ['**/*e2e-spec.js' ], 31 | 32 | 33 | // For angular tests 34 | useAllAngular2AppRoots: true, 35 | 36 | // Base URL for application server 37 | baseUrl: 'http://localhost:8080', 38 | 39 | // doesn't seem to work. 40 | // resultJsonOutputFile: "foo.json", 41 | 42 | onPrepare: function() { 43 | //// SpecReporter 44 | //var SpecReporter = require('jasmine-spec-reporter'); 45 | //jasmine.getEnv().addReporter(new SpecReporter({displayStacktrace: 'none'})); 46 | //// jasmine.getEnv().addReporter(new SpecReporter({displayStacktrace: 'all'})); 47 | 48 | // debugging 49 | // console.log('browser.params:' + JSON.stringify(browser.params)); 50 | jasmine.getEnv().addReporter(new Reporter( browser.params )) ; 51 | 52 | // Allow changing bootstrap mode to NG1 for upgrade tests 53 | global.setProtractorToNg1Mode = function() { 54 | browser.useAllAngular2AppRoots = false; 55 | browser.rootEl = 'body'; 56 | }; 57 | }, 58 | 59 | jasmineNodeOpts: { 60 | // defaultTimeoutInterval: 60000, 61 | defaultTimeoutInterval: 10000, 62 | showTiming: true, 63 | print: function() {} 64 | } 65 | }; 66 | 67 | // Custom reporter 68 | function Reporter(options) { 69 | var _defaultOutputFile = path.resolve(process.cwd(), './_test-output', 'protractor-results.txt'); 70 | options.outputFile = options.outputFile || _defaultOutputFile; 71 | 72 | initOutputFile(options.outputFile); 73 | options.appDir = options.appDir || './'; 74 | var _root = { appDir: options.appDir, suites: [] }; 75 | log('AppDir: ' + options.appDir, +1); 76 | var _currentSuite; 77 | 78 | this.suiteStarted = function(suite) { 79 | _currentSuite = { description: suite.description, status: null, specs: [] }; 80 | _root.suites.push(_currentSuite); 81 | log('Suite: ' + suite.description, +1); 82 | }; 83 | 84 | this.suiteDone = function(suite) { 85 | var statuses = _currentSuite.specs.map(function(spec) { 86 | return spec.status; 87 | }); 88 | statuses = _.uniq(statuses); 89 | var status = statuses.indexOf('failed') >= 0 ? 'failed' : statuses.join(', '); 90 | _currentSuite.status = status; 91 | log('Suite ' + _currentSuite.status + ': ' + suite.description, -1); 92 | }; 93 | 94 | this.specStarted = function(spec) { 95 | 96 | }; 97 | 98 | this.specDone = function(spec) { 99 | var currentSpec = { 100 | description: spec.description, 101 | status: spec.status 102 | }; 103 | if (spec.failedExpectations.length > 0) { 104 | currentSpec.failedExpectations = spec.failedExpectations; 105 | } 106 | 107 | _currentSuite.specs.push(currentSpec); 108 | log(spec.status + ' - ' + spec.description); 109 | }; 110 | 111 | this.jasmineDone = function() { 112 | outputFile = options.outputFile; 113 | //// Alternate approach - just stringify the _root - not as pretty 114 | //// but might be more useful for automation. 115 | // var output = JSON.stringify(_root, null, 2); 116 | var output = formatOutput(_root); 117 | fs.appendFileSync(outputFile, output); 118 | }; 119 | 120 | function ensureDirectoryExistence(filePath) { 121 | var dirname = path.dirname(filePath); 122 | if (directoryExists(dirname)) { 123 | return true; 124 | } 125 | ensureDirectoryExistence(dirname); 126 | fs.mkdirSync(dirname); 127 | } 128 | 129 | function directoryExists(path) { 130 | try { 131 | return fs.statSync(path).isDirectory(); 132 | } 133 | catch (err) { 134 | return false; 135 | } 136 | } 137 | 138 | function initOutputFile(outputFile) { 139 | ensureDirectoryExistence(outputFile); 140 | var header = "Protractor results for: " + (new Date()).toLocaleString() + "\n\n"; 141 | fs.writeFileSync(outputFile, header); 142 | } 143 | 144 | // for output file output 145 | function formatOutput(output) { 146 | var indent = ' '; 147 | var pad = ' '; 148 | var results = []; 149 | results.push('AppDir:' + output.appDir); 150 | output.suites.forEach(function(suite) { 151 | results.push(pad + 'Suite: ' + suite.description + ' -- ' + suite.status); 152 | pad+=indent; 153 | suite.specs.forEach(function(spec) { 154 | results.push(pad + spec.status + ' - ' + spec.description); 155 | if (spec.failedExpectations) { 156 | pad+=indent; 157 | spec.failedExpectations.forEach(function (fe) { 158 | results.push(pad + 'message: ' + fe.message); 159 | }); 160 | pad=pad.substr(2); 161 | } 162 | }); 163 | pad = pad.substr(2); 164 | results.push(''); 165 | }); 166 | results.push(''); 167 | return results.join('\n'); 168 | } 169 | 170 | // for console output 171 | var _pad; 172 | function log(str, indent) { 173 | _pad = _pad || ''; 174 | if (indent == -1) { 175 | _pad = _pad.substr(2); 176 | } 177 | console.log(_pad + str); 178 | if (indent == 1) { 179 | _pad = _pad + ' '; 180 | } 181 | } 182 | 183 | } 184 | -------------------------------------------------------------------------------- /week1/1/todoApp/src/app/add.component/add.component.css: -------------------------------------------------------------------------------- 1 | #add-todo { 2 | padding: 20px; 3 | } 4 | 5 | #add-todo * { 6 | display: inline-block; 7 | } -------------------------------------------------------------------------------- /week1/1/todoApp/src/app/add.component/add.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
-------------------------------------------------------------------------------- /week1/1/todoApp/src/app/add.component/add.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Output, EventEmitter } from '@angular/core'; 2 | import { Todo } from '../todo.model'; 3 | 4 | @Component({ 5 | selector: 'add-todo', 6 | templateUrl: './app/add.component/add.component.html', 7 | styleUrls: ['./app/add.component/add.component.css'], 8 | }) 9 | export class AddComponent { 10 | @Output() newTodo: EventEmitter = new EventEmitter(); 11 | 12 | add(text: string) { 13 | this.newTodo.emit(new Todo(text)); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /week1/1/todoApp/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { AppComponent } from './app.component'; 2 | 3 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 4 | import { By } from '@angular/platform-browser'; 5 | import { DebugElement } from '@angular/core'; 6 | 7 | describe('AppComponent', function () { 8 | let de: DebugElement; 9 | let comp: AppComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async(() => { 13 | TestBed.configureTestingModule({ 14 | declarations: [ AppComponent ] 15 | }) 16 | .compileComponents(); 17 | })); 18 | 19 | beforeEach(() => { 20 | fixture = TestBed.createComponent(AppComponent); 21 | comp = fixture.componentInstance; 22 | de = fixture.debugElement.query(By.css('h1')); 23 | }); 24 | 25 | it('should create component', () => expect(comp).toBeDefined() ); 26 | 27 | it('should have expected

text', () => { 28 | fixture.detectChanges(); 29 | const h1 = de.nativeElement; 30 | expect(h1.innerText).toMatch(/angular/i, 31 | '

should say something about "Angular"'); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /week1/1/todoApp/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Todo } from './todo.model'; 3 | import { FilterState } from './enums/filter'; 4 | 5 | const todoFiltersMap: {[key: number]: (todo?: Todo) => boolean } = { 6 | [FilterState.All]: (): boolean => true, 7 | [FilterState.Default]: (todo: Todo): boolean => !todo.isChecked, 8 | [FilterState.CompletedOnly]: (todo: Todo): boolean => !!todo.isChecked 9 | }; 10 | 11 | @Component({ 12 | selector: 'my-app', 13 | template: ` 14 |

{{title}}

15 | 16 | 17 | 18 | `, 19 | }) 20 | export class AppComponent { 21 | title = 'TODO APP'; 22 | todos: Todo[] = []; 23 | filterState: FilterState = FilterState.Default; 24 | 25 | newTodoHandler(todo: Todo) { 26 | this.todos.push(todo); 27 | } 28 | 29 | stateChangeHandler(state: FilterState) { 30 | this.filterState = state; 31 | } 32 | 33 | private get todoList(): Todo[] { 34 | return this.todos.filter(todoFiltersMap[this.filterState]); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /week1/1/todoApp/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | 4 | import { AppComponent } from './app.component'; 5 | 6 | import { ListComponent } from './list.component/list.component'; 7 | import { AddComponent } from './add.component/add.component'; 8 | import { FilterComponent } from './filter.component/filter.component'; 9 | 10 | @NgModule({ 11 | imports: [ BrowserModule ], 12 | declarations: [ AppComponent, ListComponent, AddComponent, FilterComponent ], 13 | bootstrap: [ AppComponent ] 14 | }) 15 | export class AppModule { } 16 | -------------------------------------------------------------------------------- /week1/1/todoApp/src/app/enums/filter.ts: -------------------------------------------------------------------------------- 1 | export enum FilterState { 2 | Default, 3 | CompletedOnly, 4 | All 5 | }; 6 | -------------------------------------------------------------------------------- /week1/1/todoApp/src/app/filter.component/filter.component.css: -------------------------------------------------------------------------------- 1 | #filter { 2 | width: 100%; 3 | } 4 | 5 | #filter li { 6 | display: inline-block; 7 | margin: 5px; 8 | padding: 20px; 9 | } 10 | 11 | #filter li.active { 12 | background-color: rgba(0, 128, 0, 0.57); 13 | color: white; 14 | } -------------------------------------------------------------------------------- /week1/1/todoApp/src/app/filter.component/filter.component.html: -------------------------------------------------------------------------------- 1 |
    2 |
  • {{state}}
  • 3 |
-------------------------------------------------------------------------------- /week1/1/todoApp/src/app/filter.component/filter.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, Output, EventEmitter } from '@angular/core'; 2 | import { FilterState } from '../enums/filter'; 3 | 4 | @Component({ 5 | selector: 'filter', 6 | templateUrl: './app/filter.component/filter.component.html', 7 | styleUrls: ['./app/filter.component/filter.component.css'] 8 | }) 9 | export class FilterComponent { 10 | @Input() state: FilterState; 11 | @Output() stateChange: EventEmitter = new EventEmitter(); 12 | 13 | private states: string[] = []; 14 | 15 | constructor() { 16 | this.states = Object.keys(FilterState).filter(el => isNaN(parseInt(el, 10))); 17 | } 18 | 19 | stateChanged(stateName: string) { 20 | this.stateChange.emit(FilterState[stateName]); 21 | } 22 | 23 | private isActive(stateName: string): boolean { 24 | return FilterState[stateName] === this.state; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /week1/1/todoApp/src/app/list.component/list.component.css: -------------------------------------------------------------------------------- 1 | li.todo { 2 | color: orange; 3 | } 4 | 5 | li.todo.completed { 6 | color: green; 7 | } -------------------------------------------------------------------------------- /week1/1/todoApp/src/app/list.component/list.component.html: -------------------------------------------------------------------------------- 1 |
    2 |
  • {{item.getText()}}
  • 3 |
-------------------------------------------------------------------------------- /week1/1/todoApp/src/app/list.component/list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { Todo } from '../todo.model'; 3 | @Component({ 4 | selector: 'todo-list', 5 | templateUrl: './app/list.component/list.component.html', 6 | styleUrls: ['./app/list.component/list.component.css'] 7 | }) 8 | export class ListComponent { 9 | @Input() list: Todo[] = []; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /week1/1/todoApp/src/app/todo.model.ts: -------------------------------------------------------------------------------- 1 | export class Todo { 2 | 3 | get isChecked() { 4 | return this.checked; 5 | } 6 | 7 | private checked: boolean = false; 8 | 9 | constructor(private text: string) {} 10 | 11 | getText(): string { 12 | return this.text; 13 | } 14 | 15 | toggleChacked() { 16 | this.checked = !this.checked; 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /week1/1/todoApp/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Angular-2/c584709e30b08e9800e8629242e96672b60f7226/week1/1/todoApp/src/favicon.ico -------------------------------------------------------------------------------- /week1/1/todoApp/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Angular QuickStart 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | Loading AppComponent content here ... 24 | 25 | 26 | -------------------------------------------------------------------------------- /week1/1/todoApp/src/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | 3 | import { AppModule } from './app/app.module'; 4 | 5 | platformBrowserDynamic().bootstrapModule(AppModule); 6 | -------------------------------------------------------------------------------- /week1/1/todoApp/src/styles.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: #369; 3 | font-family: Arial, Helvetica, sans-serif; 4 | font-size: 250%; 5 | } 6 | -------------------------------------------------------------------------------- /week1/1/todoApp/src/systemjs.config.extras.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Add barrels and stuff 3 | * Adjust as necessary for your application needs. 4 | */ 5 | // (function (global) { 6 | // System.config({ 7 | // packages: { 8 | // // add packages here 9 | // } 10 | // }); 11 | // })(this); 12 | -------------------------------------------------------------------------------- /week1/1/todoApp/src/systemjs.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * System configuration for Angular samples 3 | * Adjust as necessary for your application needs. 4 | */ 5 | (function (global) { 6 | System.config({ 7 | paths: { 8 | // paths serve as alias 9 | 'npm:': 'node_modules/' 10 | }, 11 | // map tells the System loader where to look for things 12 | map: { 13 | // our app is within the app folder 14 | app: 'app', 15 | 16 | // angular bundles 17 | '@angular/core': 'npm:@angular/core/bundles/core.umd.js', 18 | '@angular/common': 'npm:@angular/common/bundles/common.umd.js', 19 | '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js', 20 | '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js', 21 | '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js', 22 | '@angular/http': 'npm:@angular/http/bundles/http.umd.js', 23 | '@angular/router': 'npm:@angular/router/bundles/router.umd.js', 24 | // '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js', 25 | 26 | // other libraries 27 | 'rxjs': 'npm:rxjs', 28 | 'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js' 29 | }, 30 | // packages tells the System loader how to load when no filename and/or no extension 31 | packages: { 32 | app: { 33 | defaultExtension: 'js' 34 | }, 35 | rxjs: { 36 | defaultExtension: 'js' 37 | } 38 | } 39 | }); 40 | })(this); 41 | -------------------------------------------------------------------------------- /week1/1/todoApp/src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "sourceMap": true, 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true, 9 | "lib": [ "es2015", "dom" ], 10 | "noImplicitAny": true, 11 | "suppressImplicitAnyIndexErrors": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /week1/1/todoApp/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "class-name": true, 4 | "comment-format": [ 5 | true, 6 | "check-space" 7 | ], 8 | "curly": true, 9 | "eofline": true, 10 | "forin": true, 11 | "indent": [ 12 | true, 13 | "spaces" 14 | ], 15 | "label-position": true, 16 | "label-undefined": true, 17 | "max-line-length": [ 18 | true, 19 | 140 20 | ], 21 | "member-access": false, 22 | "member-ordering": [ 23 | true, 24 | "static-before-instance", 25 | "variables-before-functions" 26 | ], 27 | "no-arg": true, 28 | "no-bitwise": true, 29 | "no-console": [ 30 | true, 31 | "debug", 32 | "info", 33 | "time", 34 | "timeEnd", 35 | "trace" 36 | ], 37 | "no-construct": true, 38 | "no-debugger": true, 39 | "no-duplicate-key": true, 40 | "no-duplicate-variable": true, 41 | "no-empty": false, 42 | "no-eval": true, 43 | "no-inferrable-types": true, 44 | "no-shadowed-variable": true, 45 | "no-string-literal": false, 46 | "no-switch-case-fall-through": true, 47 | "no-trailing-whitespace": true, 48 | "no-unused-expression": true, 49 | "no-unused-variable": true, 50 | "no-unreachable": true, 51 | "no-use-before-declare": true, 52 | "no-var-keyword": true, 53 | "object-literal-sort-keys": false, 54 | "one-line": [ 55 | true, 56 | "check-open-brace", 57 | "check-catch", 58 | "check-else", 59 | "check-whitespace" 60 | ], 61 | "quotemark": [ 62 | true, 63 | "single" 64 | ], 65 | "radix": true, 66 | "semicolon": [ 67 | "always" 68 | ], 69 | "triple-equals": [ 70 | true, 71 | "allow-null-check" 72 | ], 73 | "typedef-whitespace": [ 74 | true, 75 | { 76 | "call-signature": "nospace", 77 | "index-signature": "nospace", 78 | "parameter": "nospace", 79 | "property-declaration": "nospace", 80 | "variable-declaration": "nospace" 81 | } 82 | ], 83 | "variable-name": false, 84 | "whitespace": [ 85 | true, 86 | "check-branch", 87 | "check-decl", 88 | "check-operator", 89 | "check-separator", 90 | "check-type" 91 | ] 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /week1/2/README.md: -------------------------------------------------------------------------------- 1 | # Week 1.2 2 | 3 | # Lecture: 4 | Click [here](https://speakerdeck.com/iliaidakiev/2-functional-programming-and-rxjs) 5 | 6 | # Tasks 7 | 8 | ## Using RxJS solve the tasks bellow (use pure functions and separate functionality): 9 | Use this [link](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/categories.md) if you need help. 10 | --- 11 | 12 | ### Part 1. 13 | 1.1.Sum up all the elements from the array bellow: 14 | ``` 15 | const numbers = [10,64,12,32]; 16 | ``` 17 | 18 | 1.2.Extract the odd numbers from the array bellow: 19 | ``` 20 | const numbers = [3,7,5,2,8,1]; 21 | ``` 22 | 23 | 1.3.From the given array extract all the names and ages to separate arrays: 24 | ``` 25 | const users = [{ 26 | name: 'Ivan', 27 | age: 18 28 | }, { 29 | name: 'Petar', 30 | age: 25 31 | }, { 32 | name: 'Alex', 33 | age: 10 34 | }] 35 | ``` 36 | 37 | 1.4.Create a component clock that gets the current time from the browser on initialization and creates an Observable. Subscribe to the observable and set the value to a local property. Every minute this proeperty needs to be changed to represent the real time on our page. 38 | 39 | --- 40 | 41 | ### Part 2. 42 | 2.1.We have a dummy method post in our main component that looks like: 43 | ``` 44 | get(url, callback) { 45 | console.log(`fake get request to ${url}`); 46 | const id = setTimeout(() => { 47 | console.log(`get request JSON response`) 48 | callback(null, `[{ 49 | "name": "Ivan", 50 | "age": 18 51 | }, { 52 | "name": "Petar", 53 | "age": 25 54 | }, { 55 | "name": "Alex", 56 | "age": 10 57 | }]`) 58 | }, 2000); 59 | return { 60 | cancel: () => clearInterval(id) 61 | } 62 | } 63 | 64 | ``` 65 | Using Observables wrap up this get method so we can use it like: 66 | 67 | ``` 68 | this.get('url').map(...).map(...).subscribe(...) 69 | ``` 70 | 71 | The flow should be like: get -> JSON.parse -> modify and use 1.4 to extract names and ages 72 | 73 | 2.2.We have an array of urls. Using the map operator and the modified get from the previous task, simulate getting the data from each urls. 74 | ``` 75 | const urls = ['http://domain.com', 'http://domain.bg', 'http://domain.eu'] 76 | ``` 77 | 78 | --- 79 | 80 | ### Part 3. 81 | 3.1.Use the modified dummy get method bellow and make sure you handle the error and present the error message to the user: 82 | 83 | ``` 84 | errorGet(url: string, callback: any) { 85 | console.log(`fake get request to ${url}`); 86 | if(/(.*)\.(bg)/.test(url)) callback(new Error('Cannot resolve domain .bg')); 87 | const id = setTimeout(() => { 88 | console.log(`get request JSON response`) 89 | callback(null, `[{ 90 | "name": "Ivan", 91 | "age": 18 92 | }, { 93 | "name": "Petar", 94 | "age": 25 95 | }, { 96 | "name": "Alex", 97 | "age": 10 98 | }]`) 99 | }, 2000); 100 | return { 101 | cancel: () => clearInterval(id) 102 | }; 103 | } 104 | ``` 105 | 106 | 3.2.Make sure that all the urls from the array are fetched. If there is a failed request just set the data as ``null`` (Chain isolation). Show the results to the user like so: 107 | ``` 108 | http://domain.com 109 | Ivan - 18 110 | Petar - 25 111 | Alex - 10 112 | 113 | http://domain.eu 114 | Ivan - 18 115 | Petar - 25 116 | Alex - 10 117 | ``` 118 | ### Part 4. 119 | 4.1 In this task we will simulate a socket connection. We have a method ``connect`` that takes a url and creates a (fake) connection to the given url. The returned value of calling this method is an (Hot) Observable. When we subscribe to the method we should get the same stream and not open a new conncetion. Use `interval(1500)` to simulate data coming from the server. 120 | 121 | ### Part 5. 122 | Use a ReplySubject for this task to create data caching for the socket connection from the previous task. By doing this every subscriber will receive all the data emited from the source when subscribed. Review links: [link1](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/subjects/subject.md), [link2](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/subjects/replaysubject.md). -------------------------------------------------------------------------------- /week1/2/solutionsApp/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | 12 | [*.md] 13 | max_line_length = 0 14 | trim_trailing_whitespace = false 15 | 16 | # Indentation override 17 | #[lib/**.js] 18 | #[{package.json,.travis.yml}] 19 | #[**/**.js] 20 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | jspm_packages 4 | npm-debug.log 5 | debug.log 6 | src/**/*.js 7 | !src/systemjs.config.extras.js 8 | !src/systemjs.config.js 9 | *.js.map 10 | e2e/**/*.js 11 | e2e/**/*.js.map 12 | _test-output 13 | _temp 14 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: required 3 | language: node_js 4 | node_js: 5 | - "5" 6 | os: 7 | - linux 8 | env: 9 | global: 10 | - DBUS_SESSION_BUS_ADDRESS=/dev/null 11 | - DISPLAY=:99.0 12 | - CHROME_BIN=chromium-browser 13 | before_script: 14 | - sh -e /etc/init.d/xvfb start 15 | install: 16 | - npm install 17 | script: 18 | - npm run lint 19 | - npm run test:once 20 | - npm run e2e 21 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible Node.js debug attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "program": "${workspaceRoot}/serve\"", 12 | "cwd": "${workspaceRoot}", 13 | "outFiles": [] 14 | }, 15 | { 16 | "type": "node", 17 | "request": "attach", 18 | "name": "Attach to Process", 19 | "port": 5858, 20 | "outFiles": [] 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /week1/2/solutionsApp/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "0.1.0", 5 | "command": "echo", 6 | "isShellCommand": true, 7 | "args": ["Hello World"], 8 | "showOutput": "never" 9 | } -------------------------------------------------------------------------------- /week1/2/solutionsApp/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Angular Documentation QuickStart Changelog 2 | Upgraders: for a fresh start, consider running these commands 3 | * `git clean -xdf` 4 | * `npm install` 5 | 6 | 7 | # 0.2.22 (2017-01-05) 8 | * Add `non-essential-files.txt` and instructions to use it to README 9 | 10 | 11 | # 0.2.21 (2016-12-14) 12 | * Update to in-memory-web-api v.0.2.1 13 | 14 | 15 | # 0.2.20 (2016-12-07) 16 | * Update to Angular 2.3.0 17 | 18 | 19 | # 0.2.19 (2016-11-30) 20 | * remove upgrade mappings from `systemjs.config.js` PR #301 21 | 22 | 23 | # 0.2.18 (2016-11-30) 24 | * remove `exclude` clause from `tsconfig.json`; it was just confusing people 25 | * karma.config + karma-test-shim can handle multiple spec source paths (issue #294) 26 | * cosmetic `app.component.spec.ts` changes 27 | * cosmetic `karma.config.js` changes 28 | 29 | 30 | # 0.2.17 (2016-11-16) 31 | * Conform to updated QuickStart advice 32 | * removed docker everywhere (was nice but not necessary) 33 | * removed wallaby 34 | * shrink styles.css 35 | * refine tsconfig.json 36 | * `AppComponent` uses interpolation 37 | 38 | 39 | # 0.2.16 (2016-11-14) 40 | * Update to Angular 2.2.0 41 | 42 | 43 | # 0.2.15 (2016-10-29) 44 | * Revert to Jasmine 2.4.1 because bug in 2.5.x (see [jasmine issue #1231](https://github.com/jasmine/jasmine/issues/1231)) 45 | 46 | 47 | # 0.2.14 (2016-10-29) 48 | * Remove bootstrap.css install 49 | * Angular v2.1.2 50 | 51 | 52 | # 0.2.13 (2016-10-20) 53 | * Protractor 4 54 | * Move from `typings` to `@types`. See `tsconfig.json` changes. 55 | * Angular v2.1.1 56 | 57 | 58 | # 0.2.12 (2016-10-06) 59 | * Angular v2.1.0 60 | 61 | 62 | # 0.2.11 (2016-10-06) 63 | * Angular v2.0.2 64 | * License is MIT 65 | * Current testing configuration 66 | * No code changes 67 | 68 | 69 | # 0.2.10 (2016-09-19) 70 | * All "Angular 2" references become just "Angular" 71 | * No code changes 72 | 73 | 74 | # 0.2.9 (2016-09-14) 75 | * Angular 2.0.0 version 76 | * Update to Typescript 2.0.2 77 | * Fix e2e test missing dir 78 | 79 | 80 | # 0.2.8 (2016-09-01) 81 | * remove @angular test libraries from system.js (now in shim) 82 | * update test related files 83 | * wallaby doesn't completely work. Researching. 84 | 85 | 86 | # 0.2.7 (2016-08-31) 87 | * Angular 2 RC6 version 88 | * Updated new forms, router, angular2-in-memory-web-api, karma, core-js, rxjs and zone.js packages 89 | * Removed router-deprecated package 90 | * Updated karma.conf.js and systemjs.config.js 91 | 92 | 93 | # 0.2.6 (2016-08-09) 94 | * Angular 2 RC5 version 95 | * Updated new forms, router and angular2-in-memory-web-api 96 | 97 | 98 | # 0.2.5 (2016-06-30) 99 | * Angular 2 RC4 version 100 | * Updated new forms and router 101 | 102 | 103 | # 0.2.4 (2016-06-21) 104 | * Angular 2 RC3 version 105 | * Add new forms and router 106 | * Add support for TS e2e tests 107 | 108 | 109 | # 0.2.3 (2016-06-15) 110 | * Angular 2 RC2 version 111 | 112 | 113 | # 0.2.2 (2016-05-21) 114 | * Update to Typings 1.x 115 | 116 | 117 | # 0.2.1 (2016-05-03) 118 | * Angular 2 RC01 version 119 | 120 | 121 | # 0.2.0 (2016-05-02) 122 | * Angular 2 RC0 version 123 | 124 | 125 | # 0.1.17 (2016-04-29) 126 | * update packages 127 | * Angular 2 beta 17 128 | * RxJS 5.0.0-beta.6 129 | * a2-in-memory-web-api 0.1.17 130 | 131 | 132 | # 0.1.16 (2016-04-26) 133 | * update packages 134 | * Angular 2 beta 16 135 | * a2-in-memory-web-api 0.1.6 136 | * protractor 3.3.0 137 | * typings 0.8.1 138 | * zone.js 0.6.12 139 | 140 | * added favicon.ico 141 | 142 | * testing 143 | - updated wallaby.js and karma.conf.js 144 | - updated app.component.spec.ts 145 | 146 | 147 | 148 | # 0.1.15 (2016-04-13) 149 | * Add testing support 150 | * npm scripts 151 | * karma/jasmine 152 | * protractor 153 | 154 | * update packages 155 | * Angular 2 beta 15 156 | * lite-server 2.2.0 157 | * systemjs 0.19.26 158 | * typescript 1.8.10 159 | * typings 0.7.12 160 | 161 | * add run packages 162 | * a2-in-memory-web-api 163 | 164 | * add testing dev-dependency packages 165 | * canonical-path: 0.0.2, 166 | * http-server: ^0.9.0, 167 | * jasmine-core: ~2.4.1, 168 | * karma: ^0.13.22, 169 | * karma-chrome-launcher: ^0.2.3, 170 | * karma-cli: ^0.1.2, 171 | * karma-htmlfile-reporter: ^0.2.2, 172 | * karma-jasmine: ^0.3.8, 173 | * protractor: ^3.2.2, 174 | * rimraf: ^2.5.2 175 | 176 | 177 | # 0.1.14 (2016-04-07) 178 | * update packages 179 | * Angular 2 beta 14 180 | * lite-server 2.2.0 181 | * typings 0.7.12 182 | 183 | 184 | # 0.1.13 (2016-03-31) 185 | * update packages 186 | * Angular 2 beta 13 187 | 188 | 189 | # 0.1.12 (2016-03-23) 190 | * update packages 191 | * Angular 2 beta 12 192 | * zones 0.6.6 193 | * remove es6-promise because no longer needed. 194 | 195 | 196 | # 0.1.11 (2016-03-18) 197 | * update packages 198 | * Angular 2 beta 11 199 | * zones 0.6.4 200 | * typescript 1.8.9 201 | * typings 0.7.9 202 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2014-2016 Google, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/README.md: -------------------------------------------------------------------------------- 1 | # RxJS practice 2 | 3 | ## rxjs.practice contains tasks: 4 | * 1.1 5 | * 1.2 6 | * 1.3 7 | 8 | ## clock.component is task 1.4 9 | 10 | ## app.component.ts contains taks: 11 | * 2.1 12 | * 2.2 13 | * 3.1 14 | * 3.2 15 | * 4.1 16 | * 5.1 17 | 18 | --- 19 | * `npm start` - runs the compiler and a server at the same time, both in "watch mode". 20 | * `npm run build` - runs the TypeScript compiler once. 21 | * `npm run build:w` - runs the TypeScript compiler in watch mode; the process keeps running, awaiting changes to TypeScript files and re-compiling when it sees them. 22 | * `npm run serve` - runs the [lite-server](https://www.npmjs.com/package/lite-server), a light-weight, static file server, written and maintained by -------------------------------------------------------------------------------- /week1/2/solutionsApp/bs-config.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "open": false, 3 | "logLevel": "silent", 4 | "port": 8080, 5 | "server": { 6 | "baseDir": "src", 7 | "routes": { 8 | "/node_modules": "node_modules" 9 | }, 10 | "middleware": { 11 | "0": null 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/bs-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": { 3 | "baseDir": "src", 4 | "routes": { 5 | "/node_modules": "node_modules" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { browser, element, by } from 'protractor'; 2 | 3 | describe('QuickStart E2E Tests', function () { 4 | 5 | let expectedMsg = 'Hello Angular'; 6 | 7 | beforeEach(function () { 8 | browser.get(''); 9 | }); 10 | 11 | it('should display: ' + expectedMsg, function () { 12 | expect(element(by.css('h1')).getText()).toEqual(expectedMsg); 13 | }); 14 | 15 | }); 16 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "sourceMap": true, 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true, 9 | "lib": [ "es2015", "dom" ], 10 | "noImplicitAny": true, 11 | "suppressImplicitAnyIndexErrors": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/karma-test-shim.js: -------------------------------------------------------------------------------- 1 | // /*global jasmine, __karma__, window*/ 2 | Error.stackTraceLimit = 0; // "No stacktrace"" is usually best for app testing. 3 | 4 | // Uncomment to get full stacktrace output. Sometimes helpful, usually not. 5 | // Error.stackTraceLimit = Infinity; // 6 | 7 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000; 8 | 9 | // builtPaths: root paths for output ("built") files 10 | // get from karma.config.js, then prefix with '/base/' (default is 'src/') 11 | var builtPaths = (__karma__.config.builtPaths || ['src/']) 12 | .map(function(p) { return '/base/'+p;}); 13 | 14 | __karma__.loaded = function () { }; 15 | 16 | function isJsFile(path) { 17 | return path.slice(-3) == '.js'; 18 | } 19 | 20 | function isSpecFile(path) { 21 | return /\.spec\.(.*\.)?js$/.test(path); 22 | } 23 | 24 | // Is a "built" file if is JavaScript file in one of the "built" folders 25 | function isBuiltFile(path) { 26 | return isJsFile(path) && 27 | builtPaths.reduce(function(keep, bp) { 28 | return keep || (path.substr(0, bp.length) === bp); 29 | }, false); 30 | } 31 | 32 | var allSpecFiles = Object.keys(window.__karma__.files) 33 | .filter(isSpecFile) 34 | .filter(isBuiltFile); 35 | 36 | System.config({ 37 | // Base URL for System.js calls. 'base/' is where Karma serves files from. 38 | baseURL: 'base/src', 39 | // Extend usual application package list with test folder 40 | packages: { 'testing': { main: 'index.js', defaultExtension: 'js' } }, 41 | 42 | // Assume npm: is set in `paths` in systemjs.config 43 | // Map the angular testing umd bundles 44 | map: { 45 | '@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js', 46 | '@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js', 47 | '@angular/compiler/testing': 'npm:@angular/compiler/bundles/compiler-testing.umd.js', 48 | '@angular/platform-browser/testing': 'npm:@angular/platform-browser/bundles/platform-browser-testing.umd.js', 49 | '@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js', 50 | '@angular/http/testing': 'npm:@angular/http/bundles/http-testing.umd.js', 51 | '@angular/router/testing': 'npm:@angular/router/bundles/router-testing.umd.js', 52 | '@angular/forms/testing': 'npm:@angular/forms/bundles/forms-testing.umd.js', 53 | }, 54 | }); 55 | 56 | System.import('systemjs.config.js') 57 | .then(importSystemJsExtras) 58 | .then(initTestBed) 59 | .then(initTesting); 60 | 61 | /** Optional SystemJS configuration extras. Keep going w/o it */ 62 | function importSystemJsExtras(){ 63 | return System.import('systemjs.config.extras.js') 64 | .catch(function(reason) { 65 | console.log( 66 | 'Warning: System.import could not load the optional "systemjs.config.extras.js". Did you omit it by accident? Continuing without it.' 67 | ); 68 | console.log(reason); 69 | }); 70 | } 71 | 72 | function initTestBed(){ 73 | return Promise.all([ 74 | System.import('@angular/core/testing'), 75 | System.import('@angular/platform-browser-dynamic/testing') 76 | ]) 77 | 78 | .then(function (providers) { 79 | var coreTesting = providers[0]; 80 | var browserTesting = providers[1]; 81 | 82 | coreTesting.TestBed.initTestEnvironment( 83 | browserTesting.BrowserDynamicTestingModule, 84 | browserTesting.platformBrowserDynamicTesting()); 85 | }) 86 | } 87 | 88 | // Import all spec files and start karma 89 | function initTesting () { 90 | return Promise.all( 91 | allSpecFiles.map(function (moduleName) { 92 | return System.import(moduleName); 93 | }) 94 | ) 95 | .then(__karma__.start, __karma__.error); 96 | } 97 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | 3 | var appBase = 'src/'; // transpiled app JS and map files 4 | var appSrcBase = appBase; // app source TS files 5 | 6 | // Testing helpers (optional) are conventionally in a folder called `testing` 7 | var testingBase = 'testing/'; // transpiled test JS and map files 8 | var testingSrcBase = 'testing/'; // test source TS files 9 | 10 | config.set({ 11 | basePath: '', 12 | frameworks: ['jasmine'], 13 | 14 | plugins: [ 15 | require('karma-jasmine'), 16 | require('karma-chrome-launcher'), 17 | require('karma-jasmine-html-reporter') 18 | ], 19 | 20 | client: { 21 | builtPaths: [appBase, testingBase], // add more spec base paths as needed 22 | clearContext: false // leave Jasmine Spec Runner output visible in browser 23 | }, 24 | 25 | customLaunchers: { 26 | // From the CLI. Not used here but interesting 27 | // chrome setup for travis CI using chromium 28 | Chrome_travis_ci: { 29 | base: 'Chrome', 30 | flags: ['--no-sandbox'] 31 | } 32 | }, 33 | 34 | files: [ 35 | // System.js for module loading 36 | 'node_modules/systemjs/dist/system.src.js', 37 | 38 | // Polyfills 39 | 'node_modules/core-js/client/shim.js', 40 | 41 | // zone.js 42 | 'node_modules/zone.js/dist/zone.js', 43 | 'node_modules/zone.js/dist/long-stack-trace-zone.js', 44 | 'node_modules/zone.js/dist/proxy.js', 45 | 'node_modules/zone.js/dist/sync-test.js', 46 | 'node_modules/zone.js/dist/jasmine-patch.js', 47 | 'node_modules/zone.js/dist/async-test.js', 48 | 'node_modules/zone.js/dist/fake-async-test.js', 49 | 50 | // RxJs 51 | { pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false }, 52 | { pattern: 'node_modules/rxjs/**/*.js.map', included: false, watched: false }, 53 | 54 | // Paths loaded via module imports: 55 | // Angular itself 56 | { pattern: 'node_modules/@angular/**/*.js', included: false, watched: false }, 57 | { pattern: 'node_modules/@angular/**/*.js.map', included: false, watched: false }, 58 | 59 | { pattern: appBase + '/systemjs.config.js', included: false, watched: false }, 60 | { pattern: appBase + '/systemjs.config.extras.js', included: false, watched: false }, 61 | 'karma-test-shim.js', // optionally extend SystemJS mapping e.g., with barrels 62 | 63 | // transpiled application & spec code paths loaded via module imports 64 | { pattern: appBase + '**/*.js', included: false, watched: true }, 65 | { pattern: testingBase + '**/*.js', included: false, watched: true }, 66 | 67 | 68 | // Asset (HTML & CSS) paths loaded via Angular's component compiler 69 | // (these paths need to be rewritten, see proxies section) 70 | { pattern: appBase + '**/*.html', included: false, watched: true }, 71 | { pattern: appBase + '**/*.css', included: false, watched: true }, 72 | 73 | // Paths for debugging with source maps in dev tools 74 | { pattern: appBase + '**/*.ts', included: false, watched: false }, 75 | { pattern: appBase + '**/*.js.map', included: false, watched: false }, 76 | { pattern: testingSrcBase + '**/*.ts', included: false, watched: false }, 77 | { pattern: testingBase + '**/*.js.map', included: false, watched: false} 78 | ], 79 | 80 | // Proxied base paths for loading assets 81 | proxies: { 82 | // required for modules fetched by SystemJS 83 | '/base/src/node_modules/': '/base/node_modules/' 84 | }, 85 | 86 | exclude: [], 87 | preprocessors: {}, 88 | reporters: ['progress', 'kjhtml'], 89 | 90 | port: 9876, 91 | colors: true, 92 | logLevel: config.LOG_INFO, 93 | autoWatch: true, 94 | browsers: ['Chrome'], 95 | singleRun: false 96 | }) 97 | } 98 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/non-essential-files.txt: -------------------------------------------------------------------------------- 1 | .git 2 | .gitignore 3 | .travis.yml 4 | *.spec*.ts 5 | CHANGELOG.md 6 | e2e 7 | favicon.ico 8 | karma.conf.js 9 | karma-test-shim.js 10 | LICENSE 11 | non-essential-files.txt 12 | protractor.config.js 13 | README.md 14 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-quickstart", 3 | "version": "1.0.0", 4 | "description": "QuickStart package.json from the documentation, supplemented with testing support", 5 | "scripts": { 6 | "build": "tsc -p src/", 7 | "build:watch": "tsc -p src/ -w", 8 | "build:e2e": "tsc -p e2e/", 9 | "serve": "lite-server -c=bs-config.json", 10 | "serve:e2e": "lite-server -c=bs-config.e2e.json", 11 | "prestart": "npm run build", 12 | "start": "concurrently \"npm run build:watch\" \"npm run serve\"", 13 | "pree2e": "npm run build:e2e", 14 | "e2e": "concurrently \"npm run serve:e2e\" \"npm run protractor\" --kill-others --success first", 15 | "preprotractor": "webdriver-manager update", 16 | "protractor": "protractor protractor.config.js", 17 | "pretest": "npm run build", 18 | "test": "concurrently \"npm run build:watch\" \"karma start karma.conf.js\"", 19 | "pretest:once": "npm run build", 20 | "test:once": "karma start karma.conf.js --single-run", 21 | "lint": "tslint ./src/**/*.ts -t verbose" 22 | }, 23 | "keywords": [], 24 | "author": "", 25 | "license": "MIT", 26 | "dependencies": { 27 | "@angular/common": "~2.4.0", 28 | "@angular/compiler": "~2.4.0", 29 | "@angular/core": "~2.4.0", 30 | "@angular/forms": "~2.4.0", 31 | "@angular/http": "~2.4.0", 32 | "@angular/platform-browser": "~2.4.0", 33 | "@angular/platform-browser-dynamic": "~2.4.0", 34 | "@angular/router": "~3.4.0", 35 | 36 | "angular-in-memory-web-api": "~0.2.4", 37 | "systemjs": "0.19.40", 38 | "core-js": "^2.4.1", 39 | "rxjs": "5.0.1", 40 | "zone.js": "^0.7.4" 41 | }, 42 | "devDependencies": { 43 | "concurrently": "^3.1.0", 44 | "lite-server": "^2.2.2", 45 | "typescript": "~2.0.10", 46 | 47 | "canonical-path": "0.0.2", 48 | "tslint": "^3.15.1", 49 | "lodash": "^4.16.4", 50 | "jasmine-core": "~2.4.1", 51 | "karma": "^1.3.0", 52 | "karma-chrome-launcher": "^2.0.0", 53 | "karma-cli": "^1.0.1", 54 | "karma-jasmine": "^1.0.2", 55 | "karma-jasmine-html-reporter": "^0.2.2", 56 | "protractor": "~4.0.14", 57 | "rimraf": "^2.5.4", 58 | 59 | "@types/node": "^6.0.46", 60 | "@types/jasmine": "^2.5.36" 61 | }, 62 | "repository": {} 63 | } 64 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/protractor.config.js: -------------------------------------------------------------------------------- 1 | // FIRST TIME ONLY- run: 2 | // ./node_modules/.bin/webdriver-manager update 3 | // 4 | // Try: `npm run webdriver:update` 5 | // 6 | // AND THEN EVERYTIME ... 7 | // 1. Compile with `tsc` 8 | // 2. Make sure the test server (e.g., lite-server: localhost:8080) is running. 9 | // 3. ./node_modules/.bin/protractor protractor.config.js 10 | // 11 | // To do all steps, try: `npm run e2e` 12 | 13 | var fs = require('fs'); 14 | var path = require('canonical-path'); 15 | var _ = require('lodash'); 16 | 17 | 18 | exports.config = { 19 | directConnect: true, 20 | 21 | // Capabilities to be passed to the webdriver instance. 22 | capabilities: { 23 | 'browserName': 'chrome' 24 | }, 25 | 26 | // Framework to use. Jasmine is recommended. 27 | framework: 'jasmine', 28 | 29 | // Spec patterns are relative to this config file 30 | specs: ['**/*e2e-spec.js' ], 31 | 32 | 33 | // For angular tests 34 | useAllAngular2AppRoots: true, 35 | 36 | // Base URL for application server 37 | baseUrl: 'http://localhost:8080', 38 | 39 | // doesn't seem to work. 40 | // resultJsonOutputFile: "foo.json", 41 | 42 | onPrepare: function() { 43 | //// SpecReporter 44 | //var SpecReporter = require('jasmine-spec-reporter'); 45 | //jasmine.getEnv().addReporter(new SpecReporter({displayStacktrace: 'none'})); 46 | //// jasmine.getEnv().addReporter(new SpecReporter({displayStacktrace: 'all'})); 47 | 48 | // debugging 49 | // console.log('browser.params:' + JSON.stringify(browser.params)); 50 | jasmine.getEnv().addReporter(new Reporter( browser.params )) ; 51 | 52 | // Allow changing bootstrap mode to NG1 for upgrade tests 53 | global.setProtractorToNg1Mode = function() { 54 | browser.useAllAngular2AppRoots = false; 55 | browser.rootEl = 'body'; 56 | }; 57 | }, 58 | 59 | jasmineNodeOpts: { 60 | // defaultTimeoutInterval: 60000, 61 | defaultTimeoutInterval: 10000, 62 | showTiming: true, 63 | print: function() {} 64 | } 65 | }; 66 | 67 | // Custom reporter 68 | function Reporter(options) { 69 | var _defaultOutputFile = path.resolve(process.cwd(), './_test-output', 'protractor-results.txt'); 70 | options.outputFile = options.outputFile || _defaultOutputFile; 71 | 72 | initOutputFile(options.outputFile); 73 | options.appDir = options.appDir || './'; 74 | var _root = { appDir: options.appDir, suites: [] }; 75 | log('AppDir: ' + options.appDir, +1); 76 | var _currentSuite; 77 | 78 | this.suiteStarted = function(suite) { 79 | _currentSuite = { description: suite.description, status: null, specs: [] }; 80 | _root.suites.push(_currentSuite); 81 | log('Suite: ' + suite.description, +1); 82 | }; 83 | 84 | this.suiteDone = function(suite) { 85 | var statuses = _currentSuite.specs.map(function(spec) { 86 | return spec.status; 87 | }); 88 | statuses = _.uniq(statuses); 89 | var status = statuses.indexOf('failed') >= 0 ? 'failed' : statuses.join(', '); 90 | _currentSuite.status = status; 91 | log('Suite ' + _currentSuite.status + ': ' + suite.description, -1); 92 | }; 93 | 94 | this.specStarted = function(spec) { 95 | 96 | }; 97 | 98 | this.specDone = function(spec) { 99 | var currentSpec = { 100 | description: spec.description, 101 | status: spec.status 102 | }; 103 | if (spec.failedExpectations.length > 0) { 104 | currentSpec.failedExpectations = spec.failedExpectations; 105 | } 106 | 107 | _currentSuite.specs.push(currentSpec); 108 | log(spec.status + ' - ' + spec.description); 109 | }; 110 | 111 | this.jasmineDone = function() { 112 | outputFile = options.outputFile; 113 | //// Alternate approach - just stringify the _root - not as pretty 114 | //// but might be more useful for automation. 115 | // var output = JSON.stringify(_root, null, 2); 116 | var output = formatOutput(_root); 117 | fs.appendFileSync(outputFile, output); 118 | }; 119 | 120 | function ensureDirectoryExistence(filePath) { 121 | var dirname = path.dirname(filePath); 122 | if (directoryExists(dirname)) { 123 | return true; 124 | } 125 | ensureDirectoryExistence(dirname); 126 | fs.mkdirSync(dirname); 127 | } 128 | 129 | function directoryExists(path) { 130 | try { 131 | return fs.statSync(path).isDirectory(); 132 | } 133 | catch (err) { 134 | return false; 135 | } 136 | } 137 | 138 | function initOutputFile(outputFile) { 139 | ensureDirectoryExistence(outputFile); 140 | var header = "Protractor results for: " + (new Date()).toLocaleString() + "\n\n"; 141 | fs.writeFileSync(outputFile, header); 142 | } 143 | 144 | // for output file output 145 | function formatOutput(output) { 146 | var indent = ' '; 147 | var pad = ' '; 148 | var results = []; 149 | results.push('AppDir:' + output.appDir); 150 | output.suites.forEach(function(suite) { 151 | results.push(pad + 'Suite: ' + suite.description + ' -- ' + suite.status); 152 | pad+=indent; 153 | suite.specs.forEach(function(spec) { 154 | results.push(pad + spec.status + ' - ' + spec.description); 155 | if (spec.failedExpectations) { 156 | pad+=indent; 157 | spec.failedExpectations.forEach(function (fe) { 158 | results.push(pad + 'message: ' + fe.message); 159 | }); 160 | pad=pad.substr(2); 161 | } 162 | }); 163 | pad = pad.substr(2); 164 | results.push(''); 165 | }); 166 | results.push(''); 167 | return results.join('\n'); 168 | } 169 | 170 | // for console output 171 | var _pad; 172 | function log(str, indent) { 173 | _pad = _pad || ''; 174 | if (indent == -1) { 175 | _pad = _pad.substr(2); 176 | } 177 | console.log(_pad + str); 178 | if (indent == 1) { 179 | _pad = _pad + ' '; 180 | } 181 | } 182 | 183 | } 184 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { AppComponent } from './app.component'; 2 | 3 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 4 | import { By } from '@angular/platform-browser'; 5 | import { DebugElement } from '@angular/core'; 6 | 7 | describe('AppComponent', function () { 8 | let de: DebugElement; 9 | let comp: AppComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async(() => { 13 | TestBed.configureTestingModule({ 14 | declarations: [ AppComponent ] 15 | }) 16 | .compileComponents(); 17 | })); 18 | 19 | beforeEach(() => { 20 | fixture = TestBed.createComponent(AppComponent); 21 | comp = fixture.componentInstance; 22 | de = fixture.debugElement.query(By.css('h1')); 23 | }); 24 | 25 | it('should create component', () => expect(comp).toBeDefined() ); 26 | 27 | it('should have expected

text', () => { 28 | fixture.detectChanges(); 29 | const h1 = de.nativeElement; 30 | expect(h1.innerText).toMatch(/angular/i, 31 | '

should say something about "Angular"'); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import './rxjs.practice'; 3 | import { Observable } from 'rxjs/Observable'; 4 | import { ReplaySubject } from 'rxjs/ReplaySubject'; 5 | import { Observer } from 'rxjs/Observer'; 6 | import { extract } from './rxjs.practice'; 7 | import 'rxjs/add/operator/mergeMap'; 8 | import 'rxjs/add/operator/catch'; 9 | import 'rxjs/add/operator/bufferCount'; 10 | import 'rxjs/add/operator/take'; 11 | import 'rxjs/add/operator/delay'; 12 | import 'rxjs/add/operator/publish'; 13 | import 'rxjs/add/observable/of'; 14 | import 'rxjs/add/observable/from'; 15 | 16 | @Component({ 17 | selector: 'my-app', 18 | template: ` 19 |

{{title}}

20 | 21 | `, 22 | }) 23 | export class AppComponent { 24 | title = 'RxJS is cool'; 25 | socketConnections: { [key: string]: Observable } = {}; 26 | cachedSockets: { [key: string]: Observable } = {}; 27 | constructor() { 28 | // Tasl 2.1 29 | this.get$('http://someurl.com') 30 | .map(data => JSON.parse(data)) 31 | .mergeMap(data => extract(data)) 32 | .subscribe(console.log, console.error); 33 | 34 | // Task 2.2 35 | const urls = ['http://domain.com', 'http://domain.bg', 'http://domain.eu']; 36 | Observable.from(urls).mergeMap(url => this.get$(url)).map(data => JSON.parse(data)).subscribe(console.log); 37 | 38 | // Task 3.1 39 | Observable.from(urls).mergeMap(url => this.errorGet$(url)).map(data => JSON.parse(data)).subscribe(console.log, console.error); 40 | 41 | // Task 3.2 42 | Observable.from(urls) 43 | .mergeMap((url) => { 44 | return Observable.of(url) 45 | .mergeMap(url => this.errorGet$(url)) 46 | .map(res => JSON.parse(res)) 47 | .catch(err => { 48 | console.error(err); 49 | return Observable.of(null); 50 | }) 51 | .map(responseData => ({ responseData, url })); 52 | }) 53 | .bufferCount(urls.length) 54 | .subscribe(res => console.log('4.1 ', res)); 55 | 56 | // Task 4.1 57 | const socket_url = 'Some URL'; 58 | this.connect(socket_url).subscribe(data => console.log(`socket connecting 1: ${data}`)); 59 | setTimeout(() => this.connect(socket_url).subscribe(data => console.log(`socket connection 2: ${data}`)), 4000); 60 | 61 | // Task 5.1 62 | const cache_socket_url = 'Cache Some URL'; 63 | setTimeout(() => { 64 | console.log('Cache'); 65 | setTimeout(() => this.socketCache(cache_socket_url).subscribe(data => console.log(`cached socket connection 1 ${data}`)), 2000); 66 | setTimeout(() => this.socketCache(cache_socket_url).subscribe(data => console.log(`cached socket connection 2: ${data}`)), 6000); 67 | setTimeout(() => this.socketCache(cache_socket_url).subscribe(data => console.log(`cached socket connection 3: ${data}`)), 12000); 68 | }, 10000); 69 | } 70 | 71 | get$(url: string): Observable { 72 | return new Observable((observer: Observer) => { 73 | const request = this.get(url, (err: any, data: any) => { 74 | if (err) return observer.error(err); 75 | observer.next(data); 76 | observer.complete(); 77 | }); 78 | 79 | return () => { 80 | request.cancel(); 81 | }; 82 | }); 83 | } 84 | 85 | get(url: string, callback: any) { 86 | console.log(`fake get request to ${url}`); 87 | const id = setTimeout(() => { 88 | console.log(`get request JSON response`) 89 | callback(null, `[{ 90 | "name": "Ivan", 91 | "age": 18 92 | }, { 93 | "name": "Petar", 94 | "age": 25 95 | }, { 96 | "name": "Alex", 97 | "age": 10 98 | }]`); 99 | }, 2000); 100 | return { 101 | cancel: () => clearInterval(id) 102 | }; 103 | } 104 | 105 | errorGet(url: string, callback: any) { 106 | console.log(`fake get request to ${url}`); 107 | if (/(.*)\.(bg)/.test(url)) callback(new Error('Cannot resolve domain .bg')); 108 | const id = setTimeout(() => { 109 | console.log(`get request JSON response`) 110 | callback(null, `[{ 111 | "name": "Ivan", 112 | "age": 18 113 | }, { 114 | "name": "Petar", 115 | "age": 25 116 | }, { 117 | "name": "Alex", 118 | "age": 10 119 | }]`) 120 | }, 2000); 121 | return { 122 | cancel: () => clearInterval(id) 123 | }; 124 | } 125 | 126 | errorGet$(url: string): Observable { 127 | return new Observable((observer: Observer) => { 128 | const request = this.errorGet(url, (err: any, data: any) => { 129 | if (err) return observer.error(err); 130 | observer.next(data); 131 | observer.complete(); 132 | }); 133 | 134 | return () => { 135 | request.cancel(); 136 | }; 137 | }); 138 | } 139 | 140 | connect(url: string) { 141 | console.log(`socket connecting to url: ${url}`); 142 | let socket; 143 | if (socket = this.socketConnections[url]) { return socket; } 144 | return this.socketConnections[url] = Observable.interval(1000) 145 | .startWith(-1) 146 | .delay(1000) 147 | .take(5) 148 | .map(i => i === -1 ? 'socket connected' : `socket message ${i}`) 149 | .publish() 150 | .refCount(); 151 | } 152 | 153 | socketCache(url: string) { 154 | let socket; 155 | if (socket = this.cachedSockets[url]) { return socket; } 156 | const subject = new ReplaySubject(); 157 | this.connect(url).subscribe(subject); 158 | return this.cachedSockets[url] = subject; 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | import { ClockComponent } from './clock.component/clock.component'; 4 | 5 | import { AppComponent } from './app.component'; 6 | 7 | @NgModule({ 8 | imports: [ BrowserModule ], 9 | declarations: [ AppComponent, ClockComponent ], 10 | bootstrap: [ AppComponent ] 11 | }) 12 | export class AppModule { } 13 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/src/app/clock.component/clock.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Observable } from 'rxjs/Observable'; 3 | import 'rxjs/add/observable/of'; 4 | import 'rxjs/add/observable/interval'; 5 | import 'rxjs/add/operator/delay'; 6 | import 'rxjs/add/operator/map'; 7 | import 'rxjs/add/operator/startWith'; 8 | 9 | const getTime = () => { 10 | const d = new Date(); 11 | return { 12 | hours: d.getHours(), 13 | minutes: d.getMinutes(), 14 | seconds: d.getSeconds() 15 | }; 16 | }; 17 | 18 | @Component({ 19 | selector: 'clock', 20 | template: ` 21 |

0{{clock.hours}}:0{{clock.minutes}}

22 | `, 23 | }) 24 | export class ClockComponent { 25 | clock: { hours: number, minutes: number, seconds: number } = null; 26 | constructor() { 27 | /* 28 | Task 1.4 29 | This solution is more complicated because I want to have the exact time but the main idea was to have something like: 30 | Observable.interval(6000).startWith(0).map(getTime()).subscribe(...) 31 | */ 32 | Observable.interval(60000) // every 60 seconds we want our stream to emit a value 33 | .startWith(0) // when the delay is over the interval will be turend on so we will miss one value so we just poroduce one here 34 | .delay((60 - getTime().seconds) * 1000) // we want our stream to produce values on the exact minute so seconds need to be 0 35 | .startWith(0) // produce an extra init value so we can get the clock initialized 36 | .map(() => getTime()) // we dont care about the value just get the time 37 | .map(val => { console.log(val); return val; }) // just loging 38 | .subscribe(val => this.clock = val); // set the value 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/src/app/rxjs.practice.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs/Observable'; 2 | import 'rxjs/add/observable/from'; 3 | import 'rxjs/add/observable/of'; 4 | import 'rxjs/add/observable/zip'; 5 | import 'rxjs/add/operator/reduce'; 6 | import 'rxjs/add/operator/filter'; 7 | import 'rxjs/add/operator/map'; 8 | import 'rxjs/add/operator/bufferCount'; 9 | 10 | const users = [{ 11 | name: 'Ivan', 12 | age: 18 13 | }, { 14 | name: 'Petar', 15 | age: 25 16 | }, { 17 | name: 'Alex', 18 | age: 10 19 | }]; 20 | // 1.1 21 | Observable.from([10, 64, 12, 32]).reduce((acc, curr) => acc + curr, 0).subscribe(console.log); 22 | // 1.2 23 | Observable.from([3, 7, 5, 2, 8, 1]).filter(x => x % 2 !== 0).subscribe(console.log); 24 | // 1.3 25 | export const extract = (arr: any[]) => { 26 | const source$ = Observable.from(arr); 27 | const names$ = source$.map(u => u.name).bufferCount(arr.length); 28 | const ages$ = source$.map(u => u.ages).bufferCount(arr.length); 29 | return Observable.zip(names$, ages$, (names, ages) => ({names, ages})); 30 | }; 31 | 32 | extract(users).subscribe(console.log); 33 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Angular-2/c584709e30b08e9800e8629242e96672b60f7226/week1/2/solutionsApp/src/favicon.ico -------------------------------------------------------------------------------- /week1/2/solutionsApp/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Angular QuickStart 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | Loading AppComponent content here ... 24 | 25 | 26 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/src/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | 3 | import { AppModule } from './app/app.module'; 4 | 5 | platformBrowserDynamic().bootstrapModule(AppModule); 6 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/src/styles.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: #369; 3 | font-family: Arial, Helvetica, sans-serif; 4 | font-size: 250%; 5 | } 6 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/src/systemjs.config.extras.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Add barrels and stuff 3 | * Adjust as necessary for your application needs. 4 | */ 5 | // (function (global) { 6 | // System.config({ 7 | // packages: { 8 | // // add packages here 9 | // } 10 | // }); 11 | // })(this); 12 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/src/systemjs.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * System configuration for Angular samples 3 | * Adjust as necessary for your application needs. 4 | */ 5 | (function (global) { 6 | System.config({ 7 | paths: { 8 | // paths serve as alias 9 | 'npm:': 'node_modules/' 10 | }, 11 | // map tells the System loader where to look for things 12 | map: { 13 | // our app is within the app folder 14 | app: 'app', 15 | 16 | // angular bundles 17 | '@angular/core': 'npm:@angular/core/bundles/core.umd.js', 18 | '@angular/common': 'npm:@angular/common/bundles/common.umd.js', 19 | '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js', 20 | '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js', 21 | '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js', 22 | '@angular/http': 'npm:@angular/http/bundles/http.umd.js', 23 | '@angular/router': 'npm:@angular/router/bundles/router.umd.js', 24 | // '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js', 25 | 26 | // other libraries 27 | 'rxjs': 'npm:rxjs', 28 | 'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js' 29 | }, 30 | // packages tells the System loader how to load when no filename and/or no extension 31 | packages: { 32 | app: { 33 | defaultExtension: 'js' 34 | }, 35 | rxjs: { 36 | defaultExtension: 'js' 37 | } 38 | } 39 | }); 40 | })(this); 41 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "sourceMap": true, 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true, 9 | "lib": [ "es2015", "dom" ], 10 | "noImplicitAny": true, 11 | "suppressImplicitAnyIndexErrors": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /week1/2/solutionsApp/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "class-name": true, 4 | "comment-format": [ 5 | true, 6 | "check-space" 7 | ], 8 | "curly": true, 9 | "eofline": true, 10 | "forin": true, 11 | "indent": [ 12 | true, 13 | "spaces" 14 | ], 15 | "label-position": true, 16 | "label-undefined": true, 17 | "max-line-length": [ 18 | true, 19 | 140 20 | ], 21 | "member-access": false, 22 | "member-ordering": [ 23 | true, 24 | "static-before-instance", 25 | "variables-before-functions" 26 | ], 27 | "no-arg": true, 28 | "no-bitwise": true, 29 | "no-console": [ 30 | true, 31 | "debug", 32 | "info", 33 | "time", 34 | "timeEnd", 35 | "trace" 36 | ], 37 | "no-construct": true, 38 | "no-debugger": true, 39 | "no-duplicate-key": true, 40 | "no-duplicate-variable": true, 41 | "no-empty": false, 42 | "no-eval": true, 43 | "no-inferrable-types": true, 44 | "no-shadowed-variable": true, 45 | "no-string-literal": false, 46 | "no-switch-case-fall-through": true, 47 | "no-trailing-whitespace": true, 48 | "no-unused-expression": true, 49 | "no-unused-variable": true, 50 | "no-unreachable": true, 51 | "no-use-before-declare": true, 52 | "no-var-keyword": true, 53 | "object-literal-sort-keys": false, 54 | "one-line": [ 55 | true, 56 | "check-open-brace", 57 | "check-catch", 58 | "check-else", 59 | "check-whitespace" 60 | ], 61 | "quotemark": [ 62 | true, 63 | "single" 64 | ], 65 | "radix": true, 66 | "semicolon": [ 67 | "always" 68 | ], 69 | "triple-equals": [ 70 | true, 71 | "allow-null-check" 72 | ], 73 | "typedef-whitespace": [ 74 | true, 75 | { 76 | "call-signature": "nospace", 77 | "index-signature": "nospace", 78 | "parameter": "nospace", 79 | "property-declaration": "nospace", 80 | "variable-declaration": "nospace" 81 | } 82 | ], 83 | "variable-name": false, 84 | "whitespace": [ 85 | true, 86 | "check-branch", 87 | "check-decl", 88 | "check-operator", 89 | "check-separator", 90 | "check-type" 91 | ] 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /week2/1/README.md: -------------------------------------------------------------------------------- 1 | # Week 2 2 | 3 | ## Lecture 4 | Click [here](https://speakerdeck.com/iliaidakiev/3-angular-cli-custom-directives-renderer) 5 | 6 | ## Tasks 7 | 8 | 1. Create a custom directive called angActive that takes two inputs. The first input is a color that needs to be applied if the second input is true (second input is a predicate). 9 | 10 | 2. Create a custom directive called angIf that shows the contents of the template if a condition is true and hides it otherwise. 11 | 12 | 3. Create a custom directive angIfElse. (Use ContentChild or ContentChildren to get the children) 13 | 14 | 4. Create a directive called pager. It takes three inputs: data - array of items, currentPage and pageSize. Depending on the inputs it needs to list the necessary items. (use rxjs skip and take operators) 15 | 16 | 5. Create a component called grid. It has the same inputs as pager and it uses pager to present the data. The grid component needs to hold the state for the currentPage and to create buttons for the different pages (use the directive from task 1). Handle the case when there are too many pages and some of the page buttons are replaced by ... (example 1 2 3 ... 10, 1 ... 4 5 6 ... 10) -------------------------------------------------------------------------------- /week2/1/tasks/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /week2/1/tasks/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | 7 | # dependencies 8 | /node_modules 9 | 10 | # IDEs and editors 11 | /.idea 12 | .project 13 | .classpath 14 | .c9/ 15 | *.launch 16 | .settings/ 17 | 18 | # IDE - VSCode 19 | .vscode/* 20 | !.vscode/settings.json 21 | !.vscode/tasks.json 22 | !.vscode/launch.json 23 | !.vscode/extensions.json 24 | 25 | # misc 26 | /.sass-cache 27 | /connect.lock 28 | /coverage/* 29 | /libpeerconnection.log 30 | npm-debug.log 31 | testem.log 32 | /typings 33 | 34 | # e2e 35 | /e2e/*.js 36 | /e2e/*.map 37 | 38 | #System Files 39 | .DS_Store 40 | Thumbs.db 41 | -------------------------------------------------------------------------------- /week2/1/tasks/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "vsicons.presets.angular": true 3 | } -------------------------------------------------------------------------------- /week2/1/tasks/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "0.1.0", 5 | "command": "echo", 6 | "isShellCommand": true, 7 | "args": ["Hello World"], 8 | "showOutput": "never" 9 | } -------------------------------------------------------------------------------- /week2/1/tasks/README.md: -------------------------------------------------------------------------------- 1 | # Tasks 2 | 3 | This project was generated with [angular-cli](https://github.com/angular/angular-cli) version 1.0.0-beta.28.3. 4 | 5 | ## Development server 6 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 7 | 8 | ## Code scaffolding 9 | 10 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive/pipe/service/class/module`. 11 | 12 | ## Build 13 | 14 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build. 15 | 16 | ## Running unit tests 17 | 18 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 19 | 20 | ## Running end-to-end tests 21 | 22 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 23 | Before running the tests make sure you are serving the app via `ng serve`. 24 | 25 | ## Deploying to GitHub Pages 26 | 27 | Run `ng github-pages:deploy` to deploy to GitHub Pages. 28 | 29 | ## Further help 30 | 31 | To get more help on the `angular-cli` use `ng help` or go check out the [Angular-CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 32 | -------------------------------------------------------------------------------- /week2/1/tasks/angular-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "project": { 3 | "version": "1.0.0-beta.28.3", 4 | "name": "tasks" 5 | }, 6 | "apps": [ 7 | { 8 | "root": "src", 9 | "outDir": "dist", 10 | "assets": [ 11 | "assets", 12 | "favicon.ico" 13 | ], 14 | "index": "index.html", 15 | "main": "main.ts", 16 | "polyfills": "polyfills.ts", 17 | "test": "test.ts", 18 | "tsconfig": "tsconfig.json", 19 | "prefix": "app", 20 | "styles": [ 21 | "styles.css" 22 | ], 23 | "scripts": [], 24 | "environments": { 25 | "source": "environments/environment.ts", 26 | "dev": "environments/environment.ts", 27 | "prod": "environments/environment.prod.ts" 28 | } 29 | } 30 | ], 31 | "e2e": { 32 | "protractor": { 33 | "config": "./protractor.conf.js" 34 | } 35 | }, 36 | "lint": [ 37 | { 38 | "files": "src/**/*.ts", 39 | "project": "src/tsconfig.json" 40 | }, 41 | { 42 | "files": "e2e/**/*.ts", 43 | "project": "e2e/tsconfig.json" 44 | } 45 | ], 46 | "test": { 47 | "karma": { 48 | "config": "./karma.conf.js" 49 | } 50 | }, 51 | "defaults": { 52 | "styleExt": "css", 53 | "prefixInterfaces": false, 54 | "inline": { 55 | "style": false, 56 | "template": false 57 | }, 58 | "spec": { 59 | "class": false, 60 | "component": true, 61 | "directive": true, 62 | "module": false, 63 | "pipe": true, 64 | "service": true 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /week2/1/tasks/e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { TasksPage } from './app.po'; 2 | 3 | describe('tasks App', function() { 4 | let page: TasksPage; 5 | 6 | beforeEach(() => { 7 | page = new TasksPage(); 8 | }); 9 | 10 | it('should display message saying app works', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('app works!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /week2/1/tasks/e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, element, by } from 'protractor'; 2 | 3 | export class TasksPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /week2/1/tasks/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "declaration": false, 5 | "emitDecoratorMetadata": true, 6 | "experimentalDecorators": true, 7 | "module": "commonjs", 8 | "moduleResolution": "node", 9 | "outDir": "../dist/out-tsc-e2e", 10 | "sourceMap": true, 11 | "target": "es5", 12 | "typeRoots": [ 13 | "../node_modules/@types" 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /week2/1/tasks/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/0.13/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', 'angular-cli'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-remap-istanbul'), 12 | require('angular-cli/plugins/karma') 13 | ], 14 | files: [ 15 | { pattern: './src/test.ts', watched: false } 16 | ], 17 | preprocessors: { 18 | './src/test.ts': ['angular-cli'] 19 | }, 20 | mime: { 21 | 'text/x-typescript': ['ts','tsx'] 22 | }, 23 | remapIstanbulReporter: { 24 | reports: { 25 | html: 'coverage', 26 | lcovonly: './coverage/coverage.lcov' 27 | } 28 | }, 29 | angularCli: { 30 | config: './angular-cli.json', 31 | environment: 'dev' 32 | }, 33 | reporters: config.angularCli && config.angularCli.codeCoverage 34 | ? ['progress', 'karma-remap-istanbul'] 35 | : ['progress'], 36 | port: 9876, 37 | colors: true, 38 | logLevel: config.LOG_INFO, 39 | autoWatch: true, 40 | browsers: ['Chrome'], 41 | singleRun: false 42 | }); 43 | }; 44 | -------------------------------------------------------------------------------- /week2/1/tasks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tasks", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "angular-cli": {}, 6 | "scripts": { 7 | "ng": "ng", 8 | "start": "ng serve", 9 | "test": "ng test", 10 | "pree2e": "webdriver-manager update --standalone false --gecko false", 11 | "e2e": "protractor" 12 | }, 13 | "private": true, 14 | "dependencies": { 15 | "@angular/common": "^2.3.1", 16 | "@angular/compiler": "^2.3.1", 17 | "@angular/core": "^2.3.1", 18 | "@angular/forms": "^2.3.1", 19 | "@angular/http": "^2.3.1", 20 | "@angular/platform-browser": "^2.3.1", 21 | "@angular/platform-browser-dynamic": "^2.3.1", 22 | "@angular/router": "^3.3.1", 23 | "core-js": "^2.4.1", 24 | "rxjs": "^5.0.1", 25 | "ts-helpers": "^1.1.1", 26 | "zone.js": "^0.7.2" 27 | }, 28 | "devDependencies": { 29 | "@angular/compiler-cli": "^2.3.1", 30 | "@types/jasmine": "2.5.38", 31 | "@types/node": "^6.0.42", 32 | "angular-cli": "1.0.0-beta.28.3", 33 | "codelyzer": "~2.0.0-beta.1", 34 | "jasmine-core": "2.5.2", 35 | "jasmine-spec-reporter": "2.5.0", 36 | "karma": "1.2.0", 37 | "karma-chrome-launcher": "^2.0.0", 38 | "karma-cli": "^1.0.1", 39 | "karma-jasmine": "^1.0.2", 40 | "karma-remap-istanbul": "^0.2.1", 41 | "protractor": "~4.0.13", 42 | "ts-node": "1.2.1", 43 | "tslint": "^4.3.0", 44 | "typescript": "~2.0.3" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /week2/1/tasks/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | /*global jasmine */ 5 | var SpecReporter = require('jasmine-spec-reporter'); 6 | 7 | exports.config = { 8 | allScriptsTimeout: 11000, 9 | specs: [ 10 | './e2e/**/*.e2e-spec.ts' 11 | ], 12 | capabilities: { 13 | 'browserName': 'chrome' 14 | }, 15 | directConnect: true, 16 | baseUrl: 'http://localhost:4200/', 17 | framework: 'jasmine', 18 | jasmineNodeOpts: { 19 | showColors: true, 20 | defaultTimeoutInterval: 30000, 21 | print: function() {} 22 | }, 23 | useAllAngular2AppRoots: true, 24 | beforeLaunch: function() { 25 | require('ts-node').register({ 26 | project: 'e2e' 27 | }); 28 | }, 29 | onPrepare: function() { 30 | jasmine.getEnv().addReporter(new SpecReporter()); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /week2/1/tasks/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Angular-2/c584709e30b08e9800e8629242e96672b60f7226/week2/1/tasks/src/app/app.component.css -------------------------------------------------------------------------------- /week2/1/tasks/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |

2 | {{title}} 3 |

4 | 5 | -------------------------------------------------------------------------------- /week2/1/tasks/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-unused-variable */ 2 | 3 | import { TestBed, async } from '@angular/core/testing'; 4 | import { AppComponent } from './app.component'; 5 | 6 | describe('AppComponent', () => { 7 | beforeEach(() => { 8 | TestBed.configureTestingModule({ 9 | declarations: [ 10 | AppComponent 11 | ], 12 | }); 13 | TestBed.compileComponents(); 14 | }); 15 | 16 | it('should create the app', async(() => { 17 | const fixture = TestBed.createComponent(AppComponent); 18 | const app = fixture.debugElement.componentInstance; 19 | expect(app).toBeTruthy(); 20 | })); 21 | 22 | it(`should have as title 'app works!'`, async(() => { 23 | const fixture = TestBed.createComponent(AppComponent); 24 | const app = fixture.debugElement.componentInstance; 25 | expect(app.title).toEqual('app works!'); 26 | })); 27 | 28 | it('should render title in a h1 tag', async(() => { 29 | const fixture = TestBed.createComponent(AppComponent); 30 | fixture.detectChanges(); 31 | const compiled = fixture.debugElement.nativeElement; 32 | expect(compiled.querySelector('h1').textContent).toContain('app works!'); 33 | })); 34 | }); 35 | -------------------------------------------------------------------------------- /week2/1/tasks/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'] 7 | }) 8 | export class AppComponent { 9 | title = 'app works!'; 10 | data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]; 11 | } 12 | -------------------------------------------------------------------------------- /week2/1/tasks/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { FormsModule } from '@angular/forms'; 4 | import { HttpModule } from '@angular/http'; 5 | 6 | import { AppComponent } from './app.component'; 7 | import { DirectivesModule } from './directives/directives.module'; 8 | 9 | @NgModule({ 10 | declarations: [ 11 | AppComponent 12 | ], 13 | imports: [ 14 | BrowserModule, 15 | FormsModule, 16 | HttpModule, 17 | DirectivesModule 18 | ], 19 | providers: [], 20 | bootstrap: [AppComponent] 21 | }) 22 | export class AppModule { } 23 | -------------------------------------------------------------------------------- /week2/1/tasks/src/app/directives/directives.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { PagerDirective } from './pager.directive'; 4 | import { GridComponent } from './grid/grid.component'; 5 | 6 | @NgModule({ 7 | imports: [ 8 | CommonModule 9 | ], 10 | declarations: [PagerDirective, GridComponent], 11 | exports: [GridComponent] 12 | }) 13 | export class DirectivesModule { } 14 | -------------------------------------------------------------------------------- /week2/1/tasks/src/app/directives/grid/grid.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Angular-2/c584709e30b08e9800e8629242e96672b60f7226/week2/1/tasks/src/app/directives/grid/grid.component.css -------------------------------------------------------------------------------- /week2/1/tasks/src/app/directives/grid/grid.component.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /week2/1/tasks/src/app/directives/grid/grid.component.spec.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-unused-variable */ 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | import { By } from '@angular/platform-browser'; 4 | import { DebugElement } from '@angular/core'; 5 | 6 | import { GridComponent } from './grid.component'; 7 | 8 | describe('GridComponent', () => { 9 | let component: GridComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async(() => { 13 | TestBed.configureTestingModule({ 14 | declarations: [ GridComponent ] 15 | }) 16 | .compileComponents(); 17 | })); 18 | 19 | beforeEach(() => { 20 | fixture = TestBed.createComponent(GridComponent); 21 | component = fixture.componentInstance; 22 | fixture.detectChanges(); 23 | }); 24 | 25 | it('should create', () => { 26 | expect(component).toBeTruthy(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /week2/1/tasks/src/app/directives/grid/grid.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-grid', 5 | templateUrl: './grid.component.html', 6 | styleUrls: ['./grid.component.css'] 7 | }) 8 | export class GridComponent implements OnInit { 9 | 10 | @Input() pageSize: number; 11 | @Input() data: any[]; 12 | currentPage: number = 1; 13 | 14 | constructor() { } 15 | 16 | ngOnInit() { 17 | this.pageSize = parseInt(this.pageSize.toString()); 18 | } 19 | 20 | prev() { 21 | if(this.currentPage - 1 <= 0) return; 22 | --this.currentPage; 23 | } 24 | 25 | next() { 26 | if(this.currentPage + 1 > Math.ceil(this.data.length / this.pageSize)) return; 27 | ++this.currentPage; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /week2/1/tasks/src/app/directives/pager.directive.spec.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-unused-variable */ 2 | 3 | import { TestBed, async } from '@angular/core/testing'; 4 | import { PagerDirective } from './pager.directive'; 5 | 6 | describe('PagerDirective', () => { 7 | it('should create an instance', () => { 8 | const directive = new PagerDirective(); 9 | expect(directive).toBeTruthy(); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /week2/1/tasks/src/app/directives/pager.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, TemplateRef, ViewContainerRef, Input, ContentChild, OnChanges } from '@angular/core'; 2 | import { Observable } from 'rxjs/Observable'; 3 | import 'rxjs/add/observable/from'; 4 | import 'rxjs/add/operator/skip'; 5 | import 'rxjs/add/operator/take'; 6 | 7 | @Directive({ 8 | selector: '[pager]' 9 | }) 10 | export class PagerDirective implements OnChanges { 11 | @Input() pageSize: number; 12 | @Input() currentPage: number; 13 | @Input() data: any[]; 14 | 15 | constructor(private _template: TemplateRef, private _vc: ViewContainerRef) { } 16 | 17 | ngOnChanges() { 18 | this._vc.clear(); 19 | Observable.from(this.data).skip((this.currentPage - 1) * this.pageSize).take(this.pageSize).subscribe(item => { 20 | this._vc.createEmbeddedView(this._template, { item }); 21 | }); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /week2/1/tasks/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Angular-2/c584709e30b08e9800e8629242e96672b60f7226/week2/1/tasks/src/assets/.gitkeep -------------------------------------------------------------------------------- /week2/1/tasks/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /week2/1/tasks/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false 8 | }; 9 | -------------------------------------------------------------------------------- /week2/1/tasks/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Angular-2/c584709e30b08e9800e8629242e96672b60f7226/week2/1/tasks/src/favicon.ico -------------------------------------------------------------------------------- /week2/1/tasks/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tasks 6 | 7 | 8 | 9 | 10 | 11 | 12 | Loading... 13 | 14 | 15 | -------------------------------------------------------------------------------- /week2/1/tasks/src/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | import { enableProdMode } from '@angular/core'; 3 | import { environment } from './environments/environment'; 4 | import { AppModule } from './app/app.module'; 5 | 6 | if (environment.production) { 7 | enableProdMode(); 8 | } 9 | 10 | platformBrowserDynamic().bootstrapModule(AppModule); 11 | -------------------------------------------------------------------------------- /week2/1/tasks/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | // This file includes polyfills needed by Angular and is loaded before the app. 2 | // You can add your own extra polyfills to this file. 3 | import 'core-js/es6/symbol'; 4 | import 'core-js/es6/object'; 5 | import 'core-js/es6/function'; 6 | import 'core-js/es6/parse-int'; 7 | import 'core-js/es6/parse-float'; 8 | import 'core-js/es6/number'; 9 | import 'core-js/es6/math'; 10 | import 'core-js/es6/string'; 11 | import 'core-js/es6/date'; 12 | import 'core-js/es6/array'; 13 | import 'core-js/es6/regexp'; 14 | import 'core-js/es6/map'; 15 | import 'core-js/es6/set'; 16 | import 'core-js/es6/reflect'; 17 | 18 | import 'core-js/es7/reflect'; 19 | import 'zone.js/dist/zone'; 20 | 21 | // If you need to support the browsers/features below, uncomment the import 22 | // and run `npm install import-name-here'; 23 | // Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 24 | 25 | // Needed for: IE9 26 | // import 'classlist.js'; 27 | 28 | // Animations 29 | // Needed for: All but Chrome and Firefox, Not supported in IE9 30 | // import 'web-animations-js'; 31 | 32 | // Date, currency, decimal and percent pipes 33 | // Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 34 | // import 'intl'; 35 | 36 | // NgClass on SVG elements 37 | // Needed for: IE10, IE11 38 | // import 'classlist.js'; 39 | -------------------------------------------------------------------------------- /week2/1/tasks/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /week2/1/tasks/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/long-stack-trace-zone'; 4 | import 'zone.js/dist/proxy.js'; 5 | import 'zone.js/dist/sync-test'; 6 | import 'zone.js/dist/jasmine-patch'; 7 | import 'zone.js/dist/async-test'; 8 | import 'zone.js/dist/fake-async-test'; 9 | import { getTestBed } from '@angular/core/testing'; 10 | import { 11 | BrowserDynamicTestingModule, 12 | platformBrowserDynamicTesting 13 | } from '@angular/platform-browser-dynamic/testing'; 14 | 15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. 16 | declare var __karma__: any; 17 | declare var require: any; 18 | 19 | // Prevent Karma from running prematurely. 20 | __karma__.loaded = function () {}; 21 | 22 | // First, initialize the Angular testing environment. 23 | getTestBed().initTestEnvironment( 24 | BrowserDynamicTestingModule, 25 | platformBrowserDynamicTesting() 26 | ); 27 | // Then we find all the tests. 28 | const context = require.context('./', true, /\.spec\.ts$/); 29 | // And load the modules. 30 | context.keys().map(context); 31 | // Finally, start Karma to run the tests. 32 | __karma__.start(); 33 | -------------------------------------------------------------------------------- /week2/1/tasks/src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "", 4 | "declaration": false, 5 | "emitDecoratorMetadata": true, 6 | "experimentalDecorators": true, 7 | "lib": ["es6", "dom"], 8 | "mapRoot": "./", 9 | "module": "es6", 10 | "moduleResolution": "node", 11 | "outDir": "../dist/out-tsc", 12 | "sourceMap": true, 13 | "target": "es5", 14 | "typeRoots": [ 15 | "../node_modules/@types" 16 | ] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /week2/1/tasks/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "callable-types": true, 7 | "class-name": true, 8 | "comment-format": [ 9 | true, 10 | "check-space" 11 | ], 12 | "curly": true, 13 | "eofline": true, 14 | "forin": true, 15 | "import-blacklist": [true, "rxjs"], 16 | "import-spacing": true, 17 | "indent": [ 18 | true, 19 | "spaces" 20 | ], 21 | "interface-over-type-literal": true, 22 | "label-position": true, 23 | "max-line-length": [ 24 | true, 25 | 140 26 | ], 27 | "member-access": false, 28 | "member-ordering": [ 29 | true, 30 | "static-before-instance", 31 | "variables-before-functions" 32 | ], 33 | "no-arg": true, 34 | "no-bitwise": true, 35 | "no-console": [ 36 | true, 37 | "debug", 38 | "info", 39 | "time", 40 | "timeEnd", 41 | "trace" 42 | ], 43 | "no-construct": true, 44 | "no-debugger": true, 45 | "no-duplicate-variable": true, 46 | "no-empty": false, 47 | "no-empty-interface": true, 48 | "no-eval": true, 49 | "no-inferrable-types": true, 50 | "no-shadowed-variable": true, 51 | "no-string-literal": false, 52 | "no-string-throw": true, 53 | "no-switch-case-fall-through": true, 54 | "no-trailing-whitespace": true, 55 | "no-unused-expression": true, 56 | "no-use-before-declare": true, 57 | "no-var-keyword": true, 58 | "object-literal-sort-keys": false, 59 | "one-line": [ 60 | true, 61 | "check-open-brace", 62 | "check-catch", 63 | "check-else", 64 | "check-whitespace" 65 | ], 66 | "prefer-const": true, 67 | "quotemark": [ 68 | true, 69 | "single" 70 | ], 71 | "radix": true, 72 | "semicolon": [ 73 | "always" 74 | ], 75 | "triple-equals": [ 76 | true, 77 | "allow-null-check" 78 | ], 79 | "typedef-whitespace": [ 80 | true, 81 | { 82 | "call-signature": "nospace", 83 | "index-signature": "nospace", 84 | "parameter": "nospace", 85 | "property-declaration": "nospace", 86 | "variable-declaration": "nospace" 87 | } 88 | ], 89 | "typeof-compare": true, 90 | "unified-signatures": true, 91 | "variable-name": false, 92 | "whitespace": [ 93 | true, 94 | "check-branch", 95 | "check-decl", 96 | "check-operator", 97 | "check-separator", 98 | "check-type" 99 | ], 100 | 101 | "directive-selector": [true, "attribute", "app", "camelCase"], 102 | "component-selector": [true, "element", "app", "kebab-case"], 103 | "use-input-property-decorator": true, 104 | "use-output-property-decorator": true, 105 | "use-host-property-decorator": true, 106 | "no-input-rename": true, 107 | "no-output-rename": true, 108 | "use-life-cycle-interface": true, 109 | "use-pipe-transform-interface": true, 110 | "component-class-suffix": true, 111 | "directive-class-suffix": true, 112 | "no-access-missing-member": true, 113 | "templates-use-public": true, 114 | "invoke-injectable": true 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /week2/2/README.md: -------------------------------------------------------------------------------- 1 | # Week 2.2 2 | 3 | ## Lecture 4 | Click [here](https://speakerdeck.com/iliaidakiev/4-dependency-injection-providers-and-injectors) 5 | 6 | ## Tasks 7 | For all tasks we are going to use this [API](https://jsonplaceholder.typicode.com) 8 | 9 | 1. Create a models for user, post and comment that will interact with the http service. They need to fetch the data when needed and sore it in a DataSource class that will act as storage. Whenever the component needs data it will ask the model for it. The model will check the store to see if its available and return it. If not, the model will fetch the data from the API, store it in the dataSource and return it to the component (inclue a flag that will be used for force fetch). Separate logic into differnet modules and just list all the items. 10 | 11 | 2. Refactor the DataSource to act as Hot Observable for each store entry (you can use Subjects if you feel like for the different data that we will be keeping (in our case users, posts comments)). When ever a certain entity is updated we want the store to to push the update to all of its subscribers for the certain set. The idea is to extract the state from our components into one place. -------------------------------------------------------------------------------- /week3/1/README.md: -------------------------------------------------------------------------------- 1 | # Week 3.1 2 | 3 | ### Lecture 4 | 5 | [Click here!](https://speakerdeck.com/iliaidakiev/5-predictable-reactive-state-management) -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | 7 | # dependencies 8 | /node_modules 9 | 10 | # IDEs and editors 11 | /.idea 12 | .project 13 | .classpath 14 | .c9/ 15 | *.launch 16 | .settings/ 17 | 18 | # IDE - VSCode 19 | .vscode/* 20 | !.vscode/settings.json 21 | !.vscode/tasks.json 22 | !.vscode/launch.json 23 | !.vscode/extensions.json 24 | 25 | # misc 26 | /.sass-cache 27 | /connect.lock 28 | /coverage/* 29 | /libpeerconnection.log 30 | npm-debug.log 31 | testem.log 32 | /typings 33 | 34 | # e2e 35 | /e2e/*.js 36 | /e2e/*.map 37 | 38 | #System Files 39 | .DS_Store 40 | Thumbs.db 41 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "0.1.0", 5 | "command": "echo", 6 | "isShellCommand": true, 7 | "args": [""], 8 | "showOutput": "never" 9 | } -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/README.md: -------------------------------------------------------------------------------- 1 | # NgrxTodoApp 2 | 3 | This project was generated with [angular-cli](https://github.com/angular/angular-cli) version 1.0.0-beta.28.3. 4 | 5 | ## Development server 6 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 7 | 8 | ## Code scaffolding 9 | 10 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive/pipe/service/class/module`. 11 | 12 | ## Build 13 | 14 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build. 15 | 16 | ## Running unit tests 17 | 18 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 19 | 20 | ## Running end-to-end tests 21 | 22 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 23 | Before running the tests make sure you are serving the app via `ng serve`. 24 | 25 | ## Deploying to GitHub Pages 26 | 27 | Run `ng github-pages:deploy` to deploy to GitHub Pages. 28 | 29 | ## Further help 30 | 31 | To get more help on the `angular-cli` use `ng help` or go check out the [Angular-CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 32 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/angular-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "project": { 3 | "version": "1.0.0-beta.28.3", 4 | "name": "ngrx-todo-app" 5 | }, 6 | "apps": [ 7 | { 8 | "root": "src", 9 | "outDir": "dist", 10 | "assets": [ 11 | "assets", 12 | "favicon.ico" 13 | ], 14 | "index": "index.html", 15 | "main": "main.ts", 16 | "polyfills": "polyfills.ts", 17 | "test": "test.ts", 18 | "tsconfig": "tsconfig.json", 19 | "prefix": "app", 20 | "styles": [ 21 | "styles.css" 22 | ], 23 | "scripts": [], 24 | "environments": { 25 | "source": "environments/environment.ts", 26 | "dev": "environments/environment.ts", 27 | "prod": "environments/environment.prod.ts" 28 | } 29 | } 30 | ], 31 | "e2e": { 32 | "protractor": { 33 | "config": "./protractor.conf.js" 34 | } 35 | }, 36 | "lint": [ 37 | { 38 | "files": "src/**/*.ts", 39 | "project": "src/tsconfig.json" 40 | }, 41 | { 42 | "files": "e2e/**/*.ts", 43 | "project": "e2e/tsconfig.json" 44 | } 45 | ], 46 | "test": { 47 | "karma": { 48 | "config": "./karma.conf.js" 49 | } 50 | }, 51 | "defaults": { 52 | "styleExt": "css", 53 | "prefixInterfaces": false, 54 | "inline": { 55 | "style": false, 56 | "template": false 57 | }, 58 | "spec": { 59 | "class": false, 60 | "component": true, 61 | "directive": true, 62 | "module": false, 63 | "pipe": true, 64 | "service": true 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { NgrxTodoAppPage } from './app.po'; 2 | 3 | describe('ngrx-todo-app App', function() { 4 | let page: NgrxTodoAppPage; 5 | 6 | beforeEach(() => { 7 | page = new NgrxTodoAppPage(); 8 | }); 9 | 10 | it('should display message saying app works', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('app works!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, element, by } from 'protractor'; 2 | 3 | export class NgrxTodoAppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "declaration": false, 5 | "emitDecoratorMetadata": true, 6 | "experimentalDecorators": true, 7 | "module": "commonjs", 8 | "moduleResolution": "node", 9 | "outDir": "../dist/out-tsc-e2e", 10 | "sourceMap": true, 11 | "target": "es5", 12 | "typeRoots": [ 13 | "../node_modules/@types" 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/0.13/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', 'angular-cli'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-remap-istanbul'), 12 | require('angular-cli/plugins/karma') 13 | ], 14 | files: [ 15 | { pattern: './src/test.ts', watched: false } 16 | ], 17 | preprocessors: { 18 | './src/test.ts': ['angular-cli'] 19 | }, 20 | mime: { 21 | 'text/x-typescript': ['ts','tsx'] 22 | }, 23 | remapIstanbulReporter: { 24 | reports: { 25 | html: 'coverage', 26 | lcovonly: './coverage/coverage.lcov' 27 | } 28 | }, 29 | angularCli: { 30 | config: './angular-cli.json', 31 | environment: 'dev' 32 | }, 33 | reporters: config.angularCli && config.angularCli.codeCoverage 34 | ? ['progress', 'karma-remap-istanbul'] 35 | : ['progress'], 36 | port: 9876, 37 | colors: true, 38 | logLevel: config.LOG_INFO, 39 | autoWatch: true, 40 | browsers: ['Chrome'], 41 | singleRun: false 42 | }); 43 | }; 44 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngrx-todo-app", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "angular-cli": {}, 6 | "scripts": { 7 | "ng": "ng", 8 | "start": "ng serve", 9 | "test": "ng test", 10 | "pree2e": "webdriver-manager update --standalone false --gecko false", 11 | "e2e": "protractor" 12 | }, 13 | "private": true, 14 | "dependencies": { 15 | "@angular/common": "^2.3.1", 16 | "@angular/compiler": "^2.3.1", 17 | "@angular/core": "^2.3.1", 18 | "@angular/forms": "^2.3.1", 19 | "@angular/http": "^2.3.1", 20 | "@angular/platform-browser": "^2.3.1", 21 | "@angular/platform-browser-dynamic": "^2.3.1", 22 | "@angular/router": "^3.3.1", 23 | "@ngrx/core": "^1.2.0", 24 | "@ngrx/effects": "^2.0.0", 25 | "@ngrx/router-store": "^1.2.5", 26 | "@ngrx/store": "^2.2.1", 27 | "@ngrx/store-devtools": "^3.2.3", 28 | "core-js": "^2.4.1", 29 | "immutability-helper": "^2.1.1", 30 | "reselect": "^2.5.4", 31 | "rxjs": "^5.0.1", 32 | "ts-helpers": "^1.1.1", 33 | "zone.js": "^0.7.2" 34 | }, 35 | "devDependencies": { 36 | "@angular/compiler-cli": "^2.3.1", 37 | "@types/jasmine": "2.5.38", 38 | "@types/node": "^6.0.42", 39 | "angular-cli": "1.0.0-beta.28.3", 40 | "codelyzer": "~2.0.0-beta.1", 41 | "jasmine-core": "2.5.2", 42 | "jasmine-spec-reporter": "2.5.0", 43 | "karma": "1.2.0", 44 | "karma-chrome-launcher": "^2.0.0", 45 | "karma-cli": "^1.0.1", 46 | "karma-jasmine": "^1.0.2", 47 | "karma-remap-istanbul": "^0.2.1", 48 | "protractor": "~4.0.13", 49 | "ts-node": "1.2.1", 50 | "tslint": "^4.3.0", 51 | "typescript": "~2.0.3" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | /*global jasmine */ 5 | var SpecReporter = require('jasmine-spec-reporter'); 6 | 7 | exports.config = { 8 | allScriptsTimeout: 11000, 9 | specs: [ 10 | './e2e/**/*.e2e-spec.ts' 11 | ], 12 | capabilities: { 13 | 'browserName': 'chrome' 14 | }, 15 | directConnect: true, 16 | baseUrl: 'http://localhost:4200/', 17 | framework: 'jasmine', 18 | jasmineNodeOpts: { 19 | showColors: true, 20 | defaultTimeoutInterval: 30000, 21 | print: function() {} 22 | }, 23 | useAllAngular2AppRoots: true, 24 | beforeLaunch: function() { 25 | require('ts-node').register({ 26 | project: 'e2e' 27 | }); 28 | }, 29 | onPrepare: function() { 30 | jasmine.getEnv().addReporter(new SpecReporter()); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Angular-2/c584709e30b08e9800e8629242e96672b60f7226/week3/1/ngrxTodoApp/src/app/app.component.css -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |

2 | {{title}} 3 |

4 | 5 | 6 | 7 | 8 | 9 | {{model.todos$ | async | json }} -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-unused-variable */ 2 | 3 | import { TestBed, async } from '@angular/core/testing'; 4 | import { AppComponent } from './app.component'; 5 | 6 | describe('AppComponent', () => { 7 | beforeEach(() => { 8 | TestBed.configureTestingModule({ 9 | declarations: [ 10 | AppComponent 11 | ], 12 | }); 13 | TestBed.compileComponents(); 14 | }); 15 | 16 | it('should create the app', async(() => { 17 | const fixture = TestBed.createComponent(AppComponent); 18 | const app = fixture.debugElement.componentInstance; 19 | expect(app).toBeTruthy(); 20 | })); 21 | 22 | it(`should have as title 'app works!'`, async(() => { 23 | const fixture = TestBed.createComponent(AppComponent); 24 | const app = fixture.debugElement.componentInstance; 25 | expect(app.title).toEqual('app works!'); 26 | })); 27 | 28 | it('should render title in a h1 tag', async(() => { 29 | const fixture = TestBed.createComponent(AppComponent); 30 | fixture.detectChanges(); 31 | const compiled = fixture.debugElement.nativeElement; 32 | expect(compiled.querySelector('h1').textContent).toContain('app works!'); 33 | })); 34 | }); 35 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { AppModel } from './store/app.model'; 3 | 4 | @Component({ 5 | selector: 'app-root', 6 | templateUrl: './app.component.html', 7 | styleUrls: ['./app.component.css'] 8 | }) 9 | export class AppComponent { 10 | title = 'app works!'; 11 | constructor(private model: AppModel) { 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { FormsModule } from '@angular/forms'; 4 | import { HttpModule } from '@angular/http'; 5 | 6 | import { StoreModule } from '@ngrx/store'; 7 | 8 | import { AppComponent } from './app.component'; 9 | import { TodoListComponent } from './todo-list/todo-list.component'; 10 | import { TodoEditComponent } from './todo-edit/todo-edit.component'; 11 | import { TodoFilterComponent } from './todo-filter/todo-filter.component'; 12 | import { todoReducer } from './store/app.reducer'; 13 | 14 | // import { appStore } from './store/index'; 15 | 16 | import { StoreDevtoolsModule } from '@ngrx/store-devtools'; 17 | import { AppModel } from './store/app.model'; 18 | 19 | @NgModule({ 20 | declarations: [ 21 | AppComponent, 22 | TodoListComponent, 23 | TodoEditComponent, 24 | TodoFilterComponent 25 | ], 26 | imports: [ 27 | BrowserModule, 28 | FormsModule, 29 | HttpModule, 30 | StoreModule.provideStore(todoReducer), 31 | StoreDevtoolsModule.instrumentOnlyWithExtension() 32 | ], 33 | providers: [ AppModel ], 34 | bootstrap: [AppComponent] 35 | }) 36 | export class AppModule { } 37 | 38 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/app/filter-state.enum.ts: -------------------------------------------------------------------------------- 1 | export enum FilterState { 2 | Default, 3 | CompletedOnly, 4 | All 5 | }; 6 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/app/store/app.actions.ts: -------------------------------------------------------------------------------- 1 | import { Action } from '@ngrx/store'; 2 | import { FilterState } from '../filter-state.enum'; 3 | import { Todo } from '../todo.model'; 4 | 5 | export const ActionTypes = { 6 | SAVE_TODO: '[App] Save Todo', 7 | TODO_SAVED: '[App] Todo Saved', 8 | TODO_TOGGLE: '[App] Todo Toggle', 9 | TODO_TOGGLED: '[App] Todo Toggled', 10 | SET_FILTER: '[App] Set Filter' 11 | }; 12 | 13 | export class SaveTodoAction implements Action { 14 | type = ActionTypes.SAVE_TODO; 15 | constructor(public payload: { todo: Todo }) {} 16 | } 17 | 18 | export class CompleteTodoAction implements Action { 19 | type = ActionTypes.TODO_TOGGLE; 20 | constructor(public payload: { id: number }) {} 21 | } 22 | 23 | export class SetFilterAction implements Action { 24 | type = ActionTypes.SET_FILTER; 25 | constructor(public payload: { filterState: FilterState }) {} 26 | } 27 | 28 | 29 | 30 | 31 | export class TodoSavedAction implements Action { 32 | type = ActionTypes.TODO_SAVED; 33 | constructor(public payload: { todo: Todo }) {} 34 | } 35 | 36 | export class TodoCompletedAction implements Action { 37 | type = ActionTypes.TODO_TOGGLED; 38 | constructor(public payload: { id: number }) {} 39 | } 40 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/app/store/app.model.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Store } from '@ngrx/store'; 3 | import { IAppState, todoReducer, getTodos, getFilterState, getFilteredTodos } from './app.reducer'; 4 | import { Todo } from '../todo.model'; 5 | import { Observable } from 'rxjs/Observable'; 6 | import { FilterState } from '../filter-state.enum'; 7 | import { SaveTodoAction, CompleteTodoAction, SetFilterAction } from './app.actions'; 8 | 9 | @Injectable() 10 | export class AppModel { 11 | todos$: Observable; 12 | filterState$: Observable; 13 | 14 | constructor(private _store: Store) { 15 | this.todos$ = this._store.select(s => getFilteredTodos(s)); 16 | this.filterState$ = this._store.select(s => getFilterState(s)); 17 | } 18 | 19 | addTodo(todo: Todo) { 20 | this._store.dispatch(new SaveTodoAction({ todo })); 21 | } 22 | 23 | toggleCompleted(todo: Todo) { 24 | this._store.dispatch(new CompleteTodoAction({ id: todo.id })); 25 | } 26 | 27 | setFilter(filterState: FilterState) { 28 | this._store.dispatch(new SetFilterAction({ filterState })); 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/app/store/app.reducer.ts: -------------------------------------------------------------------------------- 1 | import { ActionTypes } from './app.actions'; 2 | import { Todo } from '../todo.model'; 3 | import { FilterState } from '../filter-state.enum'; 4 | import { Action, combineReducers } from '@ngrx/store'; 5 | import { reducerFactory } from './reducer-factory'; 6 | import { routerReducer, RouterStoreModule } from '@ngrx/router-store'; 7 | 8 | import { createSelector } from 'reselect'; 9 | 10 | export interface IAppState { 11 | readonly todos?: Todo[]; 12 | readonly filterState?: FilterState; 13 | } 14 | 15 | export const initialState: IAppState = { 16 | todos: [], 17 | filterState: FilterState.Default 18 | }; 19 | 20 | export const map: { [key: string]: (payload: any, state: IAppState) => IAppState} = { 21 | [ActionTypes.SAVE_TODO]: function(payload: { todo: Todo }, state: IAppState) { 22 | const newState: IAppState = { 23 | todos: state.todos.concat(payload.todo) 24 | }; 25 | return Object.assign({}, state, newState); 26 | }, 27 | [ActionTypes.TODO_TOGGLE]: function(payload: { id: number }, state: IAppState) { 28 | const todosUpdated = state.todos.map(t => { 29 | if (t.id !== payload.id) return t; 30 | return Object.assign({}, t, { checked: !t.checked }); 31 | }); 32 | const newState: IAppState = { 33 | todos: todosUpdated 34 | }; 35 | return Object.assign({}, state, newState); 36 | }, 37 | [ActionTypes.SET_FILTER]: function(paylaod: { filterState: FilterState }, state: IAppState) { 38 | const newState: IAppState = { 39 | filterState: paylaod.filterState 40 | }; 41 | return Object.assign({}, state, newState); 42 | } 43 | }; 44 | 45 | export function todoReducer(state, action) { 46 | const todoReducer = reducerFactory(initialState, map); 47 | return todoReducer(state, action); 48 | // const customRouterReducer = function(theState, theAction) { 49 | // console.log(theAction, 'routerReducer'); 50 | // return routerReducer(theState, theAction); 51 | // }; 52 | // return combineReducers({ todo: todoReducer, router: customRouterReducer })(state, action); 53 | } 54 | 55 | 56 | export const getTodos = (state: IAppState) => state.todos; 57 | export const getFilterState = (state: IAppState) => state.filterState; 58 | 59 | export const getFilteredTodos = createSelector(getTodos, getFilterState, (todos: Todo[], filterState: FilterState) => { 60 | let result; 61 | switch (filterState) { 62 | case FilterState.Default: 63 | result = todos.filter(t => !t.checked); 64 | break; 65 | case FilterState.CompletedOnly: 66 | result = todos.filter(t => t.checked); 67 | break; 68 | case FilterState.All: 69 | result = todos; 70 | break; 71 | } 72 | return result; 73 | }); 74 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/app/store/index.ts: -------------------------------------------------------------------------------- 1 | import { todoReducer } from './app.reducer'; 2 | 3 | export function appStore(state, action) { 4 | return { 5 | todo: todoReducer(state, action) 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/app/store/reducer-factory.ts: -------------------------------------------------------------------------------- 1 | import { Action } from '@ngrx/store'; 2 | 3 | export function reducerFactory(initialState: State, map: { [key: string]: (payload: any, state: State) => State } ) { 4 | return function reducer(state = initialState, action: Action) { 5 | return map[action.type] && map[action.type](action.payload, state) || state; 6 | }; 7 | }; 8 | 9 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/app/todo-edit/todo-edit.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Angular-2/c584709e30b08e9800e8629242e96672b60f7226/week3/1/ngrxTodoApp/src/app/todo-edit/todo-edit.component.css -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/app/todo-edit/todo-edit.component.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/app/todo-edit/todo-edit.component.spec.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-unused-variable */ 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | import { By } from '@angular/platform-browser'; 4 | import { DebugElement } from '@angular/core'; 5 | 6 | import { TodoEditComponent } from './todo-edit.component'; 7 | 8 | describe('TodoEditComponent', () => { 9 | let component: TodoEditComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async(() => { 13 | TestBed.configureTestingModule({ 14 | declarations: [ TodoEditComponent ] 15 | }) 16 | .compileComponents(); 17 | })); 18 | 19 | beforeEach(() => { 20 | fixture = TestBed.createComponent(TodoEditComponent); 21 | component = fixture.componentInstance; 22 | fixture.detectChanges(); 23 | }); 24 | 25 | it('should create', () => { 26 | expect(component).toBeTruthy(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/app/todo-edit/todo-edit.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { AppModel } from '../store/app.model'; 3 | import { Todo } from '../todo.model'; 4 | 5 | @Component({ 6 | selector: 'app-todo-edit', 7 | templateUrl: './todo-edit.component.html', 8 | styleUrls: ['./todo-edit.component.css'] 9 | }) 10 | export class TodoEditComponent implements OnInit { 11 | 12 | constructor(private appModel: AppModel) { } 13 | 14 | ngOnInit() { 15 | } 16 | 17 | addTodo(text: string) { 18 | this.appModel.addTodo(new Todo(text)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/app/todo-filter/todo-filter.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Angular-2/c584709e30b08e9800e8629242e96672b60f7226/week3/1/ngrxTodoApp/src/app/todo-filter/todo-filter.component.css -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/app/todo-filter/todo-filter.component.html: -------------------------------------------------------------------------------- 1 |
    2 |
  • Default
  • 3 |
  • CompletedOnly
  • 4 |
  • All
  • 5 |
6 | {{ model.filterState$ | async }} -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/app/todo-filter/todo-filter.component.spec.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-unused-variable */ 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | import { By } from '@angular/platform-browser'; 4 | import { DebugElement } from '@angular/core'; 5 | 6 | import { TodoFilterComponent } from './todo-filter.component'; 7 | 8 | describe('TodoFilterComponent', () => { 9 | let component: TodoFilterComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async(() => { 13 | TestBed.configureTestingModule({ 14 | declarations: [ TodoFilterComponent ] 15 | }) 16 | .compileComponents(); 17 | })); 18 | 19 | beforeEach(() => { 20 | fixture = TestBed.createComponent(TodoFilterComponent); 21 | component = fixture.componentInstance; 22 | fixture.detectChanges(); 23 | }); 24 | 25 | it('should create', () => { 26 | expect(component).toBeTruthy(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/app/todo-filter/todo-filter.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { AppModel } from '../store/app.model'; 3 | import { FilterState } from '../filter-state.enum'; 4 | 5 | @Component({ 6 | selector: 'app-todo-filter', 7 | templateUrl: './todo-filter.component.html', 8 | styleUrls: ['./todo-filter.component.css'] 9 | }) 10 | export class TodoFilterComponent implements OnInit { 11 | 12 | constructor(private model: AppModel) { } 13 | 14 | ngOnInit() { 15 | } 16 | 17 | setFilterState(filterState: FilterState) { 18 | this.model.setFilter(filterState); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/app/todo-list/todo-list.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Angular-2/c584709e30b08e9800e8629242e96672b60f7226/week3/1/ngrxTodoApp/src/app/todo-list/todo-list.component.css -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/app/todo-list/todo-list.component.html: -------------------------------------------------------------------------------- 1 |
    2 |
  • 3 | {{todo.text}} 4 | 5 |
  • 6 |
7 | 8 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/app/todo-list/todo-list.component.spec.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-unused-variable */ 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | import { By } from '@angular/platform-browser'; 4 | import { DebugElement } from '@angular/core'; 5 | 6 | import { TodoListComponent } from './todo-list.component'; 7 | 8 | describe('TodoListComponent', () => { 9 | let component: TodoListComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async(() => { 13 | TestBed.configureTestingModule({ 14 | declarations: [ TodoListComponent ] 15 | }) 16 | .compileComponents(); 17 | })); 18 | 19 | beforeEach(() => { 20 | fixture = TestBed.createComponent(TodoListComponent); 21 | component = fixture.componentInstance; 22 | fixture.detectChanges(); 23 | }); 24 | 25 | it('should create', () => { 26 | expect(component).toBeTruthy(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/app/todo-list/todo-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { AppModel } from '../store/app.model'; 3 | import { Observable } from 'rxjs/Observable'; 4 | import { Todo } from '../todo.model'; 5 | 6 | @Component({ 7 | selector: 'app-todo-list', 8 | templateUrl: './todo-list.component.html', 9 | styleUrls: ['./todo-list.component.css'] 10 | }) 11 | export class TodoListComponent implements OnInit { 12 | todos$: Observable; 13 | 14 | constructor(private appModel: AppModel) { 15 | this.todos$ = appModel.todos$; 16 | } 17 | 18 | ngOnInit() { 19 | } 20 | 21 | toggleTodo(todo: Todo) { 22 | this.appModel.toggleCompleted(todo); 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/app/todo.model.ts: -------------------------------------------------------------------------------- 1 | let lastTodoId = 0; 2 | 3 | export class Todo { 4 | id: number; 5 | checked: boolean = false; 6 | 7 | constructor(private text: string) { 8 | this.id = ++lastTodoId; 9 | } 10 | 11 | }; 12 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Angular-2/c584709e30b08e9800e8629242e96672b60f7226/week3/1/ngrxTodoApp/src/assets/.gitkeep -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false 8 | }; 9 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackBulgaria/Angular-2/c584709e30b08e9800e8629242e96672b60f7226/week3/1/ngrxTodoApp/src/favicon.ico -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NgrxTodoApp 6 | 7 | 8 | 9 | 10 | 11 | 12 | Loading... 13 | 14 | 15 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | import { enableProdMode } from '@angular/core'; 3 | import { environment } from './environments/environment'; 4 | import { AppModule } from './app/app.module'; 5 | 6 | if (environment.production) { 7 | enableProdMode(); 8 | } 9 | 10 | platformBrowserDynamic().bootstrapModule(AppModule); 11 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | // This file includes polyfills needed by Angular and is loaded before the app. 2 | // You can add your own extra polyfills to this file. 3 | import 'core-js/es6/symbol'; 4 | import 'core-js/es6/object'; 5 | import 'core-js/es6/function'; 6 | import 'core-js/es6/parse-int'; 7 | import 'core-js/es6/parse-float'; 8 | import 'core-js/es6/number'; 9 | import 'core-js/es6/math'; 10 | import 'core-js/es6/string'; 11 | import 'core-js/es6/date'; 12 | import 'core-js/es6/array'; 13 | import 'core-js/es6/regexp'; 14 | import 'core-js/es6/map'; 15 | import 'core-js/es6/set'; 16 | import 'core-js/es6/reflect'; 17 | 18 | import 'core-js/es7/reflect'; 19 | import 'zone.js/dist/zone'; 20 | 21 | // If you need to support the browsers/features below, uncomment the import 22 | // and run `npm install import-name-here'; 23 | // Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 24 | 25 | // Needed for: IE9 26 | // import 'classlist.js'; 27 | 28 | // Animations 29 | // Needed for: All but Chrome and Firefox, Not supported in IE9 30 | // import 'web-animations-js'; 31 | 32 | // Date, currency, decimal and percent pipes 33 | // Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 34 | // import 'intl'; 35 | 36 | // NgClass on SVG elements 37 | // Needed for: IE10, IE11 38 | // import 'classlist.js'; 39 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/long-stack-trace-zone'; 4 | import 'zone.js/dist/proxy.js'; 5 | import 'zone.js/dist/sync-test'; 6 | import 'zone.js/dist/jasmine-patch'; 7 | import 'zone.js/dist/async-test'; 8 | import 'zone.js/dist/fake-async-test'; 9 | import { getTestBed } from '@angular/core/testing'; 10 | import { 11 | BrowserDynamicTestingModule, 12 | platformBrowserDynamicTesting 13 | } from '@angular/platform-browser-dynamic/testing'; 14 | 15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. 16 | declare var __karma__: any; 17 | declare var require: any; 18 | 19 | // Prevent Karma from running prematurely. 20 | __karma__.loaded = function () {}; 21 | 22 | // First, initialize the Angular testing environment. 23 | getTestBed().initTestEnvironment( 24 | BrowserDynamicTestingModule, 25 | platformBrowserDynamicTesting() 26 | ); 27 | // Then we find all the tests. 28 | const context = require.context('./', true, /\.spec\.ts$/); 29 | // And load the modules. 30 | context.keys().map(context); 31 | // Finally, start Karma to run the tests. 32 | __karma__.start(); 33 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "", 4 | "declaration": false, 5 | "emitDecoratorMetadata": true, 6 | "experimentalDecorators": true, 7 | "lib": ["es6", "dom"], 8 | "mapRoot": "./", 9 | "module": "es6", 10 | "moduleResolution": "node", 11 | "outDir": "../dist/out-tsc", 12 | "sourceMap": true, 13 | "target": "es5", 14 | "typeRoots": [ 15 | "../node_modules/@types" 16 | ] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /week3/1/ngrxTodoApp/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "callable-types": true, 7 | "class-name": true, 8 | "comment-format": [ 9 | true, 10 | "check-space" 11 | ], 12 | "curly": false, 13 | "eofline": true, 14 | "forin": true, 15 | "import-blacklist": [true, "rxjs"], 16 | "import-spacing": true, 17 | "indent": [ 18 | true, 19 | "spaces" 20 | ], 21 | "interface-over-type-literal": true, 22 | "label-position": true, 23 | "max-line-length": [ 24 | true, 25 | 140 26 | ], 27 | "member-access": false, 28 | "member-ordering": [ 29 | true, 30 | "static-before-instance", 31 | "variables-before-functions" 32 | ], 33 | "no-arg": true, 34 | "no-bitwise": true, 35 | "no-console": [ 36 | true, 37 | "debug", 38 | "info", 39 | "time", 40 | "timeEnd", 41 | "trace" 42 | ], 43 | "no-construct": true, 44 | "no-debugger": true, 45 | "no-duplicate-variable": true, 46 | "no-empty": false, 47 | "no-empty-interface": true, 48 | "no-eval": true, 49 | "no-inferrable-types": false, 50 | "no-shadowed-variable": true, 51 | "no-string-literal": false, 52 | "no-string-throw": true, 53 | "no-switch-case-fall-through": true, 54 | "no-trailing-whitespace": true, 55 | "no-unused-expression": true, 56 | "no-use-before-declare": true, 57 | "no-var-keyword": true, 58 | "object-literal-sort-keys": false, 59 | "one-line": [ 60 | true, 61 | "check-open-brace", 62 | "check-catch", 63 | "check-else", 64 | "check-whitespace" 65 | ], 66 | "prefer-const": true, 67 | "quotemark": [ 68 | true, 69 | "single" 70 | ], 71 | "radix": true, 72 | "semicolon": [ 73 | "always" 74 | ], 75 | "triple-equals": [ 76 | true, 77 | "allow-null-check" 78 | ], 79 | "typedef-whitespace": [ 80 | true, 81 | { 82 | "call-signature": "nospace", 83 | "index-signature": "nospace", 84 | "parameter": "nospace", 85 | "property-declaration": "nospace", 86 | "variable-declaration": "nospace" 87 | } 88 | ], 89 | "typeof-compare": true, 90 | "unified-signatures": true, 91 | "variable-name": false, 92 | "whitespace": [ 93 | true, 94 | "check-branch", 95 | "check-decl", 96 | "check-operator", 97 | "check-separator", 98 | "check-type" 99 | ], 100 | 101 | "directive-selector": [true, "attribute", "app", "camelCase"], 102 | "component-selector": [true, "element", "app", "kebab-case"], 103 | "use-input-property-decorator": true, 104 | "use-output-property-decorator": true, 105 | "use-host-property-decorator": true, 106 | "no-input-rename": true, 107 | "no-output-rename": true, 108 | "use-life-cycle-interface": true, 109 | "use-pipe-transform-interface": true, 110 | "component-class-suffix": true, 111 | "directive-class-suffix": true, 112 | "no-access-missing-member": true, 113 | "templates-use-public": true, 114 | "invoke-injectable": true 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /week3/2/README.md: -------------------------------------------------------------------------------- 1 | # Week 3.2 2 | 3 | ### Lecture 4 | 5 | ### Tasks: 6 | 7 | (Use the pager and grid from [here](https://github.com/HackBulgaria/Angular-2/tree/master/week2/1/tasks/src/app/directives). Use ContentChild and ViewChild to solve the tasks bellow) 8 | 9 | 1. Modify the grid component so it can present a custom header and footer that the user provided. 10 | 2. Modify the grid component and the pager to be able to use a custom template provided by the user and remove the default one. This will allow the user to control how the data is displayed. 11 | 3. Create search input that will filter the results showin in the grid dynamically. Use ```Observable.fromEvent(nativeElement, 'keyup').``` Don't forget to throttle the events. (Use [ElementRef](https://angular.io/docs/js/latest/api/core/index/ElementRef-class.html) to get the input element) 12 | 4. Modify the app to use redux (ngrx) 13 | 5. Use ngrx/effects to dispatch an action that will fetch the data from this [API](https://jsonplaceholder.typicode.com/), put it into the store and show it to the user. -------------------------------------------------------------------------------- /week4/1/README.md: -------------------------------------------------------------------------------- 1 | # Week 4.1 2 | 3 | ### Lecture 4 | [click here](https://speakerdeck.com/iliaidakiev/7-forms-and-simple-validations) 5 | 6 | ### Tasks 7 | 8 | * Try using ngrx/store to export all your state and ngrx/effects for the (fake) server calls. 9 | * Use [ng-bootstrap](https://ng-bootstrap.github.io/#/home) to create the dialogs. 10 | * Create a [core module](https://angular.io/docs/ts/latest/guide/style-guide.html#!#04-11) that will contain all the single use components. 11 | * Consider using [combineReducer](https://github.com/ngrx/ngrx.github.io/blob/master/store/api/combine_reducers.md) to separate the different stores (auth store that will contain the loggedUser and todo store). 12 | 13 | 1. Create a reactive signup form for our todoApp from last time. We are going to need the first name, last name, email, password and a confirmation password field, address, city, post code (create a form group for address, city, post code). If the form is submitted and there are any errors in the form display an error message. 14 | 15 | 2. Create a fake auth service that will receive the input data from the form and simulate an ajax call (use the [localStorage](https://developer.mozilla.org/en/docs/Web/API/Window/localStorage) to save the registered users). After a few seconds return a response with { success: true } if the user is registered successfuly and { success: false } otherwise (if the email is already registered). 16 | 17 | 3. Create a login popup using the template driven approach. If the form is invalid and submitted show an error message. 18 | 19 | 4. Create an async login method on the auth service and check if the user exists and if so check if the passwords are matching. Return a proper response message and if the user is logged in successfully show the todoApp. 20 | 21 | -------------------------------------------------------------------------------- /week4/2/README.md: -------------------------------------------------------------------------------- 1 | # Week 4.2 2 | 3 | ### Lecture 4 | [Click here](https://speakerdeck.com/iliaidakiev/8-custom-validations-controls-and-multi-providers) 5 | ### Tasks 6 | 1. Finish the tasks from last time 7 | 2. Create a custom password validation for the registration form. Password should contain 1 specail symbol (*, _, &). 8 | 3. Create a custom async model driven validation to check if the provided email in the registration form is not already registered. 9 | 4. Create a custom async template driven validation to check if the provided email in the login is registered. 10 | 5. Create a custom clock form control that will be used for setting the time of the todo. 11 | -------------------------------------------------------------------------------- /week5/1/README.md: -------------------------------------------------------------------------------- 1 | # Week 5.1 2 | 3 | ### Lecture 4 | [Click here](https://speakerdeck.com/iliaidakiev/9-routing-and-navigation) 5 | ### Tasks 6 | 7 | 1. Use the application from last time and create a view where all the registered users are listed. 8 | 2. Create a UserEditComponent where the users can be edited (use a route param :id, get the user id using activatedRoute snapshot and get the user from the store) 9 | 3. Create a default 404 component that will handle not found routes. 10 | 3. Separate the user components into a different module - user.module. Create child routes for the components and use LazyLoading to load the module when the user clicks on the navigation link. 11 | 4. Use the preload strategies to preload the users module after the application is bootstrapped. -------------------------------------------------------------------------------- /week5/2/README.md: -------------------------------------------------------------------------------- 1 | # Week 5.2 2 | 3 | ### Lecture 4 | [Click here](https://speakerdeck.com/iliaidakiev/10-routing-and-navigation-2) 5 | 6 | ### Tasks 7 | 8 | 1. Create a pipe called time that gets applied to a string that represents time (e.g. 16:45) and takes an optional number argument - format ( 12, 24 ). Depending on the format the pipe will convert the time (e.g. 16:45 | time: 12 -> 4:45 pm) and make this as a setting for our todo application. 9 | 2. Create a CanDeactivate guard that checks if there is any data written on the add todo form when the user is trying to navigate elsewhere. If there is show a confirmation popup. If the user clicks 'yes' proceed with the CanDeactivate procedure if 'no' do nothing. Use a named outlet to show the popup. --------------------------------------------------------------------------------