├── .gitignore ├── README.md ├── WebApi ├── .gitignore ├── README.md ├── app.js ├── app.ts ├── launchserver.bat ├── package-lock.json ├── package.json └── tsconfig.json ├── WebApi_deno ├── README.md ├── server.ts └── src │ ├── apis │ └── employeeApis.ts │ ├── model │ └── employee.ts │ └── routes.ts ├── final └── ngTvdTraining │ ├── .browserslistrc │ ├── .editorconfig │ ├── .gitignore │ ├── README.md │ ├── angular.json │ ├── e2e │ ├── app.e2e-spec.ts │ ├── app.po.ts │ └── tsconfig.e2e.json │ ├── karma.conf.js │ ├── package-lock.json │ ├── package.json │ ├── protractor.conf.js │ ├── screenshot-1.png │ ├── screenshot-2.png │ ├── screenshot-3.png │ ├── screenshot-4.png │ ├── screenshot-5.png │ ├── screenshot-6.png │ ├── src │ ├── app │ │ ├── about │ │ │ ├── about-routing.module.ts │ │ │ ├── about.module.ts │ │ │ └── containers │ │ │ │ └── about-list │ │ │ │ ├── about-list.component.html │ │ │ │ └── about-list.component.ts │ │ ├── app-routing.module.ts │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── core │ │ │ ├── app-custom-preloader.ts │ │ │ ├── core.module.ts │ │ │ └── super-service.ts │ │ ├── employee │ │ │ ├── components │ │ │ │ ├── employee-form │ │ │ │ │ ├── employee-form.component.html │ │ │ │ │ ├── employee-form.component.scss │ │ │ │ │ └── employee-form.component.ts │ │ │ │ └── index.ts │ │ │ ├── containers │ │ │ │ ├── employee-list │ │ │ │ │ ├── employee-list.component.html │ │ │ │ │ ├── employee-list.component.spec.ts │ │ │ │ │ └── employee-list.component.ts │ │ │ │ ├── employee │ │ │ │ │ ├── employee.component.html │ │ │ │ │ └── employee.component.ts │ │ │ │ └── index.ts │ │ │ ├── employee-routing.module.ts │ │ │ ├── employee.module.ts │ │ │ ├── guards │ │ │ │ ├── employee-edit.guard.ts │ │ │ │ ├── employee.guard.ts │ │ │ │ └── index.ts │ │ │ ├── model │ │ │ │ └── employee.model.ts │ │ │ ├── pipes │ │ │ │ ├── employee-filter.pipe.spec.ts │ │ │ │ ├── employee-filter.pipe.ts │ │ │ │ └── index.ts │ │ │ ├── services │ │ │ │ ├── employee.service.spec.ts │ │ │ │ ├── employee.service.ts │ │ │ │ └── index.ts │ │ │ ├── store │ │ │ │ ├── actions │ │ │ │ │ ├── employee.action.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── effects │ │ │ │ │ ├── employee.effect.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ ├── reducers │ │ │ │ │ ├── employee.reducer.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── selectors │ │ │ │ │ ├── employee.selector.ts │ │ │ │ │ └── index.ts │ │ │ │ └── state │ │ │ │ │ ├── employee.state.ts │ │ │ │ │ └── index.ts │ │ │ └── validators │ │ │ │ ├── check-email-match.validator.ts │ │ │ │ └── employee.validator.ts │ │ ├── layout │ │ │ ├── layout.module.ts │ │ │ ├── standard-layout │ │ │ │ ├── standard-layout.component.html │ │ │ │ └── standard-layout.component.ts │ │ │ └── welcome │ │ │ │ ├── welcome.component.html │ │ │ │ └── welcome.component.ts │ │ ├── shared │ │ │ ├── auth.interceptor.ts │ │ │ ├── directives │ │ │ │ ├── hotkey.directive.ts │ │ │ │ ├── index.ts │ │ │ │ └── select.directive.ts │ │ │ ├── http-error.interceptor.ts │ │ │ └── shared.module.ts │ │ └── store │ │ │ ├── actions │ │ │ ├── index.ts │ │ │ └── router.action.ts │ │ │ ├── effects │ │ │ ├── index.ts │ │ │ └── router.effect.ts │ │ │ ├── index.ts │ │ │ ├── reducers │ │ │ ├── index.ts │ │ │ └── router.reducer.ts │ │ │ └── state │ │ │ └── app.state.ts │ ├── assets │ │ └── .gitkeep │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ ├── polyfills.ts │ ├── styles.scss │ ├── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.spec.json │ └── typings.d.ts │ ├── tsconfig.json │ └── tslint.json ├── finalOhneNgRx └── ngTvdTraining │ ├── .browserslistrc │ ├── .editorconfig │ ├── .gitignore │ ├── README.md │ ├── angular.json │ ├── e2e │ ├── app.e2e-spec.ts │ ├── app.po.ts │ └── tsconfig.e2e.json │ ├── karma.conf.js │ ├── package-lock.json │ ├── package.json │ ├── projects │ └── auto-focus │ │ ├── README.md │ │ ├── karma.conf.js │ │ ├── ng-package.json │ │ ├── package.json │ │ ├── src │ │ ├── lib │ │ │ ├── auto-focus.directive.ts │ │ │ └── auto-focus.module.ts │ │ ├── public-api.ts │ │ └── test.ts │ │ ├── tsconfig.lib.json │ │ ├── tsconfig.lib.prod.json │ │ ├── tsconfig.spec.json │ │ └── tslint.json │ ├── protractor.conf.js │ ├── screenshot-1.png │ ├── screenshot-2.png │ ├── screenshot-3.png │ ├── screenshot-4.png │ ├── screenshot-5.png │ ├── screenshot-6.png │ ├── src │ ├── app │ │ ├── about │ │ │ ├── about-routing.module.ts │ │ │ ├── about.module.ts │ │ │ └── containers │ │ │ │ └── about-list │ │ │ │ ├── about-list.component.html │ │ │ │ └── about-list.component.ts │ │ ├── app-routing.module.ts │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── core │ │ │ ├── app-custom-preloader.ts │ │ │ ├── core.module.ts │ │ │ └── super-service.ts │ │ ├── employee │ │ │ ├── components │ │ │ │ ├── employee-form │ │ │ │ │ ├── employee-form.component.html │ │ │ │ │ ├── employee-form.component.scss │ │ │ │ │ └── employee-form.component.ts │ │ │ │ └── index.ts │ │ │ ├── containers │ │ │ │ ├── employee-list │ │ │ │ │ ├── employee-list.component.html │ │ │ │ │ ├── employee-list.component.spec.ts │ │ │ │ │ └── employee-list.component.ts │ │ │ │ ├── employee │ │ │ │ │ ├── employee.component.html │ │ │ │ │ └── employee.component.ts │ │ │ │ └── index.ts │ │ │ ├── employee-routing.module.ts │ │ │ ├── employee.module.ts │ │ │ ├── guards │ │ │ │ ├── employee-edit.guard.ts │ │ │ │ └── index.ts │ │ │ ├── model │ │ │ │ ├── device.model.ts │ │ │ │ └── employee.model.ts │ │ │ ├── pipes │ │ │ │ ├── employee-filter.pipe.spec.ts │ │ │ │ ├── employee-filter.pipe.ts │ │ │ │ └── index.ts │ │ │ ├── services │ │ │ │ ├── device.service.ts │ │ │ │ ├── employee.service.spec.ts │ │ │ │ ├── employee.service.ts │ │ │ │ └── index.ts │ │ │ └── validators │ │ │ │ ├── check-email-match.validator.ts │ │ │ │ └── employee.validator.ts │ │ ├── layout │ │ │ ├── layout.module.ts │ │ │ ├── standard-layout │ │ │ │ ├── standard-layout.component.html │ │ │ │ └── standard-layout.component.ts │ │ │ └── welcome │ │ │ │ ├── welcome.component.html │ │ │ │ └── welcome.component.ts │ │ └── shared │ │ │ ├── auth.interceptor.ts │ │ │ ├── directives │ │ │ ├── hotkey.directive.ts │ │ │ ├── index.ts │ │ │ └── select.directive.ts │ │ │ ├── http-error.interceptor.ts │ │ │ └── shared.module.ts │ ├── assets │ │ └── .gitkeep │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ ├── polyfills.ts │ ├── styles.scss │ ├── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.spec.json │ └── typings.d.ts │ ├── tsconfig.json │ └── tslint.json └── starter └── ngTvdTraining ├── .browserslistrc ├── .editorconfig ├── .gitignore ├── README.md ├── angular.json ├── e2e ├── app.e2e-spec.ts ├── app.po.ts └── tsconfig.e2e.json ├── karma.conf.js ├── package-lock.json ├── package.json ├── protractor.conf.js ├── src ├── app │ ├── about │ │ ├── about-routing.module.ts │ │ ├── about.module.ts │ │ └── containers │ │ │ └── about-list │ │ │ ├── about-list.component.html │ │ │ └── about-list.component.ts │ ├── app-routing.module.ts │ ├── app.component.spec.ts │ ├── app.component.ts │ ├── app.module.ts │ ├── core │ │ ├── core.module.ts │ │ └── super-service.ts │ ├── employee │ │ ├── components │ │ │ ├── employee-form │ │ │ │ ├── employee-form.component.html │ │ │ │ ├── employee-form.component.scss │ │ │ │ └── employee-form.component.ts │ │ │ └── index.ts │ │ ├── containers │ │ │ ├── employee-list │ │ │ │ ├── employee-list.component.html │ │ │ │ └── employee-list.component.ts │ │ │ ├── employee │ │ │ │ ├── employee.component.html │ │ │ │ └── employee.component.ts │ │ │ └── index.ts │ │ ├── guards │ │ │ └── index.ts │ │ ├── model │ │ │ ├── device.model.ts │ │ │ └── employee.model.ts │ │ ├── pipes │ │ │ └── index.ts │ │ ├── services │ │ │ ├── device.service.ts │ │ │ ├── employee.service.ts │ │ │ └── index.ts │ │ └── validators │ │ │ ├── check-email-match.validator.ts │ │ │ └── employee.validator.ts │ ├── layout │ │ ├── layout.module.ts │ │ ├── standard-layout │ │ │ ├── standard-layout.component.html │ │ │ └── standard-layout.component.ts │ │ └── welcome │ │ │ ├── welcome.component.html │ │ │ └── welcome.component.ts │ └── shared │ │ ├── directives │ │ └── index.ts │ │ └── shared.module.ts ├── assets │ └── .gitkeep ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── main.ts ├── polyfills.ts ├── styles.scss ├── test.ts ├── tsconfig.app.json ├── tsconfig.spec.json └── typings.d.ts ├── tsconfig.json └── tslint.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | .vscode 7 | *.js.map 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # node-waf configuration 27 | .lock-wscript 28 | 29 | # Compiled binary addons (http://nodejs.org/api/addons.html) 30 | build/Release 31 | 32 | # Dependency directories 33 | node_modules 34 | jspm_packages 35 | 36 | # Optional npm cache directory 37 | .npm 38 | 39 | # Optional REPL history 40 | .node_repl_history 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Advanced Angular 2 | 3 | Contains projects for the Advanced Angular training at Trivadis: 4 | https://www.trivadis.com/de/training/advanced-angular-ad-angular-adv 5 | 6 | # Prerequisite 7 | 8 | 1. Visual Studio Code: https://code.visualstudio.com/Download 9 | 2. VS Code Trivadis Extension: https://marketplace.visualstudio.com/items?itemName=trivadis.ngtvd-extensions 10 | 3. Node.js und NPM: https://nodejs.org (LTS version (>12)) 11 | 4. Optional Git: https://git-scm.com/downloads 12 | 5. Clone this repository 13 | 14 | ``` 15 | $ git clone https://github.com/Trivadis/AdvancedAngular.git 16 | ``` 17 | -------------------------------------------------------------------------------- /WebApi/.gitignore: -------------------------------------------------------------------------------- 1 | app.js 2 | -------------------------------------------------------------------------------- /WebApi/README.md: -------------------------------------------------------------------------------- 1 | Before running "launchserver.bat", 2 | do a "npm install" for this directory -------------------------------------------------------------------------------- /WebApi/launchserver.bat: -------------------------------------------------------------------------------- 1 | start http://localhost:8180/api/employees 2 | node app.js -------------------------------------------------------------------------------- /WebApi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SimpleWebApi", 3 | "version": "1.0.0", 4 | "description": "Simple Web-API to use in sample projects", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Thomas Claudius Huber", 10 | "license": "ISC", 11 | "dependencies": { 12 | "body-parser": "^1.19.0", 13 | "express": "^4.17.1" 14 | }, 15 | "devDependencies": { 16 | "@types/express": "^4.17.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /WebApi_deno/README.md: -------------------------------------------------------------------------------- 1 | # 1. Install DENO 2 | 3 | 4 | ## Install via command 5 | 6 | **With Shell (Mac):** 7 | 8 | ```sh 9 | curl -fsSL https://deno.land/x/install/install.sh | sh 10 | ``` 11 | 12 | **With PowerShell (Windows):** 13 | 14 | ```powershell 15 | iwr https://deno.land/x/install/install.ps1 -useb | iex 16 | ``` 17 |
18 | 19 | ## Install via Package Manager 20 | 21 | **With [Homebrew](https://formulae.brew.sh/formula/deno) (Mac):** 22 | 23 | ```sh 24 | brew install deno 25 | ``` 26 | 27 | **With [Chocolatey](https://chocolatey.org/packages/deno) (Windows):** 28 | 29 | ```powershell 30 | choco install deno 31 | ``` 32 | 33 |
34 | 35 | Run `deno --version` in the shell to verify the installation. You should get a similar output: 36 | 37 | ```shell 38 | $ deno --version 39 | deno 1.0.3 40 | v8 8.4.300 41 | typescript 3.9.2 42 | ``` 43 | 44 | 45 |

46 | 47 | 48 | # 2. Run DENO server 49 | 50 | Open a shell/powershell terminal at the `server.ts` path and run the following command: 51 | 52 | ```powershell 53 | deno run --allow-net [PATH_TO_FILE]\server.ts 54 | ``` 55 | 56 |

57 | 58 | # Troubleshooting 59 | 60 | If the server is not responding to the requests, verify to use `DENO` endpoint (`apiBaseUrl`) in your `environments.ts` file: 61 | 62 | ```javascript 63 | export const environment = { 64 | production: false, 65 | 66 | // Use Node Express 67 | // apiBaseUrl: 'http://localhost:8180/api' 68 | 69 | // Use DENO 70 | apiBaseUrl: 'http://localhost:8280' <--- 71 | }; 72 | ``` -------------------------------------------------------------------------------- /WebApi_deno/server.ts: -------------------------------------------------------------------------------- 1 | import { Application } from "https://deno.land/x/oak/mod.ts"; 2 | import { oakCors } from "https://deno.land/x/cors/mod.ts"; 3 | import router from "./src/routes.ts"; 4 | 5 | const port = 8280; 6 | const app = new Application(); 7 | 8 | app.use(oakCors()); 9 | app.use(router.routes()); 10 | app.use(router.allowedMethods()); 11 | 12 | app.addEventListener("listen", ({ hostname, port, secure }) => { 13 | console.log(`--- Listening on: ${secure ? "https://" : "http://"}${ 14 | hostname ?? "localhost" 15 | }:${port} ---` 16 | ); 17 | 18 | console.log(`--> You can now navigate to http://localhost:8280/employees`); 19 | }); 20 | await app.listen({ port }); -------------------------------------------------------------------------------- /WebApi_deno/src/apis/employeeApis.ts: -------------------------------------------------------------------------------- 1 | import { EmployeeData, Employee } from './../model/employee.ts'; 2 | 3 | export const getAllEmployees = ({ response }: { response: any }) => { 4 | response.body = EmployeeData; 5 | }; 6 | 7 | export const getEmployeeById = ({ params, response }: { params: { id: string }; response: any }) => { 8 | const selectedEmployee: Employee | undefined = EmployeeData.find((employee) => 9 | employee.id === +params.id 10 | ); 11 | if (selectedEmployee) { 12 | response.status = 200; 13 | response.body = selectedEmployee; 14 | } 15 | else { 16 | response.status = 404; 17 | response.body = []; 18 | } 19 | }; 20 | 21 | export const addEmployee = async ( 22 | { request, response }: { request: any; response: any }, 23 | ) => { 24 | if (!request.hasBody) { 25 | response.status = 400; 26 | } else { 27 | const newEmployee: Employee = await request.body(); 28 | 29 | newEmployee.id = getNextEmployeeId(); 30 | EmployeeData.push(newEmployee); 31 | response.status = 201; 32 | } 33 | }; 34 | 35 | function getNextEmployeeId(): number { 36 | let maxId = 1; 37 | EmployeeData.forEach(p => { 38 | maxId = Math.max(p.id, maxId); 39 | }); 40 | return maxId + 1; 41 | } 42 | 43 | export const deleteEmployee = ( 44 | { params, response }: { params: { id: string }; response: any }, 45 | ) => { 46 | const targetId = +params.id; 47 | const newEmployeeList = EmployeeData.filter(x => x.id !== targetId); 48 | if (newEmployeeList.length < EmployeeData.length) { 49 | replaceCollection(EmployeeData, newEmployeeList); 50 | response.status = 200; 51 | } else { 52 | response.status = 404; 53 | } 54 | }; 55 | 56 | export const updateEmployee = async ( 57 | { params, request, response }: { 58 | params: { id: string }; 59 | request: any; 60 | response: any; 61 | }, 62 | ) => { 63 | const targetId = +params.id; 64 | let employeeToUpdate: Employee | undefined = EmployeeData.find((employee) => 65 | employee.id === targetId 66 | ); 67 | if (employeeToUpdate) { 68 | const body = await request.body(); 69 | const newEmployeeData: Employee = body.value; 70 | 71 | let updatedData = EmployeeData.map((e: Employee) => { 72 | return e.id === targetId ? { ...e, ...newEmployeeData } : e; 73 | }); 74 | 75 | replaceCollection(EmployeeData, updatedData); 76 | response.status = 200; 77 | } else { 78 | response.status = 404; 79 | } 80 | }; 81 | 82 | function replaceCollection(originalData: Employee[], newData: Employee[]) { 83 | originalData.splice(0, originalData.length); 84 | originalData.push(...newData); 85 | } -------------------------------------------------------------------------------- /WebApi_deno/src/model/employee.ts: -------------------------------------------------------------------------------- 1 | export interface Employee { 2 | id: number; 3 | firstname: string; 4 | lastname: string; 5 | email?: string; 6 | } 7 | 8 | export const EmployeeData: Employee[] = [ 9 | { id: 1, firstname: 'Max', lastname: 'Payne', email: 'max.payne@trivadis.com' }, 10 | { id: 2, firstname: 'Lara', lastname: 'Croft', email: 'lara.croft@trivadis.com' }, 11 | { id: 3, firstname: 'Thomas', lastname: 'Huber', email: 'thomas.huber@trivadis.com' }, 12 | { id: 5, firstname: 'Thomas', lastname: 'Gassmann', email: 'thomas.gassmann@trivadis.com' }, 13 | { id: 6, firstname: 'Francesco', lastname: 'Leardini', email: 'francesco.leardini@trivadis.com' }, 14 | ]; -------------------------------------------------------------------------------- /WebApi_deno/src/routes.ts: -------------------------------------------------------------------------------- 1 | import { Router } from "https://deno.land/x/oak/mod.ts"; 2 | import { 3 | getAllEmployees, getEmployeeById, updateEmployee, addEmployee, deleteEmployee 4 | } from "./apis/employeeApis.ts"; 5 | 6 | const router = new Router(); 7 | 8 | router.get("/employees", getAllEmployees) 9 | .get("/employees/:id", getEmployeeById) 10 | .put("/employees/:id", updateEmployee) 11 | .post("/employees", addEmployee) 12 | .delete("/employees/:id", deleteEmployee); 13 | 14 | export default router; -------------------------------------------------------------------------------- /final/ngTvdTraining/.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /final/ngTvdTraining/.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 | quote_type = single 11 | 12 | [*.md] 13 | max_line_length = off 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /final/ngTvdTraining/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /dist-server 6 | /tmp 7 | /out-tsc 8 | 9 | # dependencies 10 | /node_modules 11 | 12 | # IDEs and editors 13 | /.idea 14 | .project 15 | .classpath 16 | .c9/ 17 | *.launch 18 | .settings/ 19 | *.sublime-workspace 20 | 21 | # IDE - VSCode 22 | .vscode/* 23 | !.vscode/settings.json 24 | !.vscode/tasks.json 25 | !.vscode/launch.json 26 | !.vscode/extensions.json 27 | 28 | # misc 29 | /.sass-cache 30 | /connect.lock 31 | /coverage 32 | /libpeerconnection.log 33 | npm-debug.log 34 | yarn-error.log 35 | testem.log 36 | /typings 37 | 38 | # e2e 39 | /e2e/*.js 40 | /e2e/*.map 41 | 42 | # System Files 43 | .DS_Store 44 | Thumbs.db 45 | -------------------------------------------------------------------------------- /final/ngTvdTraining/README.md: -------------------------------------------------------------------------------- 1 | # Advanced Angular 2 | 3 | Contains the final project (with NgRx) for the Advanced Angular training at Trivadis: 4 | https://www.trivadis.com/de/training/advanced-angular-ad-angular-adv 5 | 6 | ``` 7 | https://m.trivadis.com/angular-schulung 8 | ``` 9 | -------------------------------------------------------------------------------- /final/ngTvdTraining/e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('employee-portal App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo('/'); 12 | expect(page.getText('app-root h2')).toEqual('Employee Portal'); 13 | }); 14 | 15 | it('should navigate to employee page', () => { 16 | page.navigateTo('/'); 17 | 18 | page.getElement('[ng-reflect-router-link="/employees"]').click(); 19 | 20 | expect(page.getCurrentUrl()).toContain('/employees'); 21 | }); 22 | 23 | it('should search correct', () => { 24 | page.navigateTo('/employees'); 25 | 26 | page.search('Thomas'); 27 | 28 | expect(page.getResult().count()).toEqual(3); 29 | }); 30 | 31 | it('should edit a person correctly', () => { 32 | page.navigateTo('/employees'); 33 | page.takeScreenshot(1); 34 | 35 | page.getElement('table a.btn.btn-ok').click(); 36 | page.takeScreenshot(2); 37 | 38 | const inputField = page.getElement('[formcontrolname="lastname"]'); 39 | inputField.clear(); 40 | page.takeScreenshot(3); 41 | 42 | inputField.sendKeys('Wayne'); 43 | page.takeScreenshot(4); 44 | 45 | const email = page.getElement('[formcontrolname="email"]').getAttribute('value'); 46 | page.getElement('[formcontrolname="emailConfirm"]').sendKeys(email); 47 | page.takeScreenshot(5); 48 | 49 | page.getElement('button.btn.btn-ok').click(); 50 | // page.acceptAlert(); 51 | page.takeScreenshot(6); 52 | 53 | expect(page.getLastname(0)).toEqual('Wayne'); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /final/ngTvdTraining/e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | import * as fs from 'fs'; 3 | 4 | export class AppPage { 5 | navigateTo(url: string) { 6 | return browser.get(url); 7 | } 8 | 9 | getElement(selector: string) { 10 | return element(by.css(selector)); 11 | } 12 | 13 | getText(selector: string) { 14 | return this.getElement(selector).getText(); 15 | } 16 | 17 | getCurrentUrl() { 18 | return browser.getCurrentUrl(); 19 | } 20 | 21 | search(search: string) { 22 | this.getElement('input[type="search"]').clear(); 23 | this.getElement('input[type="search"]').sendKeys(search); 24 | } 25 | 26 | getResult() { 27 | return element.all(by.css('table.table tbody tr')); 28 | } 29 | 30 | takeScreenshot(idx: number) { 31 | return browser.takeScreenshot().then(png => { 32 | const stream = fs.createWriteStream('screenshot-' + idx + '.png'); 33 | stream.write(new Buffer(png, 'base64')); 34 | stream.end(); 35 | }); 36 | } 37 | 38 | getLastname(idx: number) { 39 | return element(by.css('table.table tbody tr td:nth-child(3)')).getText(); 40 | } 41 | 42 | acceptAlert() { 43 | browser 44 | .switchTo() 45 | .alert() 46 | .accept(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /final/ngTvdTraining/e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": [ 9 | "jasmine", 10 | "jasminewd2", 11 | "node" 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /final/ngTvdTraining/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-firefox-launcher'), 12 | require('karma-jasmine-html-reporter'), 13 | require('karma-coverage-istanbul-reporter'), 14 | require('@angular-devkit/build-angular/plugins/karma') 15 | ], 16 | client:{ 17 | clearContext: false // leave Jasmine Spec Runner output visible in browser 18 | }, 19 | coverageIstanbulReporter: { 20 | dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ], 21 | fixWebpackSourcePaths: true 22 | }, 23 | 24 | reporters: ['progress', 'kjhtml'], 25 | port: 9876, 26 | colors: true, 27 | logLevel: config.LOG_INFO, 28 | autoWatch: true, 29 | browsers: ['Chrome', 'Firefox'], 30 | singleRun: false 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /final/ngTvdTraining/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ng-tvd-training", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "ng": "ng", 7 | "start": "ng serve", 8 | "build": "ng build --prod", 9 | "test": "ng test", 10 | "lint": "ng lint", 11 | "e2e": "ng e2e" 12 | }, 13 | "private": true, 14 | "dependencies": { 15 | "@angular/animations": "11.0.3", 16 | "@angular/common": "11.0.3", 17 | "@angular/compiler": "11.0.3", 18 | "@angular/core": "11.0.3", 19 | "@angular/forms": "11.0.3", 20 | "@angular/platform-browser": "11.0.3", 21 | "@angular/platform-browser-dynamic": "11.0.3", 22 | "@angular/router": "11.0.3", 23 | "@ngrx/effects": "10.1.0", 24 | "@ngrx/router-store": "10.1.0", 25 | "@ngrx/schematics": "^10.1.0", 26 | "@ngrx/store": "10.1.0", 27 | "@ngrx/store-devtools": "10.1.0", 28 | "bootstrap": "4.5.3", 29 | "rxjs": "6.6.3", 30 | "tslib": "^2.0.0", 31 | "zone.js": "~0.11.3" 32 | }, 33 | "devDependencies": { 34 | "@angular-devkit/build-angular": "~0.1100.3", 35 | "@angular/cli": "~11.0.3", 36 | "@angular/compiler-cli": "~11.0.3", 37 | "@angular/language-service": "~11.0.3", 38 | "@types/node": "^12.11.1", 39 | "@types/jasmine": "~3.6.0", 40 | "@types/jasminewd2": "~2.0.6", 41 | "codelyzer": "^6.0.0", 42 | "jasmine-core": "~3.6.0", 43 | "jasmine-spec-reporter": "~6.0.0", 44 | "karma": "~5.1.1", 45 | "karma-chrome-launcher": "~3.1.0", 46 | "karma-firefox-launcher": "^2.1.0", 47 | "karma-coverage-istanbul-reporter": "~3.0.3", 48 | "karma-jasmine": "~4.0.1", 49 | "karma-jasmine-html-reporter": "^1.5.4", 50 | "protractor": "~7.0.0", 51 | "ts-node": "~7.0.0", 52 | "tslint": "~6.1.0", 53 | "typescript": "4.0.5" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /final/ngTvdTraining/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 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './e2e/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: 'e2e/tsconfig.e2e.json' 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /final/ngTvdTraining/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trivadis/AdvancedAngular/b3cfb1fc8957894cf2dc9cc6ff7ce638f071d89f/final/ngTvdTraining/screenshot-1.png -------------------------------------------------------------------------------- /final/ngTvdTraining/screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trivadis/AdvancedAngular/b3cfb1fc8957894cf2dc9cc6ff7ce638f071d89f/final/ngTvdTraining/screenshot-2.png -------------------------------------------------------------------------------- /final/ngTvdTraining/screenshot-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trivadis/AdvancedAngular/b3cfb1fc8957894cf2dc9cc6ff7ce638f071d89f/final/ngTvdTraining/screenshot-3.png -------------------------------------------------------------------------------- /final/ngTvdTraining/screenshot-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trivadis/AdvancedAngular/b3cfb1fc8957894cf2dc9cc6ff7ce638f071d89f/final/ngTvdTraining/screenshot-4.png -------------------------------------------------------------------------------- /final/ngTvdTraining/screenshot-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trivadis/AdvancedAngular/b3cfb1fc8957894cf2dc9cc6ff7ce638f071d89f/final/ngTvdTraining/screenshot-5.png -------------------------------------------------------------------------------- /final/ngTvdTraining/screenshot-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trivadis/AdvancedAngular/b3cfb1fc8957894cf2dc9cc6ff7ce638f071d89f/final/ngTvdTraining/screenshot-6.png -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/about/about-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | 4 | import { AboutListComponent } from './containers/about-list/about-list.component'; 5 | 6 | const routes: Routes = [ 7 | { path: '', component: AboutListComponent }, 8 | ]; 9 | 10 | @NgModule({ 11 | imports: [RouterModule.forChild(routes)], 12 | exports: [RouterModule], 13 | }) 14 | export class AboutRoutingModule { } 15 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/about/about.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { AboutListComponent } from './containers/about-list/about-list.component'; 4 | import { AboutRoutingModule } from './about-routing.module'; 5 | 6 | @NgModule({ 7 | imports: [AboutRoutingModule], 8 | exports: [], 9 | declarations: [AboutListComponent], 10 | providers: [] 11 | }) 12 | export class AboutModule {} 13 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/about/containers/about-list/about-list.component.html: -------------------------------------------------------------------------------- 1 | About - Lazy Loading -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/about/containers/about-list/about-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | templateUrl: 'about-list.component.html' 5 | }) 6 | export class AboutListComponent implements OnInit { 7 | constructor() { } 8 | 9 | ngOnInit() { } 10 | } 11 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | 2 | import { NgModule } from '@angular/core'; 3 | import { RouterModule, Routes } from '@angular/router'; 4 | import { AppCustomPreloader } from './core/app-custom-preloader'; 5 | import { StandardLayoutComponent } from './layout/standard-layout/standard-layout.component'; 6 | import { WelcomeComponent } from './layout/welcome/welcome.component'; 7 | 8 | const routes: Routes = [ 9 | { 10 | path: '', 11 | component: StandardLayoutComponent, 12 | children: [ 13 | { path: '', redirectTo: 'welcome', pathMatch: 'full' }, 14 | { path: 'welcome', component: WelcomeComponent }, 15 | { 16 | path: 'employees', 17 | loadChildren: () => import('./employee/employee.module').then(m => m.EmployeeModule), 18 | data: { preload: false } 19 | }, 20 | { 21 | path: 'about', 22 | loadChildren: () => import('./about/about.module').then(m => m.AboutModule), 23 | data: { preload: true } 24 | } 25 | ] 26 | } 27 | ]; 28 | 29 | @NgModule({ 30 | imports: [RouterModule.forRoot(routes, { preloadingStrategy: AppCustomPreloader, relativeLinkResolution: 'legacy' })], 31 | exports: [RouterModule], 32 | providers: [] 33 | }) 34 | export class AppRoutingModule {} 35 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { RouterTestingModule } from '@angular/router/testing'; 2 | import { TestBed, waitForAsync } from '@angular/core/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach( 7 | waitForAsync(() => { 8 | TestBed.configureTestingModule({ 9 | imports: [RouterTestingModule], 10 | declarations: [AppComponent] 11 | }).compileComponents(); 12 | }) 13 | ); 14 | it( 15 | 'should create the app', 16 | waitForAsync(() => { 17 | const fixture = TestBed.createComponent(AppComponent); 18 | const app = fixture.debugElement.componentInstance; 19 | expect(app).toBeTruthy(); 20 | }) 21 | ); 22 | }); 23 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | 4 | @Component({ 5 | selector: 'app-root', 6 | template: '' 7 | }) 8 | export class AppComponent { 9 | } 10 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 4 | import { EffectsModule } from '@ngrx/effects'; 5 | import { 6 | RouterState, 7 | RouterStateSerializer, 8 | StoreRouterConnectingModule 9 | } from '@ngrx/router-store'; 10 | import { MetaReducer, StoreModule } from '@ngrx/store'; 11 | // not used in production 12 | import { StoreDevtoolsModule } from '@ngrx/store-devtools'; 13 | import { environment } from './../environments/environment'; 14 | import { AppRoutingModule } from './app-routing.module'; 15 | import { AppComponent } from './app.component'; 16 | import { CoreModule } from './core/core.module'; 17 | import { LayoutModule } from './layout/layout.module'; 18 | import { SharedModule } from './shared/shared.module'; 19 | import { CustomSerializer, effects, reducers } from './store'; 20 | 21 | export const metaReducers: MetaReducer[] = !environment.production ? [] : []; 22 | 23 | @NgModule({ 24 | imports: [ 25 | BrowserModule, 26 | BrowserAnimationsModule, 27 | SharedModule, 28 | CoreModule.forRoot(), 29 | LayoutModule, 30 | AppRoutingModule, 31 | 32 | StoreModule.forRoot(reducers, { 33 | metaReducers, 34 | runtimeChecks: { 35 | strictStateImmutability: true, 36 | strictActionImmutability: true, 37 | strictStateSerializability: true, 38 | strictActionSerializability: true 39 | } 40 | }), 41 | EffectsModule.forRoot(effects), 42 | StoreRouterConnectingModule.forRoot({ 43 | routerState: RouterState.Minimal 44 | }), 45 | environment.production 46 | ? [] 47 | : StoreDevtoolsModule.instrument({ 48 | maxAge: 10 49 | }) 50 | ], 51 | declarations: [AppComponent], 52 | providers: [{ provide: RouterStateSerializer, useClass: CustomSerializer }], 53 | bootstrap: [AppComponent] 54 | }) 55 | export class AppModule {} 56 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/core/app-custom-preloader.ts: -------------------------------------------------------------------------------- 1 | import { PreloadingStrategy, Route } from '@angular/router'; 2 | 3 | import { Observable , of } from 'rxjs'; 4 | 5 | export class AppCustomPreloader implements PreloadingStrategy { 6 | preload(route: Route, load: Function): Observable { 7 | return route.data && route.data.preload ? load() : of(null); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/core/core.module.ts: -------------------------------------------------------------------------------- 1 | import { AppCustomPreloader } from './app-custom-preloader'; 2 | import { NgModule, Optional, SkipSelf, ModuleWithProviders } from '@angular/core'; 3 | import { CommonModule } from '@angular/common'; 4 | import { SuperService } from './super-service'; 5 | import { HTTP_INTERCEPTORS } from '@angular/common/http'; 6 | 7 | // https://angular.io/guide/styleguide#core-feature-module 8 | 9 | @NgModule({ 10 | imports: [CommonModule], 11 | exports: [], 12 | declarations: [] 13 | }) 14 | export class CoreModule { 15 | constructor( 16 | @Optional() 17 | @SkipSelf() 18 | parentModule: CoreModule 19 | ) { 20 | if (parentModule) { 21 | throw new Error(`Core has already been loaded. Import Core modules in the AppModule only.`); 22 | } 23 | } 24 | 25 | static forRoot(): ModuleWithProviders { 26 | return { 27 | ngModule: CoreModule, 28 | providers: [ 29 | SuperService, 30 | AppCustomPreloader 31 | ] 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/core/super-service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable() 4 | export class SuperService { 5 | constructor() {} 6 | 7 | get(): Boolean { 8 | return true; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/components/employee-form/employee-form.component.html: -------------------------------------------------------------------------------- 1 |

{{ title }}

2 |
3 |
4 | 5 |
6 | 7 |
8 |
9 | 10 | Firstname is required. 11 | 12 |
13 |
14 |
15 | 16 |
17 | 18 |
19 |
20 | 21 | Lastname is required. 22 | 23 |
24 |
25 |
26 | 27 |
28 | 29 |
30 |
31 | 32 | E-Mail is required. 33 | 34 | 35 | E-Mail is invalid. 36 | 37 | 38 | E-Mail is not unique. 39 | 40 |
41 |
42 |
43 | 44 |
45 | 46 |
47 |
48 | 49 | E-Mail is required. 50 | 51 | 52 | E-Mail is invalid. 53 | 54 |
55 |
56 | 57 | E-Mails must match. 58 | 59 |
60 |
61 | 62 |
63 | 66 | 69 | 72 |
73 |
-------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/components/employee-form/employee-form.component.scss: -------------------------------------------------------------------------------- 1 | // .ng-valid:not(form), .ng-valid[required], .ng-valid.required { 2 | // border-left: 5px solid #42A948; /* green */ 3 | // } 4 | 5 | // .ng-invalid:not(form) { 6 | // border-left: 5px solid #a94442; /* red */ 7 | // } -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/components/employee-form/employee-form.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ChangeDetectionStrategy, 3 | Component, 4 | EventEmitter, 5 | Input, 6 | OnChanges, 7 | OnInit, 8 | Output, 9 | SimpleChanges 10 | } from '@angular/core'; 11 | import { FormBuilder, FormGroup, Validators } from '@angular/forms'; 12 | import { Employee } from '../../model/employee.model'; 13 | import { EmployeeService } from '../../services'; 14 | import { EmployeeValidators } from './../../validators/employee.validator'; 15 | 16 | @Component({ 17 | selector: 'app-employee-form', 18 | templateUrl: 'employee-form.component.html', 19 | styleUrls: ['employee-form.component.scss'], 20 | changeDetection: ChangeDetectionStrategy.OnPush 21 | }) 22 | export class EmployeeFormComponent implements OnInit, OnChanges { 23 | @Input() employee: Employee; 24 | 25 | @Output() create = new EventEmitter(); 26 | @Output() update = new EventEmitter(); 27 | @Output() remove = new EventEmitter(); 28 | 29 | form: FormGroup = this.fb.group( 30 | { 31 | firstname: [ 32 | '', 33 | { 34 | validators: Validators.required, 35 | updateOn: 'blur' 36 | } 37 | ], 38 | lastname: ['', Validators.required], 39 | email: [ 40 | '', 41 | [Validators.required, EmployeeValidators.emailValidator], 42 | EmployeeValidators.checkEmailUnique(this.service) 43 | ], 44 | // email: [ 45 | // '', 46 | // { 47 | // validators: [Validators.required, EmployeeValidators.emailValidator], 48 | // asyncValidators: EmployeeValidators.checkEmailUnique(this.service), 49 | // updateOn: 'blur' 50 | // } 51 | // ], 52 | emailConfirm: ['', [Validators.required, EmployeeValidators.emailValidator]] 53 | }, 54 | { 55 | validators: EmployeeValidators.checkEmailsMatch 56 | // updateOn: 'blur' // not working at the moment. => Bug closed => > 7.2.x 57 | } 58 | ); 59 | 60 | isEdit = false; 61 | title = 'Create'; 62 | 63 | constructor(private fb: FormBuilder, private service: EmployeeService) {} 64 | 65 | ngOnInit() {} 66 | 67 | ngOnChanges(changes: SimpleChanges) { 68 | if (this.employee && this.employee.id) { 69 | this.isEdit = true; 70 | this.title = 'Edit'; 71 | this.form.patchValue(this.employee); 72 | } 73 | } 74 | 75 | get firstname() { 76 | return this.form.get('firstname'); 77 | } 78 | get lastname() { 79 | return this.form.get('lastname'); 80 | } 81 | get email() { 82 | return this.form.get('email'); 83 | } 84 | get emailConfirm() { 85 | return this.form.get('emailConfirm'); 86 | } 87 | 88 | createEmployee() { 89 | if (this.form.valid) { 90 | this.create.emit(this.form.value); 91 | } 92 | } 93 | 94 | updateEmployee() { 95 | this.form.markAllAsTouched(); 96 | if (this.form.touched && this.form.valid) { 97 | this.update.emit({ ...this.employee, ...this.form.value }); 98 | } 99 | } 100 | 101 | removeEmployee() { 102 | this.remove.emit({ ...this.employee, ...this.form.value }); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/components/index.ts: -------------------------------------------------------------------------------- 1 | import { EmployeeFormComponent } from './employee-form/employee-form.component'; 2 | 3 | export const components: any[] = [EmployeeFormComponent]; 4 | 5 | export * from './employee-form/employee-form.component'; 6 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/containers/employee-list/employee-list.component.html: -------------------------------------------------------------------------------- 1 |

Employees

2 | 7 |
8 |
Search:
9 |
10 | 11 |
12 |
13 |
14 |
15 | Filtered by: {{listFilter}} 16 |
17 |
18 |
19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 41 | 42 | 43 |
IdFirstnameLastnameE-Mail 
{{ employee.id }}{{ employee.firstname }}{{ employee.lastname }}{{ employee.email }} 37 | 38 | Edit 39 | 40 |
44 | Data are loaded 45 |
46 |
-------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/containers/employee-list/employee-list.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { NO_ERRORS_SCHEMA } from '@angular/core'; 2 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 3 | import { FormsModule } from '@angular/forms'; 4 | import { RouterTestingModule } from '@angular/router/testing'; 5 | import { StoreModule } from '@ngrx/store'; 6 | import { LayoutModule } from '../../../layout/layout.module'; 7 | import { EmployeeFilterPipe } from '../../pipes/employee-filter.pipe'; 8 | import { EmployeeService } from './../../services/employee.service'; 9 | import { reducers } from './../../store'; 10 | import { reducers as reducersFeature } from './../../store/reducers/'; 11 | import { EmployeeListComponent } from './employee-list.component'; 12 | 13 | describe('EmployeeListComponent', () => { 14 | let fixture: ComponentFixture; 15 | let comp: EmployeeListComponent; 16 | 17 | beforeEach(waitForAsync(() => { 18 | TestBed.configureTestingModule({ 19 | imports: [ 20 | RouterTestingModule, 21 | LayoutModule, 22 | FormsModule, 23 | StoreModule.forRoot(reducers), 24 | StoreModule.forFeature('employees', reducersFeature) 25 | ], 26 | providers: [EmployeeService], 27 | declarations: [EmployeeListComponent, EmployeeFilterPipe], 28 | schemas: [NO_ERRORS_SCHEMA] 29 | }).compileComponents(); 30 | })); 31 | 32 | beforeEach(() => { 33 | fixture = TestBed.createComponent(EmployeeListComponent); 34 | comp = fixture.componentInstance; 35 | fixture.detectChanges(); 36 | }); 37 | 38 | it('should create the app', waitForAsync(() => { 39 | expect(comp).toBeTruthy(); 40 | })); 41 | 42 | it('should render title in a h2 tag', waitForAsync(() => { 43 | const result = fixture.nativeElement.querySelector('h2').textContent; 44 | expect(result).toContain('Employees'); 45 | })); 46 | }); 47 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/containers/employee-list/employee-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; 2 | import { ActivatedRoute } from '@angular/router'; 3 | import { Store } from '@ngrx/store'; 4 | import { Observable } from 'rxjs'; 5 | import { Employee } from '../../model/employee.model'; 6 | import * as fromStore from '../../store'; 7 | 8 | @Component({ 9 | templateUrl: 'employee-list.component.html', 10 | }) 11 | export class EmployeeListComponent implements OnInit { 12 | employees$: Observable; 13 | listFilter: string; 14 | 15 | @ViewChild('input') 16 | input: ElementRef; 17 | 18 | constructor( 19 | private store$: Store, 20 | private route: ActivatedRoute 21 | ) {} 22 | 23 | ngOnInit() { 24 | this.employees$ = this.store$.select(fromStore.getEmployees); 25 | // this.employees$ = this.service.getEmployees(); 26 | this.listFilter = this.route.snapshot.queryParams['filterBy'] || ''; 27 | } 28 | 29 | keyClicked() { 30 | this.input.nativeElement.focus(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/containers/employee/employee.component.html: -------------------------------------------------------------------------------- 1 | 6 | > 7 | 8 | go back -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/containers/employee/employee.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, ViewChild } from '@angular/core'; 2 | import { Store } from '@ngrx/store'; 3 | import { Observable } from 'rxjs'; 4 | import { EmployeeFormComponent } from '../../components/employee-form/employee-form.component'; 5 | import { Employee } from '../../model/employee.model'; 6 | import * as fromStore from '../../store'; 7 | 8 | @Component({ 9 | templateUrl: 'employee.component.html' 10 | }) 11 | export class EmployeeComponent implements OnInit { 12 | employee$: Observable; 13 | 14 | @ViewChild(EmployeeFormComponent, { static: true }) 15 | employeeFormComp: EmployeeFormComponent; 16 | 17 | constructor(private store$: Store) {} 18 | 19 | ngOnInit() { 20 | this.employee$ = this.store$.select(fromStore.getSelectedEmployee); 21 | } 22 | 23 | onCreate(employee: Employee) { 24 | this.store$.dispatch(fromStore.createEmployee({ employee })); 25 | } 26 | 27 | onUpdate(employee: Employee) { 28 | this.store$.dispatch(fromStore.updateEmployee({ employee })); 29 | } 30 | 31 | onRemove(employee: Employee) { 32 | const remove = window.confirm('Are you sure?'); 33 | if (remove) { 34 | this.store$.dispatch(fromStore.removeEmployee({ employee })); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/containers/index.ts: -------------------------------------------------------------------------------- 1 | import { EmployeeListComponent } from './employee-list/employee-list.component'; 2 | import { EmployeeComponent } from './employee/employee.component'; 3 | 4 | export const containers: any[] = [EmployeeListComponent, EmployeeComponent]; 5 | 6 | export * from './employee-list/employee-list.component'; 7 | export * from './employee/employee.component'; 8 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/employee-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | 4 | import * as fromContainer from './containers'; 5 | import * as fromGuards from './guards'; 6 | 7 | const routes: Routes = [ 8 | { 9 | path: '', 10 | component: fromContainer.EmployeeListComponent, 11 | canActivate: [fromGuards.EmployeeGuard] 12 | }, 13 | { 14 | path: 'new', 15 | component: fromContainer.EmployeeComponent, 16 | canActivate: [fromGuards.EmployeeGuard] 17 | }, 18 | { 19 | path: ':employeeId', 20 | component: fromContainer.EmployeeComponent, 21 | canActivate: [fromGuards.EmployeeGuard], 22 | canDeactivate: [fromGuards.EmployeeEditGuard] 23 | } 24 | ]; 25 | 26 | @NgModule({ 27 | imports: [RouterModule.forChild(routes)], 28 | exports: [RouterModule], 29 | }) 30 | export class EmployeeRoutingModule { } 31 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/employee.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { StoreModule } from '@ngrx/store'; 4 | import { EffectsModule } from '@ngrx/effects'; 5 | 6 | import { SharedModule } from '../shared/shared.module'; 7 | import { EmployeeRoutingModule } from './employee-routing.module'; 8 | 9 | import { reducers, effects } from './store'; 10 | // components 11 | import * as fromComponents from './components'; 12 | 13 | // containers 14 | import * as fromContainers from './containers'; 15 | 16 | // guards 17 | import * as fromGuards from './guards'; 18 | 19 | // pipes 20 | import * as fromPipes from './pipes'; 21 | 22 | // services 23 | import * as fromServices from './services'; 24 | 25 | @NgModule({ 26 | imports: [ 27 | SharedModule, 28 | EmployeeRoutingModule, 29 | 30 | StoreModule.forFeature('employees', reducers), 31 | EffectsModule.forFeature(effects) 32 | ], 33 | exports: [], 34 | declarations: [...fromContainers.containers, ...fromComponents.components, ...fromPipes.pipes], 35 | providers: [...fromServices.services, ...fromGuards.guards] 36 | }) 37 | export class EmployeeModule {} 38 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/guards/employee-edit.guard.ts: -------------------------------------------------------------------------------- 1 | import { EmployeeComponent } from './../containers/employee/employee.component'; 2 | import { Injectable } from '@angular/core'; 3 | import { CanDeactivate } from '@angular/router'; 4 | 5 | @Injectable() 6 | export class EmployeeEditGuard implements CanDeactivate { 7 | canDeactivate(component: EmployeeComponent): boolean { 8 | if (!component.employeeFormComp.form.valid && component.employeeFormComp.form.touched) { 9 | return confirm(`Navigate away and lose all changes?`); 10 | } 11 | return true; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/guards/employee.guard.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { CanActivate } from '@angular/router'; 3 | import { Store } from '@ngrx/store'; 4 | import { Observable, of } from 'rxjs'; 5 | import { catchError, filter, switchMap, take, tap } from 'rxjs/operators'; 6 | import * as fromStore from '../store'; 7 | 8 | @Injectable() 9 | export class EmployeeGuard implements CanActivate { 10 | constructor(private store: Store) {} 11 | 12 | canActivate(): Observable { 13 | return this.checkStore().pipe( 14 | switchMap(() => of(true)), 15 | catchError(() => of(false)) 16 | ); 17 | } 18 | 19 | checkStore(): Observable { 20 | return this.store.select(fromStore.getEmployeeLoaded).pipe( 21 | tap(loaded => { 22 | if (!loaded) { 23 | this.store.dispatch(fromStore.loadEmployees()); 24 | } 25 | }), 26 | filter(loaded => loaded), 27 | take(1) 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/guards/index.ts: -------------------------------------------------------------------------------- 1 | import { EmployeeEditGuard } from './employee-edit.guard'; 2 | import { EmployeeGuard } from './employee.guard'; 3 | 4 | export const guards: any[] = [EmployeeEditGuard, EmployeeGuard]; 5 | 6 | export * from './employee-edit.guard'; 7 | export * from './employee.guard'; 8 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/model/employee.model.ts: -------------------------------------------------------------------------------- 1 | export interface Employee { 2 | id?: number; 3 | firstname?: string; 4 | lastname?: string; 5 | email?: string; 6 | } 7 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/pipes/employee-filter.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { EmployeeFilterPipe } from './employee-filter.pipe'; 2 | 3 | import { TestBed, waitForAsync } from '@angular/core/testing'; 4 | import { Employee } from '../model/employee.model'; 5 | 6 | describe('EmployeeFilterPipe', () => { 7 | let pipe: EmployeeFilterPipe; 8 | let employeees: Employee[]; 9 | 10 | beforeEach(() => { 11 | pipe = new EmployeeFilterPipe(); 12 | }); 13 | beforeEach(() => { 14 | employeees = [ 15 | { firstname: 'Thomas', lastname: 'Gassmann' }, 16 | { firstname: 'Lara', lastname: 'croft' } 17 | ]; 18 | }); 19 | 20 | it('should return all data', () => { 21 | const result = pipe.transform(employeees, null); 22 | expect(result.length).toBe(2); 23 | }); 24 | it('should filter correctly', () => { 25 | const result = pipe.transform(employeees, 'Thomas'); 26 | expect(result.length).toBe(1); 27 | }); 28 | it('should filter correctly with capital letters', () => { 29 | const result = pipe.transform(employeees, 'LARA'); 30 | expect(result.length).toBe(1); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/pipes/employee-filter.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Employee } from './../model/employee.model'; 2 | import { PipeTransform, Pipe } from '@angular/core'; 3 | 4 | @Pipe({ 5 | name: 'employeeFilter' 6 | }) 7 | export class EmployeeFilterPipe implements PipeTransform { 8 | transform(value: Employee[], filterBy: string): Employee[] { 9 | filterBy = filterBy?.toLocaleLowerCase(); 10 | return (filterBy && value) 11 | ? value.filter( 12 | (e: Employee) => 13 | (e.firstname.toLocaleLowerCase() + e.lastname.toLocaleLowerCase()).indexOf(filterBy) !== 14 | -1 15 | ) 16 | : value; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/pipes/index.ts: -------------------------------------------------------------------------------- 1 | import { EmployeeFilterPipe } from './employee-filter.pipe'; 2 | 3 | export const pipes: any[] = [EmployeeFilterPipe]; 4 | 5 | export * from './employee-filter.pipe'; 6 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/services/employee.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, fakeAsync, tick, waitForAsync } from '@angular/core/testing'; 2 | import { RouterModule } from '@angular/router'; 3 | import { NO_ERRORS_SCHEMA } from '@angular/core'; 4 | import { APP_BASE_HREF } from '@angular/common'; 5 | import { EmployeeService } from './employee.service'; 6 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 7 | import { HttpTestingController } from '@angular/common/http/testing'; 8 | import { environment } from '../../../environments/environment'; 9 | import { Employee } from '../model/employee.model'; 10 | 11 | describe('EmployeeService', () => { 12 | let service: EmployeeService; 13 | let httpMock: HttpTestingController; 14 | 15 | beforeEach(() => { 16 | TestBed.configureTestingModule({ 17 | imports: [HttpClientTestingModule], 18 | providers: [EmployeeService] 19 | }); 20 | }); 21 | beforeEach(() => { 22 | service = TestBed.get(EmployeeService); 23 | httpMock = TestBed.get(HttpTestingController); 24 | }); 25 | 26 | afterEach(() => { 27 | httpMock.verify(); 28 | }); 29 | 30 | it('should create the app', waitForAsync(() => { 31 | const dummy: Employee[] = [ 32 | { id: 3, firstname: 'Thomas', lastname: 'Huber', email: 'thomas.huber@trivadis.com' }, 33 | { id: 5, firstname: 'Thomas', lastname: 'Gassmann', email: 'thomas.gassmann@trivadis.com' } 34 | ]; 35 | 36 | service.getEmployees().subscribe(list => { 37 | expect(list.length).toBe(2); 38 | expect(list).toEqual(dummy); 39 | }); 40 | 41 | const request = httpMock.expectOne(`${environment.apiBaseUrl}/employees`); 42 | expect(request.request.method).toBe('GET'); 43 | 44 | request.flush(dummy); 45 | }) 46 | ); 47 | 48 | it( 49 | 'should test fakeAsync', 50 | fakeAsync(() => { 51 | let flag = false; 52 | setTimeout(() => { 53 | flag = true; 54 | }, 50); 55 | expect(flag).toBe(false); 56 | tick(50); 57 | expect(flag).toBe(true); 58 | }) 59 | ); 60 | }); 61 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/services/employee.service.ts: -------------------------------------------------------------------------------- 1 | import { Employee } from './../model/employee.model'; 2 | import { Injectable } from '@angular/core'; 3 | import { HttpClient } from '@angular/common/http'; 4 | 5 | import { Observable , of , throwError as _throw } from 'rxjs'; 6 | import { catchError, delay, map } from 'rxjs/operators'; 7 | import { environment } from '../../../environments/environment'; 8 | 9 | @Injectable() 10 | export class EmployeeService { 11 | 12 | constructor(private http: HttpClient) { } 13 | 14 | getEmployees(): Observable { 15 | return this.http 16 | .get(`${environment.apiBaseUrl}/employees`) 17 | .pipe(catchError((error: any) => _throw(error))); 18 | } 19 | 20 | 21 | getEmployee(id: number): Observable { 22 | return this.http 23 | .get(`${environment.apiBaseUrl}/employees/${id}`) 24 | .pipe(catchError((error: any) => _throw(error))); 25 | } 26 | 27 | createEmployee(payload: Employee): Observable { 28 | return this.http 29 | .post(`${environment.apiBaseUrl}/employees`, payload) 30 | .pipe(catchError((error: any) => _throw(error))); 31 | } 32 | 33 | updateEmployee(payload: Employee): Observable { 34 | return this.http 35 | .put(`${environment.apiBaseUrl}/employees/${payload.id}`, payload) 36 | .pipe(catchError((error: any) => _throw(error))); 37 | } 38 | 39 | removeEmployee(payload: Employee): Observable { 40 | return this.http 41 | .delete(`${environment.apiBaseUrl}/employees/${payload.id}`) 42 | .pipe(catchError((error: any) => _throw(error))); 43 | } 44 | 45 | checkEmailUnique(email: string) { 46 | // service call for example 47 | return of(email).pipe( 48 | delay(1000), 49 | map((e) => e !== 'info@trivadis.com') 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/services/index.ts: -------------------------------------------------------------------------------- 1 | import { EmployeeService } from './employee.service'; 2 | 3 | export const services: any[] = [EmployeeService]; 4 | 5 | export * from './employee.service'; 6 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/store/actions/employee.action.ts: -------------------------------------------------------------------------------- 1 | import { createAction, props } from '@ngrx/store'; 2 | import { Employee } from '../../model/employee.model'; 3 | 4 | export const loadEmployees = createAction('[Employees] Load Employee'); 5 | 6 | export const loadEmployeeSuccess = createAction( 7 | '[Employees] Load Employee Sucess', 8 | props<{ employees: Employee[] }>() 9 | ); 10 | 11 | export const loadEmployeeFail = createAction( 12 | '[Employees] Load Employee Fail', 13 | props<{ error: any }>() 14 | ); 15 | 16 | // create actions 17 | export const createEmployee = createAction( 18 | '[Employees] Create Employee', 19 | props<{ employee: Employee }>() 20 | ); 21 | export const createEmployeeSuccess = createAction( 22 | '[Employees] Create Employee Sucess', 23 | props<{ employee: Employee }>() 24 | ); 25 | export const createEmployeeFail = createAction( 26 | '[Employees] Create Employee Fail', 27 | props<{ error: any }>() 28 | ); 29 | 30 | // update actions 31 | export const updateEmployee = createAction( 32 | '[Employees] Update Employee', 33 | props<{ employee: Employee }>() 34 | ); 35 | export const updateEmployeeSuccess = createAction( 36 | '[Employees] Update Employee Sucess', 37 | props<{ employee: Employee }>() 38 | ); 39 | export const updateEmployeeFail = createAction( 40 | '[Employees] Update Employee Fail', 41 | props<{ error: any }>() 42 | ); 43 | 44 | // remove actions 45 | export const removeEmployee = createAction( 46 | '[Employees] Remove Employee', 47 | props<{ employee: Employee }>() 48 | ); 49 | export const removeEmployeeSuccess = createAction( 50 | '[Employees] Remove Employee Sucess', 51 | props<{ employee: Employee }>() 52 | ); 53 | export const removeEmployeeFail = createAction( 54 | '[Employees] Remove Employee Fail', 55 | props<{ error: any }>() 56 | ); 57 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/store/actions/index.ts: -------------------------------------------------------------------------------- 1 | export * from './employee.action'; 2 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/store/effects/employee.effect.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Actions, createEffect, ofType } from '@ngrx/effects'; 3 | import { of } from 'rxjs'; 4 | import { catchError, map, switchMap } from 'rxjs/operators'; 5 | import * as fromRoot from '../../../../app/store'; 6 | import * as fromServices from '../../services'; 7 | import * as employeeActions from '../actions/employee.action'; 8 | 9 | @Injectable() 10 | export class EmployeeEffects { 11 | constructor(private actions$: Actions, private employeeService: fromServices.EmployeeService) {} 12 | 13 | loadEmployees$ = createEffect(() => 14 | this.actions$.pipe( 15 | ofType(employeeActions.loadEmployees), 16 | switchMap(() => { 17 | return this.employeeService.getEmployees().pipe( 18 | map(employees => employeeActions.loadEmployeeSuccess({ employees })), 19 | catchError(error => of(employeeActions.loadEmployeeFail({ error }))) 20 | ); 21 | }) 22 | ) 23 | ); 24 | 25 | createEmployee$ = createEffect(() => 26 | this.actions$.pipe( 27 | ofType(employeeActions.createEmployee), 28 | switchMap(({ employee }) => { 29 | return this.employeeService.createEmployee(employee).pipe( 30 | map(newEmpl => employeeActions.createEmployeeSuccess({ employee: newEmpl })), 31 | catchError(error => of(employeeActions.createEmployeeFail({ error }))) 32 | ); 33 | }) 34 | ) 35 | ); 36 | 37 | updateEmployee$ = createEffect(() => 38 | this.actions$.pipe( 39 | ofType(employeeActions.updateEmployee), 40 | switchMap(({ employee }) => { 41 | return this.employeeService.updateEmployee(employee).pipe( 42 | map(newEmpl => employeeActions.updateEmployeeSuccess({ employee: newEmpl })), 43 | catchError(error => { 44 | console.log(error); 45 | return of(employeeActions.updateEmployeeFail({ error })); 46 | }) 47 | ); 48 | }) 49 | ) 50 | ); 51 | 52 | removeEmployee$ = createEffect(() => 53 | this.actions$.pipe( 54 | ofType(employeeActions.removeEmployee), 55 | switchMap(({ employee }) => { 56 | return this.employeeService.removeEmployee(employee).pipe( 57 | map(() => employeeActions.removeEmployeeSuccess({ employee })), 58 | catchError(error => { 59 | console.log(error); 60 | return of(employeeActions.removeEmployeeFail(error)); 61 | }) 62 | ); 63 | }) 64 | ) 65 | ); 66 | 67 | handleEmployeeSuccess$ = createEffect(() => 68 | this.actions$.pipe( 69 | ofType( 70 | employeeActions.createEmployeeSuccess, 71 | employeeActions.updateEmployeeSuccess, 72 | employeeActions.removeEmployeeSuccess 73 | ), 74 | map(({ employee }) => { 75 | return fromRoot.go({ 76 | payload: { 77 | path: ['/employees'], 78 | extras: { queryParamsHandling: 'preserve' } 79 | } 80 | }); 81 | }) 82 | ) 83 | ); 84 | } 85 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/store/effects/index.ts: -------------------------------------------------------------------------------- 1 | import { EmployeeEffects } from './employee.effect'; 2 | 3 | export const effects: any[] = [EmployeeEffects]; 4 | 5 | export * from './employee.effect'; 6 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/store/index.ts: -------------------------------------------------------------------------------- 1 | export * from './reducers'; 2 | export * from './actions'; 3 | export * from './effects'; 4 | export * from './selectors'; 5 | export * from './state'; 6 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/store/reducers/employee.reducer.ts: -------------------------------------------------------------------------------- 1 | import { createReducer, on } from '@ngrx/store'; 2 | import { Employee } from '../../model/employee.model'; 3 | import * as fromActions from '../actions/employee.action'; 4 | import * as fromState from '../state/employee.state'; 5 | 6 | export const reducer = createReducer( 7 | fromState.initialState, 8 | on(fromActions.loadEmployees, state => ({ 9 | ...state, 10 | loading: true 11 | })), 12 | on(fromActions.loadEmployeeSuccess, (state, { employees }) => { 13 | const entities = employees.reduce( 14 | (e: { [id: number]: Employee }, employee: Employee) => { 15 | return { 16 | ...e, 17 | [employee.id]: employee 18 | }; 19 | }, 20 | { 21 | ...state.entities 22 | } 23 | ); 24 | 25 | return { 26 | ...state, 27 | loading: false, 28 | loaded: true, 29 | entities 30 | }; 31 | }), 32 | on(fromActions.loadEmployeeFail, (state, { error }) => { 33 | return { 34 | ...state, 35 | loading: false, 36 | loaded: false 37 | }; 38 | }), 39 | on( 40 | fromActions.createEmployeeSuccess, 41 | fromActions.updateEmployeeSuccess, 42 | (state, { employee }) => { 43 | const entities = { 44 | ...state.entities, 45 | [employee.id]: employee 46 | }; 47 | 48 | return { 49 | ...state, 50 | entities 51 | }; 52 | } 53 | ), 54 | on(fromActions.removeEmployeeSuccess, (state, { employee }) => { 55 | const { [employee.id]: removed, ...entities } = state.entities; 56 | 57 | return { 58 | ...state, 59 | entities 60 | }; 61 | }) 62 | ); 63 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/store/reducers/index.ts: -------------------------------------------------------------------------------- 1 | import { ActionReducerMap, createFeatureSelector } from '@ngrx/store'; 2 | import * as fromState from '../state'; 3 | import * as fromEmployee from './employee.reducer'; 4 | 5 | export const reducers: ActionReducerMap = { 6 | employee: fromEmployee.reducer 7 | }; 8 | 9 | export const getState = createFeatureSelector('employees'); 10 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/store/selectors/employee.selector.ts: -------------------------------------------------------------------------------- 1 | import { createSelector } from '@ngrx/store'; 2 | import * as fromRoot from '../../../../app/store'; 3 | import { Employee } from '../../model/employee.model'; 4 | import * as fromFeature from '../reducers'; 5 | import * as fromState from '../state'; 6 | import * as fromEmployeeState from '../state/employee.state'; 7 | 8 | export const getEmployeeState = createSelector( 9 | fromFeature.getState, 10 | (state: fromState.State) => state.employee 11 | ); 12 | 13 | export const getEmployeeEntities = createSelector( 14 | getEmployeeState, 15 | fromEmployeeState.getEmployeeEntities 16 | ); 17 | export const getEmployees = createSelector( 18 | getEmployeeEntities, 19 | entities => { 20 | // return Object.keys(entities).map(id => entities[+id]); 21 | return Object.values(entities); 22 | } 23 | ); 24 | 25 | export const getEmployeeLoading = createSelector( 26 | getEmployeeState, 27 | fromEmployeeState.getEmployeeLoading 28 | ); 29 | 30 | export const getEmployeeLoaded = createSelector( 31 | getEmployeeState, 32 | fromEmployeeState.getEmployeeLoaded 33 | ); 34 | 35 | export const getSelectedEmployee = createSelector( 36 | getEmployeeEntities, 37 | fromRoot.getRouterState, 38 | (entities, router): Employee => { 39 | return router.state && entities[router.state.params.employeeId]; 40 | } 41 | ); 42 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/store/selectors/index.ts: -------------------------------------------------------------------------------- 1 | export * from './employee.selector'; 2 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/store/state/employee.state.ts: -------------------------------------------------------------------------------- 1 | import { Employee } from '../../model/employee.model'; 2 | 3 | export interface EmployeeState { 4 | entities: { [id: number]: Employee }; 5 | loading: boolean; 6 | loaded: boolean; 7 | } 8 | 9 | export const initialState: EmployeeState = { 10 | entities: {}, 11 | loading: false, 12 | loaded: false 13 | }; 14 | 15 | export const getEmployeeEntities = (state: EmployeeState) => state.entities; 16 | export const getEmployeeLoading = (state: EmployeeState) => state.loading; 17 | export const getEmployeeLoaded = (state: EmployeeState) => state.loaded; 18 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/store/state/index.ts: -------------------------------------------------------------------------------- 1 | import * as fromEmployee from './employee.state'; 2 | 3 | export interface State { 4 | employee: fromEmployee.EmployeeState; 5 | } 6 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/validators/check-email-match.validator.ts: -------------------------------------------------------------------------------- 1 | // import { Directive } from '@angular/core'; 2 | // import { Validator, ValidatorFn, FormControl, AbstractControl, NG_VALIDATORS } from '@angular/forms'; 3 | // import { EmployeeValidators } from './employee.validator'; 4 | 5 | // @Directive({ 6 | // selector: '[appIsEmails][ngModel]', 7 | // providers: [ 8 | // { provide: NG_VALIDATORS, useExisting: IsEmailValidator, multi: true } 9 | // ] 10 | // }) 11 | // export class IsEmailValidator implements Validator { 12 | // validate(c: AbstractControl) { 13 | // return EmployeeValidators.emailValidator(c); 14 | // } 15 | // } 16 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/employee/validators/employee.validator.ts: -------------------------------------------------------------------------------- 1 | import { AbstractControl, ValidatorFn, ValidationErrors } from '@angular/forms'; 2 | 3 | import { of } from 'rxjs'; 4 | import { delay, map } from 'rxjs/operators'; 5 | import { EmployeeService } from '../services/index'; 6 | 7 | export class EmployeeValidators { 8 | static checkEmailsMatch(control: AbstractControl) { 9 | const email = control.get('email'); 10 | const confirm = control.get('emailConfirm'); 11 | if (!(email && confirm)) { 12 | return null; 13 | } 14 | return email.value === confirm.value ? null : { nomatch: true }; 15 | } 16 | 17 | static emailValidator(control: AbstractControl): ValidationErrors | null { 18 | const val: string = control.value; 19 | if (!val || val.indexOf('@') > 0) { 20 | return null; 21 | } 22 | return { invalidemail: true }; 23 | } 24 | 25 | static checkEmailUnique(service: EmployeeService) { 26 | return (control: AbstractControl) => { 27 | return service.checkEmailUnique(control.value).pipe( 28 | map(res => { 29 | console.log(res); 30 | return res ? null : { emailNotUnique: true }; 31 | })); 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/layout/layout.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, Optional, SkipSelf } from '@angular/core'; 2 | 3 | import { StandardLayoutComponent } from './standard-layout/standard-layout.component'; 4 | import { WelcomeComponent } from './welcome/welcome.component'; 5 | import { SharedModule } from '../shared/shared.module'; 6 | import { RouterModule } from '@angular/router'; 7 | 8 | @NgModule({ 9 | imports: [RouterModule, SharedModule], 10 | exports: [], 11 | declarations: [StandardLayoutComponent, WelcomeComponent] 12 | }) 13 | export class LayoutModule { 14 | constructor( 15 | @Optional() 16 | @SkipSelf() 17 | parentModule: LayoutModule 18 | ) { 19 | if (parentModule) { 20 | throw new Error(`LayoutModule has already been loaded. Import Core modules in the AppModule only.`); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/layout/standard-layout/standard-layout.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Employee Portal

4 |
5 |
6 | 11 |
12 |
13 |
14 |
15 | 16 |
17 |
18 |
19 |
20 |

© 2020, Trivadis AG

21 |
22 |
23 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/layout/standard-layout/standard-layout.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | templateUrl: 'standard-layout.component.html' 5 | }) 6 | export class StandardLayoutComponent implements OnInit { 7 | constructor() { } 8 | 9 | ngOnInit() { } 10 | } 11 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/layout/welcome/welcome.component.html: -------------------------------------------------------------------------------- 1 |

Employee Portal

2 |

3 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut 4 | labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo 5 | dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit 6 | amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor 7 | invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et 8 | justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum 9 | dolor sit amet. 10 |

11 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/layout/welcome/welcome.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | templateUrl: 'welcome.component.html' 5 | }) 6 | export class WelcomeComponent implements OnInit { 7 | constructor() {} 8 | 9 | ngOnInit() {} 10 | } 11 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/shared/auth.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; 2 | import { Injectable } from '@angular/core'; 3 | import { Observable } from 'rxjs'; 4 | 5 | @Injectable() 6 | export class AuthInterceptor implements HttpInterceptor { 7 | intercept(req: HttpRequest, next: HttpHandler): Observable> { 8 | console.log('authentication'); 9 | const authReq = req.clone({ 10 | // headers: req.headers.set('Authorization', 'Bearer: XYZ') 11 | }); 12 | return next.handle(authReq); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/shared/directives/hotkey.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, EventEmitter, HostListener, Input, Output } from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: '[appHotkey]' 5 | }) 6 | export class HotkeyDirective { 7 | // tslint:disable-next-line:no-input-rename 8 | @Input('appHotkey') keyCode: number; 9 | 10 | @Output() keyClicked: EventEmitter = new EventEmitter(); 11 | 12 | constructor() {} 13 | 14 | @HostListener('window:keydown', ['$event']) 15 | handleKeyboardEvent(kbdEvent: KeyboardEvent) { 16 | if (kbdEvent.keyCode === this.keyCode && kbdEvent.ctrlKey) { 17 | kbdEvent.preventDefault(); 18 | this.keyClicked.emit(true); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/shared/directives/index.ts: -------------------------------------------------------------------------------- 1 | import { HotkeyDirective } from './hotkey.directive'; 2 | import { SelectDirective } from './select.directive'; 3 | 4 | export const directives: any[] = [SelectDirective, HotkeyDirective]; 5 | 6 | export * from './hotkey.directive'; 7 | export * from './select.directive'; 8 | 9 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/shared/directives/select.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, HostBinding, HostListener, Input, Renderer2 } from '@angular/core'; 2 | 3 | @Directive({ selector: '[appSelect]' }) 4 | export class SelectDirective { 5 | @HostBinding('class.selected') 6 | isSelected = false; 7 | 8 | constructor(private el: ElementRef, private renderer: Renderer2) {} 9 | @Input() 10 | backgroundColor: string; 11 | 12 | @HostListener('dblclick') 13 | doubleClick() { 14 | this.renderer.setStyle(this.el.nativeElement, 'backgroundColor', this.backgroundColor); 15 | } 16 | 17 | // @HostListener('mouseover') 18 | // @HostListener('mouseleave') 19 | @HostListener('click') 20 | setSelected() { 21 | this.isSelected = !this.isSelected; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/shared/http-error.interceptor.ts: -------------------------------------------------------------------------------- 1 | 2 | import {throwError as observableThrowError, Observable } from 'rxjs'; 3 | 4 | 5 | import { 6 | HttpErrorResponse, 7 | HttpEvent, 8 | HttpHandler, 9 | HttpInterceptor, 10 | HttpRequest 11 | } from '@angular/common/http'; 12 | import { Injectable } from '@angular/core'; 13 | 14 | import { Store } from '@ngrx/store'; 15 | import { catchError } from 'rxjs/operators'; 16 | 17 | @Injectable() 18 | export class HttpErrorInterceptor implements HttpInterceptor { 19 | constructor() {} 20 | 21 | intercept(req: HttpRequest, next: HttpHandler): Observable> { 22 | return next.handle(req).pipe( 23 | catchError((error, caught) => { 24 | console.warn('test', error); 25 | if (error instanceof HttpErrorResponse) { 26 | const httpError: HttpErrorResponse = error; 27 | console.log( 28 | `Es ist ein Fehler beim Zugriff auf '${req.url}' aufgetreten: ${httpError.message}` 29 | ); 30 | } 31 | 32 | // return the error to the method that called it 33 | return observableThrowError(error); 34 | }) 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/shared/shared.module.ts: -------------------------------------------------------------------------------- 1 | import { HttpErrorInterceptor } from './http-error.interceptor'; 2 | import { NgModule, ModuleWithProviders } from '@angular/core'; 3 | import { CommonModule } from '@angular/common'; 4 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 5 | import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; 6 | 7 | import * as fromDirectives from './directives'; 8 | import { AuthInterceptor } from './auth.interceptor'; 9 | 10 | // https://angular.io/guide/styleguide#shared-feature-module 11 | 12 | @NgModule({ 13 | imports: [CommonModule], 14 | exports: [CommonModule, 15 | HttpClientModule, 16 | FormsModule, 17 | ReactiveFormsModule, 18 | ...fromDirectives.directives], 19 | declarations: [...fromDirectives.directives], 20 | providers: [ 21 | { 22 | provide: HTTP_INTERCEPTORS, 23 | useClass: AuthInterceptor, 24 | multi: true 25 | }, 26 | { 27 | provide: HTTP_INTERCEPTORS, 28 | useClass: HttpErrorInterceptor, 29 | multi: true 30 | } 31 | ] 32 | }) 33 | export class SharedModule { 34 | } 35 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/store/actions/index.ts: -------------------------------------------------------------------------------- 1 | export * from './router.action'; 2 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/store/actions/router.action.ts: -------------------------------------------------------------------------------- 1 | import { NavigationExtras } from '@angular/router'; 2 | import { createAction, props } from '@ngrx/store'; 3 | 4 | export const go = createAction( 5 | '[Router] Go', 6 | props<{ 7 | payload: { 8 | path: any[]; 9 | query?: object; 10 | extras?: NavigationExtras; 11 | }; 12 | }>() 13 | ); 14 | 15 | export const back = createAction('[Router] Back'); 16 | 17 | export const forward = createAction('[Router] Forward'); 18 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/store/effects/index.ts: -------------------------------------------------------------------------------- 1 | import { RouterEffects } from './router.effect'; 2 | 3 | export const effects: any[] = [RouterEffects]; 4 | 5 | export * from './router.effect'; 6 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/store/effects/router.effect.ts: -------------------------------------------------------------------------------- 1 | import { Location } from '@angular/common'; 2 | import { Injectable } from '@angular/core'; 3 | import { Router } from '@angular/router'; 4 | import { Actions, createEffect, ofType } from '@ngrx/effects'; 5 | import { tap } from 'rxjs/operators'; 6 | import * as routerActions from '../actions/router.action'; 7 | 8 | @Injectable() 9 | export class RouterEffects { 10 | constructor(private actions$: Actions, private router: Router, private location: Location) {} 11 | 12 | navigate$ = createEffect( 13 | () => 14 | this.actions$.pipe( 15 | ofType(routerActions.go), 16 | tap(({ payload }) => { 17 | this.router.navigate(payload.path, { queryParams: payload.query, ...payload.extras }); 18 | }) 19 | ), 20 | { dispatch: false } 21 | ); 22 | 23 | navigateBack$ = createEffect( 24 | () => 25 | this.actions$.pipe( 26 | ofType(routerActions.back), 27 | tap(() => this.location.back()) 28 | ), 29 | { dispatch: false } 30 | ); 31 | 32 | navigateForward$ = createEffect( 33 | () => 34 | this.actions$.pipe( 35 | ofType(routerActions.forward), 36 | tap(() => this.location.forward()) 37 | ), 38 | { dispatch: false } 39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/store/index.ts: -------------------------------------------------------------------------------- 1 | export * from './reducers'; 2 | export * from './actions'; 3 | export * from './effects'; 4 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/store/reducers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './router.reducer'; 2 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/store/reducers/router.reducer.ts: -------------------------------------------------------------------------------- 1 | import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; 2 | import * as fromRouter from '@ngrx/router-store'; 3 | import { ActionReducerMap, createFeatureSelector } from '@ngrx/store'; 4 | import * as fromRoot from '../state/app.state'; 5 | import { Injectable } from "@angular/core"; 6 | 7 | export const reducers: ActionReducerMap = { 8 | routerReducer: fromRouter.routerReducer 9 | }; 10 | 11 | export const getRouterState = createFeatureSelector< 12 | fromRouter.RouterReducerState 13 | >('routerReducer'); 14 | 15 | @Injectable() 16 | export class CustomSerializer implements fromRouter.RouterStateSerializer { 17 | serialize(routerState: RouterStateSnapshot): fromRoot.RouterStateUrl { 18 | const { url } = routerState; 19 | const { queryParams } = routerState.root; 20 | 21 | let state: ActivatedRouteSnapshot = routerState.root; 22 | while (state.firstChild) { 23 | state = state.firstChild; 24 | } 25 | const { params } = state; 26 | 27 | return { url, queryParams, params }; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/app/store/state/app.state.ts: -------------------------------------------------------------------------------- 1 | import { Params } from '@angular/router'; 2 | import * as fromRouter from '@ngrx/router-store'; 3 | 4 | export interface AppState { 5 | routerReducer: fromRouter.RouterReducerState; 6 | } 7 | 8 | export interface RouterStateUrl { 9 | url: string; 10 | queryParams: Params; 11 | params: Params; 12 | } 13 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trivadis/AdvancedAngular/b3cfb1fc8957894cf2dc9cc6ff7ce638f071d89f/final/ngTvdTraining/src/assets/.gitkeep -------------------------------------------------------------------------------- /final/ngTvdTraining/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | 4 | // Use Node Express 5 | apiBaseUrl: 'http://localhost:8180/api' 6 | 7 | // Use DENO 8 | // apiBaseUrl: 'http://localhost:8280' 9 | }; 10 | -------------------------------------------------------------------------------- /final/ngTvdTraining/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 | // Use Node Express 10 | apiBaseUrl: 'http://localhost:8180/api' 11 | 12 | // Use DENO 13 | // apiBaseUrl: 'http://localhost:8280' 14 | }; 15 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trivadis/AdvancedAngular/b3cfb1fc8957894cf2dc9cc6ff7ce638f071d89f/final/ngTvdTraining/src/favicon.ico -------------------------------------------------------------------------------- /final/ngTvdTraining/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NgTvdTraining 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.log(err)); 13 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags.ts'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | /*************************************************************************************************** 61 | * APPLICATION IMPORTS 62 | */ 63 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/styles.scss: -------------------------------------------------------------------------------- 1 | body { 2 | // padding: 10px 25px; 3 | } 4 | 5 | a, 6 | a:hover, 7 | a:focus { 8 | color: #c92c21; 9 | } 10 | 11 | #pageBanner { 12 | background-image: url('https://m.trivadis.com/hubfs/TVD_Training/Landingpages/Angular/Angular-Training.jpg'); 13 | height: 280px; 14 | background-size: cover; 15 | background-repeat: no-repeat; 16 | background-position: center -165px; 17 | padding: 15px 30px; 18 | } 19 | #pageBanner h1 { 20 | font-size: 38px; 21 | color: #fff; 22 | font-weight: 700; 23 | line-height: 74px !important; 24 | margin-bottom: 0; 25 | text-shadow: 0 0 40px rgba(0, 0, 0, 0.75); 26 | } 27 | header { 28 | nav { 29 | margin: 5px 0; 30 | } 31 | nav { 32 | a { 33 | font-size: 1em; 34 | // background-color: #cdcdcd; 35 | padding: 5px; 36 | margin-right: 10px; 37 | } 38 | .active { 39 | color: black; 40 | } 41 | } 42 | } 43 | main { 44 | padding: 10px 0; 45 | } 46 | footer { 47 | font-size: 0.85em; 48 | padding-bottom: 2rem; 49 | padding-top: 1em; 50 | } 51 | .panel-primary > .panel-heading { 52 | background-color: #c92c21; 53 | border-color: #c92c21; 54 | } 55 | 56 | .btn { 57 | margin-right: 10px; 58 | margin-bottom: 10px; 59 | } 60 | .btn-primary { 61 | color: #fff; 62 | background-color: #c92c21; 63 | border-color: #c92c21; 64 | } 65 | .btn-primary:hover { 66 | color: #fff; 67 | background-color: #8c0000; 68 | border-color: #8c0000; 69 | transition: all 300ms linear; 70 | } 71 | 72 | .selected { 73 | background-color: #8c0000 !important; 74 | color: #fff !important; 75 | } 76 | 77 | /* Fade transition 78 | app-root main { 79 | animation-duration: .5s; 80 | opacity: 0; 81 | animation-name: fadeIn; 82 | animation-fill-mode: forwards; 83 | transition: transform .5s ease; 84 | } 85 | 86 | @keyframes fadeIn { 87 | from { 88 | opacity: 0; 89 | } 90 | 91 | to { 92 | opacity: 1; 93 | } 94 | } 95 | */ 96 | 97 | /* Slide transition*/ 98 | app-root main { 99 | animation-duration: .5s; 100 | animation-name: slideIn; 101 | animation-fill-mode: forwards; 102 | transition: transform .5s ease; 103 | } 104 | 105 | @keyframes slideIn { 106 | from { 107 | transform: translate(-200px); 108 | } 109 | 110 | to { 111 | transform: translate(0px); 112 | } 113 | } 114 | 115 | -------------------------------------------------------------------------------- /final/ngTvdTraining/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/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": [ 8 | "main.ts", 9 | "polyfills.ts" 10 | ], 11 | "include": ["**/*.d.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "types": ["jasmine", "node"] 6 | }, 7 | "files": ["test.ts", "polyfills.ts"], 8 | "include": ["**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /final/ngTvdTraining/src/typings.d.ts: -------------------------------------------------------------------------------- 1 | /* SystemJS module definition */ 2 | declare var module: NodeModule; 3 | interface NodeModule { 4 | id: string; 5 | } 6 | -------------------------------------------------------------------------------- /final/ngTvdTraining/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "moduleResolution": "node", 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "target": "es2015", 12 | "typeRoots": ["node_modules/@types"], 13 | "lib": ["es2018", "dom"], 14 | "module": "es2020" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /final/ngTvdTraining/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "align": { 5 | "options": [ 6 | "parameters", 7 | "statements" 8 | ] 9 | }, 10 | "array-type": false, 11 | "arrow-parens": false, 12 | "arrow-return-shorthand": true, 13 | "deprecation": { 14 | "severity": "warn" 15 | }, 16 | "component-class-suffix": true, 17 | "contextual-lifecycle": true, 18 | "curly": true, 19 | "directive-class-suffix": true, 20 | "directive-selector": [true, "attribute", "app", "camelCase"], 21 | "component-selector": [ 22 | true, 23 | "element", 24 | "app", 25 | "kebab-case" 26 | ], 27 | "eofline": true, 28 | "import-blacklist": [ 29 | true, 30 | "rxjs/Rx" 31 | ], 32 | "import-spacing": true, 33 | "indent": { 34 | "options": [ 35 | "spaces" 36 | ] 37 | }, 38 | "interface-name": false, 39 | "max-classes-per-file": false, 40 | "max-line-length": [true, 140], 41 | "member-access": false, 42 | "member-ordering": [ 43 | true, 44 | { 45 | "order": ["static-field", "instance-field", "static-method", "instance-method"] 46 | } 47 | ], 48 | "no-consecutive-blank-lines": false, 49 | "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], 50 | "no-empty": false, 51 | "no-inferrable-types": [true, "ignore-params"], 52 | "no-non-null-assertion": true, 53 | "no-redundant-jsdoc": true, 54 | "no-switch-case-fall-through": true, 55 | "no-var-requires": false, 56 | "object-literal-key-quotes": [true, "as-needed"], 57 | "object-literal-sort-keys": false, 58 | "ordered-imports": false, 59 | "quotemark": [true, "single"], 60 | "trailing-comma": false, 61 | "no-conflicting-lifecycle": true, 62 | "no-host-metadata-property": true, 63 | "no-input-rename": true, 64 | "no-inputs-metadata-property": true, 65 | "no-output-native": true, 66 | "no-output-on-prefix": true, 67 | "no-output-rename": true, 68 | "semicolon": { 69 | "options": [ 70 | "always" 71 | ] 72 | }, 73 | "space-before-function-paren": { 74 | "options": { 75 | "anonymous": "never", 76 | "asyncArrow": "always", 77 | "constructor": "never", 78 | "method": "never", 79 | "named": "never" 80 | } 81 | }, 82 | "no-outputs-metadata-property": true, 83 | "template-banana-in-box": true, 84 | "template-no-negated-async": true, 85 | "typedef-whitespace": { 86 | "options": [ 87 | { 88 | "call-signature": "nospace", 89 | "index-signature": "nospace", 90 | "parameter": "nospace", 91 | "property-declaration": "nospace", 92 | "variable-declaration": "nospace" 93 | }, 94 | { 95 | "call-signature": "onespace", 96 | "index-signature": "onespace", 97 | "parameter": "onespace", 98 | "property-declaration": "onespace", 99 | "variable-declaration": "onespace" 100 | } 101 | ] 102 | }, 103 | "use-lifecycle-interface": true, 104 | "use-pipe-transform-interface": true, 105 | "variable-name": { 106 | "options": [ 107 | "ban-keywords", 108 | "check-format", 109 | "allow-pascal-case" 110 | ] 111 | }, 112 | "whitespace": { 113 | "options": [ 114 | "check-branch", 115 | "check-decl", 116 | "check-operator", 117 | "check-separator", 118 | "check-type", 119 | "check-typecast" 120 | ] 121 | } 122 | }, 123 | "rulesDirectory": ["codelyzer"] 124 | } 125 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/.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 | quote_type = single 11 | 12 | [*.md] 13 | max_line_length = off 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /dist-server 6 | /tmp 7 | /out-tsc 8 | 9 | # dependencies 10 | /node_modules 11 | 12 | # IDEs and editors 13 | /.idea 14 | .project 15 | .classpath 16 | .c9/ 17 | *.launch 18 | .settings/ 19 | *.sublime-workspace 20 | 21 | # IDE - VSCode 22 | .vscode/* 23 | !.vscode/settings.json 24 | !.vscode/tasks.json 25 | !.vscode/launch.json 26 | !.vscode/extensions.json 27 | 28 | # misc 29 | /.sass-cache 30 | /connect.lock 31 | /coverage 32 | /libpeerconnection.log 33 | npm-debug.log 34 | yarn-error.log 35 | testem.log 36 | /typings 37 | 38 | # e2e 39 | /e2e/*.js 40 | /e2e/*.map 41 | 42 | # System Files 43 | .DS_Store 44 | Thumbs.db 45 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/README.md: -------------------------------------------------------------------------------- 1 | # Advanced Angular 2 | 3 | Contains the final project without NgRx for the Advanced Angular training at Trivadis: 4 | https://www.trivadis.com/de/training/advanced-angular-ad-angular-adv 5 | 6 | ``` 7 | https://m.trivadis.com/angular-schulung 8 | ``` 9 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('employee-portal App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display title correctly', () => { 11 | page.navigateTo('/'); 12 | expect(page.getText('app-root h2')).toEqual('Employee Portal'); 13 | }); 14 | 15 | it('should navigate to employee page', () => { 16 | page.navigateTo('/'); 17 | 18 | page.getElement('[ng-reflect-router-link="/employees"]').click(); 19 | 20 | expect(page.getCurrentUrl()).toContain('/employees'); 21 | }); 22 | 23 | it('should search correct', () => { 24 | page.navigateTo('/employees'); 25 | 26 | page.search('Thomas'); 27 | 28 | expect(page.getResult().count()).toEqual(3); 29 | }); 30 | 31 | it('should edit a person correctly', () => { 32 | page.navigateTo('/employees'); 33 | page.takeScreenshot(1); 34 | 35 | page.getElement('table a.btn.btn-ok').click(); 36 | page.takeScreenshot(2); 37 | 38 | const inputField = page.getElement('[formcontrolname="lastname"]'); 39 | inputField.clear(); 40 | page.takeScreenshot(3); 41 | 42 | inputField.sendKeys('Wayne'); 43 | page.takeScreenshot(4); 44 | 45 | const email = page.getElement('[formcontrolname="email"]').getAttribute('value'); 46 | page.getElement('[formcontrolname="emailConfirm"]').sendKeys(email); 47 | page.takeScreenshot(5); 48 | 49 | page.getElement('button.btn.btn-ok').click(); 50 | // page.acceptAlert(); 51 | page.takeScreenshot(6); 52 | 53 | expect(page.getLastname(0)).toEqual('Wayne'); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import { browser, by, element } from 'protractor'; 3 | 4 | export class AppPage { 5 | navigateTo(url: string) { 6 | return browser.get(url); 7 | } 8 | 9 | getElement(selector: string) { 10 | return element(by.css(selector)); 11 | } 12 | 13 | getText(selector: string) { 14 | return this.getElement(selector).getText(); 15 | } 16 | 17 | getCurrentUrl() { 18 | return browser.getCurrentUrl(); 19 | } 20 | 21 | search(search: string) { 22 | this.getElement('input[type="search"]').clear(); 23 | this.getElement('input[type="search"]').sendKeys(search); 24 | } 25 | 26 | getResult() { 27 | return element.all(by.css('table.table tbody tr')); 28 | } 29 | 30 | takeScreenshot(idx: number) { 31 | return browser.takeScreenshot().then(png => { 32 | const stream = fs.createWriteStream('screenshot-' + idx + '.png'); 33 | stream.write(new Buffer(png, 'base64')); 34 | stream.end(); 35 | }); 36 | } 37 | 38 | getLastname(idx: number) { 39 | return element(by.css('table.table tbody tr td:nth-child(3)')).getText(); 40 | } 41 | 42 | acceptAlert() { 43 | browser 44 | .switchTo() 45 | .alert() 46 | .accept(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": [ 9 | "jasmine", 10 | "jasminewd2", 11 | "node" 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-firefox-launcher'), 12 | require('karma-jasmine-html-reporter'), 13 | require('karma-coverage-istanbul-reporter'), 14 | require('@angular-devkit/build-angular/plugins/karma') 15 | ], 16 | client: { 17 | clearContext: false // leave Jasmine Spec Runner output visible in browser 18 | }, 19 | coverageIstanbulReporter: { 20 | dir: require('path').join(__dirname, 'coverage'), 21 | reports: ['html', 'lcovonly'], 22 | fixWebpackSourcePaths: true 23 | }, 24 | 25 | reporters: ['progress', 'kjhtml'], 26 | port: 9876, 27 | colors: true, 28 | logLevel: config.LOG_INFO, 29 | autoWatch: true, 30 | browsers: ['Chrome', 'Firefox'], 31 | singleRun: false 32 | }); 33 | }; 34 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ng-tvd-training", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "ng": "ng", 7 | "start": "ng serve", 8 | "build": "ng build --prod", 9 | "test": "ng test", 10 | "lint": "ng lint", 11 | "e2e": "ng e2e" 12 | }, 13 | "private": true, 14 | "dependencies": { 15 | "@angular/animations": "11.0.3", 16 | "@angular/common": "11.0.3", 17 | "@angular/compiler": "11.0.3", 18 | "@angular/core": "11.0.3", 19 | "@angular/forms": "11.0.3", 20 | "@angular/platform-browser": "11.0.3", 21 | "@angular/platform-browser-dynamic": "11.0.3", 22 | "@angular/router": "11.0.3", 23 | "@ngrx/effects": "10.1.0", 24 | "@ngrx/router-store": "10.1.0", 25 | "@ngrx/schematics": "^10.1.0", 26 | "@ngrx/store": "10.1.0", 27 | "@ngrx/store-devtools": "10.1.0", 28 | "bootstrap": "4.5.3", 29 | "rxjs": "6.6.3", 30 | "tslib": "^2.0.0", 31 | "zone.js": "~0.11.3" 32 | }, 33 | "devDependencies": { 34 | "@angular-devkit/build-angular": "~0.1100.3", 35 | "@angular/cli": "~11.0.3", 36 | "@angular/compiler-cli": "~11.0.3", 37 | "@angular/language-service": "~11.0.3", 38 | "@types/node": "^12.11.1", 39 | "@types/jasmine": "~3.6.0", 40 | "@types/jasminewd2": "~2.0.6", 41 | "codelyzer": "^6.0.0", 42 | "jasmine-core": "~3.6.0", 43 | "jasmine-spec-reporter": "~6.0.0", 44 | "karma": "~5.1.1", 45 | "karma-chrome-launcher": "~3.1.0", 46 | "karma-firefox-launcher": "^2.1.0", 47 | "karma-coverage-istanbul-reporter": "~3.0.3", 48 | "karma-jasmine": "~4.0.1", 49 | "karma-jasmine-html-reporter": "^1.5.4", 50 | "ng-packagr": "^11.0.3", 51 | "protractor": "~7.0.0", 52 | "ts-node": "~7.0.0", 53 | "tslint": "~6.1.0", 54 | "typescript": "4.0.5" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/projects/auto-focus/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Build 3 | 4 | Run `ng build autoFocus` to build the project. The build artifacts will be stored in the `dist/` directory. 5 | 6 | ## Publishing 7 | 8 | After building your library with `ng build autoFocus`, go to the dist folder `cd dist/auto-focus` and run `npm publish`. 9 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/projects/auto-focus/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../coverage/auto-focus'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/projects/auto-focus/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/auto-focus", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | } 7 | } -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/projects/auto-focus/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "auto-focus", 3 | "version": "0.0.2", 4 | "peerDependencies": { 5 | "@angular/common": "^11.0.3", 6 | "@angular/core": "^11.0.3" 7 | }, 8 | "dependencies": { 9 | "tslib": "^2.0.0" 10 | } 11 | } -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/projects/auto-focus/src/lib/auto-focus.directive.ts: -------------------------------------------------------------------------------- 1 | import { AfterViewInit, Directive, ElementRef } from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: '[libAutoFocus]' 5 | }) 6 | export class AutoFocusDirective implements AfterViewInit { 7 | public constructor(private el: ElementRef) {} 8 | public ngAfterViewInit() { 9 | setTimeout(() => { 10 | this.el.nativeElement.focus(); 11 | }); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/projects/auto-focus/src/lib/auto-focus.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { AutoFocusDirective } from './auto-focus.directive'; 3 | 4 | @NgModule({ 5 | declarations: [AutoFocusDirective], 6 | imports: [], 7 | exports: [AutoFocusDirective] 8 | }) 9 | export class AutoFocusModule {} 10 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/projects/auto-focus/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of auto-focus 3 | */ 4 | 5 | export * from './lib/auto-focus.directive'; 6 | export * from './lib/auto-focus.module'; 7 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/projects/auto-focus/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import { getTestBed } from '@angular/core/testing'; 4 | import { 5 | BrowserDynamicTestingModule, 6 | platformBrowserDynamicTesting 7 | } from '@angular/platform-browser-dynamic/testing'; 8 | import 'zone.js/dist/zone'; 9 | import 'zone.js/dist/zone-testing'; 10 | 11 | declare const require: { 12 | context(path: string, deep?: boolean, filter?: RegExp): { 13 | keys(): string[]; 14 | (id: string): T; 15 | }; 16 | }; 17 | 18 | // First, initialize the Angular testing environment. 19 | getTestBed().initTestEnvironment( 20 | BrowserDynamicTestingModule, 21 | platformBrowserDynamicTesting() 22 | ); 23 | // Then we find all the tests. 24 | const context = require.context('./', true, /\.spec\.ts$/); 25 | // And load the modules. 26 | context.keys().map(context); 27 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/projects/auto-focus/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/lib", 6 | "target": "es2015", 7 | "declaration": true, 8 | "declarationMap": true, 9 | "inlineSources": true, 10 | "types": [], 11 | "lib": ["dom", "es2018"] 12 | }, 13 | "angularCompilerOptions": { 14 | "skipTemplateCodegen": true, 15 | "strictMetadataEmit": true, 16 | "fullTemplateTypeCheck": true, 17 | "strictInjectionParameters": true, 18 | "enableResourceInlining": true 19 | }, 20 | "exclude": ["src/test.ts", "**/*.spec.ts"] 21 | } 22 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/projects/auto-focus/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.lib.json", 4 | "compilerOptions": { 5 | "declarationMap": false 6 | }, 7 | "angularCompilerOptions": { 8 | "enableIvy": false 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/projects/auto-focus/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts" 12 | ], 13 | "include": [ 14 | "**/*.spec.ts", 15 | "**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/projects/auto-focus/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "lib", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "lib", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/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 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './e2e/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: 'e2e/tsconfig.e2e.json' 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trivadis/AdvancedAngular/b3cfb1fc8957894cf2dc9cc6ff7ce638f071d89f/finalOhneNgRx/ngTvdTraining/screenshot-1.png -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trivadis/AdvancedAngular/b3cfb1fc8957894cf2dc9cc6ff7ce638f071d89f/finalOhneNgRx/ngTvdTraining/screenshot-2.png -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/screenshot-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trivadis/AdvancedAngular/b3cfb1fc8957894cf2dc9cc6ff7ce638f071d89f/finalOhneNgRx/ngTvdTraining/screenshot-3.png -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/screenshot-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trivadis/AdvancedAngular/b3cfb1fc8957894cf2dc9cc6ff7ce638f071d89f/finalOhneNgRx/ngTvdTraining/screenshot-4.png -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/screenshot-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trivadis/AdvancedAngular/b3cfb1fc8957894cf2dc9cc6ff7ce638f071d89f/finalOhneNgRx/ngTvdTraining/screenshot-5.png -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/screenshot-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trivadis/AdvancedAngular/b3cfb1fc8957894cf2dc9cc6ff7ce638f071d89f/finalOhneNgRx/ngTvdTraining/screenshot-6.png -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/about/about-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | 4 | import { AboutListComponent } from './containers/about-list/about-list.component'; 5 | 6 | const routes: Routes = [ 7 | { path: '', component: AboutListComponent }, 8 | ]; 9 | 10 | @NgModule({ 11 | imports: [RouterModule.forChild(routes)], 12 | exports: [RouterModule], 13 | }) 14 | export class AboutRoutingModule { } 15 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/about/about.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { AboutListComponent } from './containers/about-list/about-list.component'; 4 | import { AboutRoutingModule } from './about-routing.module'; 5 | 6 | @NgModule({ 7 | imports: [AboutRoutingModule], 8 | exports: [], 9 | declarations: [AboutListComponent], 10 | providers: [] 11 | }) 12 | export class AboutModule {} 13 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/about/containers/about-list/about-list.component.html: -------------------------------------------------------------------------------- 1 | About - Lazy Loading -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/about/containers/about-list/about-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | templateUrl: 'about-list.component.html' 5 | }) 6 | export class AboutListComponent implements OnInit { 7 | constructor() { } 8 | 9 | ngOnInit() { } 10 | } 11 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | import { AppCustomPreloader } from './core/app-custom-preloader'; 4 | import { StandardLayoutComponent } from './layout/standard-layout/standard-layout.component'; 5 | import { WelcomeComponent } from './layout/welcome/welcome.component'; 6 | 7 | const routes: Routes = [ 8 | { 9 | path: '', 10 | component: StandardLayoutComponent, 11 | children: [ 12 | { path: '', redirectTo: 'welcome', pathMatch: 'full' }, 13 | { path: 'welcome', component: WelcomeComponent }, 14 | { 15 | path: 'employees', 16 | loadChildren: () => import('./employee/employee.module').then(m => m.EmployeeModule), 17 | data: { preload: false } 18 | }, 19 | { 20 | path: 'about', 21 | loadChildren: () => import('./about/about.module').then(m => m.AboutModule), 22 | data: { preload: true } 23 | } 24 | ] 25 | } 26 | ]; 27 | 28 | @NgModule({ 29 | imports: [RouterModule.forRoot(routes, { preloadingStrategy: AppCustomPreloader })], 30 | exports: [RouterModule], 31 | providers: [] 32 | }) 33 | export class AppRoutingModule {} 34 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, waitForAsync } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(waitForAsync(() => { 7 | TestBed.configureTestingModule({ 8 | declarations: [AppComponent], 9 | imports: [RouterTestingModule] 10 | }).compileComponents(); 11 | })); 12 | it('should create the app', waitForAsync(() => { 13 | const fixture = TestBed.createComponent(AppComponent); 14 | const app = fixture.debugElement.componentInstance; 15 | expect(app).toBeTruthy(); 16 | })); 17 | }); 18 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | 4 | @Component({ 5 | selector: 'app-root', 6 | template: '' 7 | }) 8 | export class AppComponent { 9 | } 10 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { LayoutModule } from './layout/layout.module'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 4 | import { NgModule } from '@angular/core'; 5 | 6 | import { AppComponent } from './app.component'; 7 | import { CoreModule } from './core/core.module'; 8 | import { AppRoutingModule } from './app-routing.module'; 9 | 10 | import { SharedModule } from './shared/shared.module'; 11 | import { EmployeeModule } from './employee/employee.module'; 12 | import { HTTP_INTERCEPTORS } from '@angular/common/http'; 13 | 14 | @NgModule({ 15 | imports: [ 16 | BrowserModule, 17 | BrowserAnimationsModule, 18 | SharedModule, 19 | CoreModule.forRoot(), 20 | LayoutModule, 21 | AppRoutingModule 22 | ], 23 | declarations: [AppComponent], 24 | providers: [ 25 | ], 26 | bootstrap: [AppComponent] 27 | }) 28 | export class AppModule {} 29 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/core/app-custom-preloader.ts: -------------------------------------------------------------------------------- 1 | import { PreloadingStrategy, Route } from '@angular/router'; 2 | 3 | import { Observable , of } from 'rxjs'; 4 | 5 | export class AppCustomPreloader implements PreloadingStrategy { 6 | preload(route: Route, load: Function): Observable { 7 | return route.data && route.data.preload ? load() : of(null); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/core/core.module.ts: -------------------------------------------------------------------------------- 1 | import { AppCustomPreloader } from './app-custom-preloader'; 2 | import { NgModule, Optional, SkipSelf, ModuleWithProviders } from '@angular/core'; 3 | import { CommonModule } from '@angular/common'; 4 | import { SuperService } from './super-service'; 5 | import { HTTP_INTERCEPTORS } from '@angular/common/http'; 6 | 7 | // https://angular.io/guide/styleguide#core-feature-module 8 | 9 | @NgModule({ 10 | imports: [CommonModule], 11 | exports: [], 12 | declarations: [] 13 | }) 14 | export class CoreModule { 15 | constructor( 16 | @Optional() 17 | @SkipSelf() 18 | parentModule: CoreModule 19 | ) { 20 | if (parentModule) { 21 | throw new Error(`Core has already been loaded. Import Core modules in the AppModule only.`); 22 | } 23 | } 24 | 25 | static forRoot(): ModuleWithProviders { 26 | return { 27 | ngModule: CoreModule, 28 | providers: [ 29 | SuperService, 30 | AppCustomPreloader 31 | ] 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/core/super-service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable() 4 | export class SuperService { 5 | constructor() {} 6 | 7 | get(): Boolean { 8 | return true; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/components/employee-form/employee-form.component.html: -------------------------------------------------------------------------------- 1 |

{{ title }}

2 |
3 |
4 | 5 |
6 | 13 |
14 |
18 | 19 | Firstname is required. 20 | 21 |
22 |
23 |
24 | 25 |
26 | 32 |
33 |
37 | 38 | Lastname is required. 39 | 40 |
41 |
42 |
43 | 44 |
45 | 46 |
47 |
51 | 52 | E-Mail is required. 53 | 54 | 55 | E-Mail is invalid. 56 | 57 | 58 | E-Mail is not unique. 59 | 60 |
61 |
62 |
63 | 64 |
65 | 66 |
67 |
71 | 72 | E-Mail is required. 73 | 74 | 75 | E-Mail is invalid. 76 | 77 |
78 |
82 | 83 | E-Mails must match. 84 | 85 |
86 |
87 | 88 |
89 | 92 | 95 | 98 |
99 | 100 | 101 |
102 |     {{form.value | json}}
103 |   
104 | 105 |
106 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/components/employee-form/employee-form.component.scss: -------------------------------------------------------------------------------- 1 | // .ng-valid:not(form), .ng-valid[required], .ng-valid.required { 2 | // border-left: 5px solid #42A948; /* green */ 3 | // } 4 | 5 | // .ng-invalid:not(form) { 6 | // border-left: 5px solid #a94442; /* red */ 7 | // } -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/components/employee-form/employee-form.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | EventEmitter, 4 | Input, 5 | OnChanges, 6 | OnInit, 7 | Output, 8 | SimpleChanges 9 | } from '@angular/core'; 10 | import { FormBuilder, FormGroup, Validators } from '@angular/forms'; 11 | import { Employee } from '../../model/employee.model'; 12 | import { EmployeeService } from '../../services'; 13 | import { EmployeeValidators } from './../../validators/employee.validator'; 14 | 15 | @Component({ 16 | selector: 'app-employee-form', 17 | templateUrl: 'employee-form.component.html', 18 | styleUrls: ['employee-form.component.scss'], 19 | }) 20 | export class EmployeeFormComponent implements OnInit, OnChanges { 21 | @Input() employee: Employee; 22 | 23 | @Output() create = new EventEmitter(); 24 | @Output() update = new EventEmitter(); 25 | @Output() remove = new EventEmitter(); 26 | 27 | form: FormGroup = this.fb.group( 28 | { 29 | firstname: [ 30 | '', 31 | { 32 | validators: Validators.required, 33 | updateOn: 'blur', 34 | }, 35 | ], 36 | lastname: ['', Validators.required], 37 | email: [ 38 | '', 39 | [Validators.required, EmployeeValidators.emailValidator], 40 | EmployeeValidators.checkEmailUnique(this.service), 41 | ], 42 | // email: [ 43 | // '', 44 | // { 45 | // validators: [Validators.required, EmployeeValidators.emailValidator], 46 | // asyncValidators: EmployeeValidators.checkEmailUnique(this.service), 47 | // updateOn: 'blur' 48 | // } 49 | // ], 50 | emailConfirm: [ 51 | '', 52 | [Validators.required, EmployeeValidators.emailValidator], 53 | ], 54 | }, 55 | { 56 | validators: EmployeeValidators.checkEmailsMatch, 57 | // updateOn: 'blur' 58 | } 59 | ); 60 | 61 | isEdit = false; 62 | title = 'Create'; 63 | 64 | constructor(private fb: FormBuilder, private service: EmployeeService) {} 65 | 66 | ngOnInit() {} 67 | 68 | ngOnChanges(changes: SimpleChanges) { 69 | if (this.employee && this.employee.id) { 70 | this.isEdit = true; 71 | this.title = 'Edit'; 72 | this.form.patchValue(this.employee); 73 | } 74 | } 75 | createEmployee() { 76 | if (this.form.valid) { 77 | this.create.emit(this.form.value); 78 | } 79 | } 80 | 81 | updateEmployee() { 82 | this.form.markAllAsTouched(); 83 | if (this.form.touched && this.form.valid) { 84 | this.update.emit({ ...this.employee, ...this.form.value }); 85 | } 86 | } 87 | 88 | removeEmployee() { 89 | this.remove.emit({ ...this.employee, ...this.form.value }); 90 | } 91 | 92 | get firstname() { 93 | return this.form.get('firstname'); 94 | } 95 | get lastname() { 96 | return this.form.get('lastname'); 97 | } 98 | get email() { 99 | return this.form.get('email'); 100 | } 101 | get emailConfirm() { 102 | return this.form.get('emailConfirm'); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/components/index.ts: -------------------------------------------------------------------------------- 1 | import { EmployeeFormComponent } from './employee-form/employee-form.component'; 2 | 3 | export const components: any[] = [EmployeeFormComponent]; 4 | 5 | export * from './employee-form/employee-form.component'; 6 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/containers/employee-list/employee-list.component.html: -------------------------------------------------------------------------------- 1 |

Employees

2 | 7 |
8 |
Search:
9 |
10 | 11 |
12 |
13 |
14 |
15 | Filtered by: {{listFilter}} 16 |
17 |
18 |
19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 41 | 42 | 43 |
IdFirstnameLastnameE-Mail 
{{ employee.id }}{{ employee.firstname }}{{ employee.lastname }}{{ employee.email }} 37 | 38 | Edit 39 | 40 |
44 | Data are loaded 45 |
46 |
-------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/containers/employee-list/employee-list.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { NO_ERRORS_SCHEMA } from '@angular/core'; 2 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 3 | import { FormsModule } from '@angular/forms'; 4 | import { RouterTestingModule } from '@angular/router/testing'; 5 | import { LayoutModule } from '../../../layout/layout.module'; 6 | import { EmployeeFilterPipe } from '../../pipes/employee-filter.pipe'; 7 | import { EmployeeService } from '../../services/employee.service'; 8 | import { EmployeeListComponent } from './employee-list.component'; 9 | 10 | describe('EmployeeListComponent', () => { 11 | let fixture: ComponentFixture; 12 | let comp: EmployeeListComponent; 13 | 14 | beforeEach(waitForAsync(() => { 15 | TestBed.configureTestingModule({ 16 | imports: [RouterTestingModule, LayoutModule, FormsModule], 17 | declarations: [EmployeeListComponent, EmployeeFilterPipe], 18 | providers: [EmployeeService], 19 | schemas: [NO_ERRORS_SCHEMA] 20 | }).compileComponents(); 21 | })); 22 | 23 | beforeEach(() => { 24 | fixture = TestBed.createComponent(EmployeeListComponent); 25 | comp = fixture.componentInstance; 26 | fixture.detectChanges(); 27 | }); 28 | 29 | it('should create the app', waitForAsync(() => { 30 | expect(comp).toBeTruthy(); 31 | })); 32 | 33 | it('should render title in a h2 tag', waitForAsync(() => { 34 | const result = fixture.nativeElement.querySelector('h2').textContent; 35 | expect(result).toContain('Employees'); 36 | })); 37 | }); 38 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/containers/employee-list/employee-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; 2 | import { ActivatedRoute } from '@angular/router'; 3 | import { Observable } from 'rxjs'; 4 | import { Employee } from '../../model/employee.model'; 5 | import * as fromService from '../../services'; 6 | 7 | @Component({ 8 | templateUrl: 'employee-list.component.html', 9 | }) 10 | export class EmployeeListComponent implements OnInit { 11 | employees$: Observable; 12 | listFilter: string; 13 | 14 | @ViewChild('input') 15 | input: ElementRef; 16 | 17 | constructor( 18 | private service: fromService.EmployeeService, 19 | private route: ActivatedRoute 20 | ) {} 21 | 22 | ngOnInit() { 23 | this.employees$ = this.service.getEmployees(); 24 | this.listFilter = this.route.snapshot.queryParams['filterBy'] || ''; 25 | } 26 | 27 | keyClicked() { 28 | this.input.nativeElement.focus(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/containers/employee/employee.component.html: -------------------------------------------------------------------------------- 1 | 6 | > 7 | 8 | go back -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/containers/employee/employee.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, ViewChild } from '@angular/core'; 2 | import { ActivatedRoute, Router } from '@angular/router'; 3 | import { Observable } from 'rxjs'; 4 | import { EmployeeFormComponent } from '../../components/employee-form/employee-form.component'; 5 | import { Employee } from '../../model/employee.model'; 6 | import * as fromService from '../../services'; 7 | 8 | @Component({ 9 | templateUrl: 'employee.component.html' 10 | }) 11 | export class EmployeeComponent implements OnInit { 12 | employee$: Observable; 13 | 14 | @ViewChild(EmployeeFormComponent, { static: true }) 15 | employeeFormComp: EmployeeFormComponent; 16 | 17 | constructor( 18 | private service: fromService.EmployeeService, 19 | private router: Router, 20 | private route: ActivatedRoute 21 | ) {} 22 | 23 | ngOnInit() { 24 | this.route.paramMap.subscribe(p => { 25 | const eId = +p.get('employeeId'); 26 | if (eId > 0) { 27 | this.employee$ = this.service.getEmployee(eId); 28 | } 29 | }); 30 | } 31 | 32 | onCreate(event: Employee) { 33 | this.service.createEmployee(event).subscribe(() => { 34 | this.navigateBack(); 35 | }); 36 | } 37 | 38 | onUpdate(event: Employee) { 39 | this.service.updateEmployee(event).subscribe(() => { 40 | this.navigateBack(); 41 | }); 42 | } 43 | 44 | onRemove(event: Employee) { 45 | const remove = window.confirm('Are you sure?'); 46 | if (remove) { 47 | this.service.removeEmployee(event).subscribe(() => { 48 | this.navigateBack(); 49 | }); 50 | } 51 | } 52 | navigateBack() { 53 | this.router.navigate(['/employees'], { queryParamsHandling: 'preserve' }); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/containers/index.ts: -------------------------------------------------------------------------------- 1 | import { EmployeeListComponent } from './employee-list/employee-list.component'; 2 | import { EmployeeComponent } from './employee/employee.component'; 3 | 4 | export const containers: any[] = [EmployeeListComponent, EmployeeComponent]; 5 | 6 | export * from './employee-list/employee-list.component'; 7 | export * from './employee/employee.component'; 8 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/employee-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | 4 | import * as fromContainer from './containers'; 5 | import * as fromGuard from './guards'; 6 | 7 | const routes: Routes = [ 8 | { 9 | path: '', 10 | component: fromContainer.EmployeeListComponent 11 | }, 12 | { 13 | path: 'new', 14 | component: fromContainer.EmployeeComponent 15 | }, 16 | { 17 | path: ':employeeId', 18 | component: fromContainer.EmployeeComponent, 19 | canActivate: [fromGuard.EmployeeEditGuard], 20 | canDeactivate: [fromGuard.EmployeeEditGuard] 21 | } 22 | ]; 23 | 24 | @NgModule({ 25 | imports: [RouterModule.forChild(routes)], 26 | exports: [RouterModule] 27 | }) 28 | export class EmployeeRoutingModule {} 29 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/employee.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { AutoFocusModule } from 'auto-focus'; 3 | import { SharedModule } from '../shared/shared.module'; 4 | import * as fromComponents from './components'; 5 | import * as fromContainers from './containers'; 6 | import { EmployeeRoutingModule } from './employee-routing.module'; 7 | import * as fromGuards from './guards'; 8 | import * as fromPipes from './pipes'; 9 | import * as fromServices from './services'; 10 | import { IsEmailValidator } from './validators/check-email-match.validator'; 11 | 12 | @NgModule({ 13 | imports: [SharedModule, EmployeeRoutingModule, AutoFocusModule], 14 | exports: [], 15 | declarations: [ 16 | ...fromContainers.containers, 17 | ...fromComponents.components, 18 | ...fromPipes.pipes, 19 | IsEmailValidator 20 | ], 21 | providers: [...fromServices.services, ...fromGuards.guards] 22 | }) 23 | export class EmployeeModule { } 24 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/guards/employee-edit.guard.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { 3 | ActivatedRouteSnapshot, 4 | CanActivate, 5 | CanDeactivate, 6 | Router, 7 | RouterStateSnapshot 8 | } from '@angular/router'; 9 | import { EmployeeComponent } from './../containers/employee/employee.component'; 10 | 11 | @Injectable() 12 | export class EmployeeEditGuard implements CanActivate, CanDeactivate { 13 | constructor(private router: Router) {} 14 | 15 | canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { 16 | const employeeId = +route.paramMap.get('employeeId'); 17 | 18 | if (isNaN(employeeId)) { 19 | this.router.navigate(['/employees']); 20 | } else { 21 | return true; 22 | } 23 | } 24 | canDeactivate(component: EmployeeComponent): boolean { 25 | if (!component.employeeFormComp.form.valid && component.employeeFormComp.form.touched) { 26 | return confirm(`Navigate away and lose all changes?`); 27 | } 28 | return true; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/guards/index.ts: -------------------------------------------------------------------------------- 1 | import { EmployeeEditGuard } from './employee-edit.guard'; 2 | 3 | export const guards: any[] = [EmployeeEditGuard]; 4 | 5 | export * from './employee-edit.guard'; 6 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/model/device.model.ts: -------------------------------------------------------------------------------- 1 | export interface Device { 2 | id?: number; 3 | name?: string; 4 | price?: number; 5 | photo?: string; 6 | } 7 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/model/employee.model.ts: -------------------------------------------------------------------------------- 1 | export interface Employee { 2 | id?: number; 3 | firstname?: string; 4 | lastname?: string; 5 | email?: string; 6 | } 7 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/pipes/employee-filter.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { EmployeeFilterPipe } from './employee-filter.pipe'; 2 | 3 | import { TestBed, async } from '@angular/core/testing'; 4 | import { Employee } from '../model/employee.model'; 5 | 6 | describe('EmployeeFilterPipe', () => { 7 | let pipe: EmployeeFilterPipe; 8 | let employeees: Employee[]; 9 | 10 | beforeEach(() => { 11 | pipe = new EmployeeFilterPipe(); 12 | }); 13 | beforeEach(() => { 14 | employeees = [ 15 | { firstname: 'Thomas', lastname: 'Gassmann' }, 16 | { firstname: 'Lara', lastname: 'croft' } 17 | ]; 18 | }); 19 | 20 | it('should return all data', () => { 21 | const result = pipe.transform(employeees, null); 22 | expect(result.length).toBe(2); 23 | }); 24 | it('should filter correctly', () => { 25 | const result = pipe.transform(employeees, 'Thomas'); 26 | expect(result.length).toBe(1); 27 | }); 28 | it('should filter correctly with capital letters', () => { 29 | const result = pipe.transform(employeees, 'LARA'); 30 | expect(result.length).toBe(1); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/pipes/employee-filter.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Employee } from './../model/employee.model'; 2 | import { PipeTransform, Pipe } from '@angular/core'; 3 | 4 | @Pipe({ 5 | name: 'employeeFilter' 6 | }) 7 | export class EmployeeFilterPipe implements PipeTransform { 8 | transform(value: Employee[], filterBy: string): Employee[] { 9 | filterBy = filterBy?.toLocaleLowerCase(); 10 | return (filterBy && value) 11 | ? value.filter( 12 | (e: Employee) => 13 | (e.firstname.toLocaleLowerCase() + e.lastname.toLocaleLowerCase()).indexOf(filterBy) !== 14 | -1 15 | ) 16 | : value; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/pipes/index.ts: -------------------------------------------------------------------------------- 1 | import { EmployeeFilterPipe } from './employee-filter.pipe'; 2 | 3 | export const pipes: any[] = [EmployeeFilterPipe]; 4 | 5 | export * from './employee-filter.pipe'; 6 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/services/device.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient } from '@angular/common/http'; 2 | import { Injectable } from '@angular/core'; 3 | import { Observable, throwError as _throw } from 'rxjs'; 4 | import { catchError } from 'rxjs/operators'; 5 | import { environment } from '../../../environments/environment'; 6 | import { Device } from './../model/device.model'; 7 | 8 | @Injectable() 9 | export class DeviceService { 10 | constructor(private http: HttpClient) {} 11 | 12 | getDevices(): Observable { 13 | return this.http 14 | .get(`${environment.apiBaseUrl}/devices`) 15 | .pipe(catchError((error: any) => _throw(error))); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/services/employee.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; 2 | import { fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing'; 3 | import { environment } from '../../../environments/environment'; 4 | import { Employee } from '../model/employee.model'; 5 | import { EmployeeService } from './employee.service'; 6 | 7 | describe('EmployeeService', () => { 8 | let service: EmployeeService; 9 | let httpMock: HttpTestingController; 10 | 11 | beforeEach(() => { 12 | TestBed.configureTestingModule({ 13 | imports: [HttpClientTestingModule], 14 | providers: [EmployeeService] 15 | }); 16 | }); 17 | beforeEach(() => { 18 | service = TestBed.get(EmployeeService); 19 | httpMock = TestBed.get(HttpTestingController); 20 | }); 21 | 22 | afterEach(() => { 23 | httpMock.verify(); 24 | }); 25 | 26 | it('should create the app', waitForAsync(() => { 27 | const dummy: Employee[] = [ 28 | { id: 3, firstname: 'Thomas', lastname: 'Huber', email: 'thomas.huber@trivadis.com' }, 29 | { id: 5, firstname: 'Thomas', lastname: 'Gassmann', email: 'thomas.gassmann@trivadis.com' } 30 | ]; 31 | 32 | service.getEmployees().subscribe(list => { 33 | expect(list.length).toBe(2); 34 | expect(list).toEqual(dummy); 35 | }); 36 | 37 | const request = httpMock.expectOne(`${environment.apiBaseUrl}/employees`); 38 | expect(request.request.method).toBe('GET'); 39 | 40 | request.flush(dummy); 41 | })); 42 | 43 | it('should test fakeAsync', fakeAsync(() => { 44 | let flag = false; 45 | setTimeout(() => { 46 | flag = true; 47 | }, 50); 48 | expect(flag).toBe(false); 49 | tick(50); 50 | expect(flag).toBe(true); 51 | })); 52 | }); 53 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/services/employee.service.ts: -------------------------------------------------------------------------------- 1 | import { Employee } from './../model/employee.model'; 2 | import { Injectable } from '@angular/core'; 3 | import { HttpClient } from '@angular/common/http'; 4 | 5 | import { Observable , of , throwError as _throw } from 'rxjs'; 6 | import { catchError, delay, map } from 'rxjs/operators'; 7 | import { environment } from '../../../environments/environment'; 8 | 9 | @Injectable() 10 | export class EmployeeService { 11 | 12 | constructor(private http: HttpClient) { } 13 | 14 | getEmployees(): Observable { 15 | return this.http 16 | .get(`${environment.apiBaseUrl}/employees`) 17 | .pipe(catchError((error: any) => _throw(error))); 18 | } 19 | 20 | 21 | getEmployee(id: number): Observable { 22 | return this.http 23 | .get(`${environment.apiBaseUrl}/employees/${id}`) 24 | .pipe(catchError((error: any) => _throw(error))); 25 | } 26 | 27 | createEmployee(payload: Employee): Observable { 28 | return this.http 29 | .post(`${environment.apiBaseUrl}/employees`, payload) 30 | .pipe(catchError((error: any) => _throw(error))); 31 | } 32 | 33 | updateEmployee(payload: Employee): Observable { 34 | return this.http 35 | .put(`${environment.apiBaseUrl}/employees/${payload.id}`, payload) 36 | .pipe(catchError((error: any) => _throw(error))); 37 | } 38 | 39 | removeEmployee(payload: Employee): Observable { 40 | return this.http 41 | .delete(`${environment.apiBaseUrl}/employees/${payload.id}`) 42 | .pipe(catchError((error: any) => _throw(error))); 43 | } 44 | 45 | checkEmailUnique(email: string) { 46 | // service call for example 47 | return of(email).pipe( 48 | delay(1000), 49 | map((e) => e !== 'info@trivadis.com') 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/services/index.ts: -------------------------------------------------------------------------------- 1 | import { EmployeeService } from './employee.service'; 2 | import { DeviceService } from './device.service'; 3 | 4 | export const services: any[] = [EmployeeService, DeviceService]; 5 | 6 | export * from './employee.service'; 7 | export * from './device.service'; 8 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/validators/check-email-match.validator.ts: -------------------------------------------------------------------------------- 1 | import { Directive } from '@angular/core'; 2 | import { Validator, ValidatorFn, FormControl, AbstractControl, NG_VALIDATORS } from '@angular/forms'; 3 | import { EmployeeValidators } from './employee.validator'; 4 | 5 | @Directive({ 6 | selector: '[appIsEmails][ngModel]', 7 | providers: [ 8 | { provide: NG_VALIDATORS, useExisting: IsEmailValidator, multi: true } 9 | ] 10 | }) 11 | export class IsEmailValidator implements Validator { 12 | validate(c: AbstractControl) { 13 | return EmployeeValidators.emailValidator(c); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/employee/validators/employee.validator.ts: -------------------------------------------------------------------------------- 1 | import { AbstractControl, ValidationErrors } from '@angular/forms'; 2 | import { map } from 'rxjs/operators'; 3 | import { EmployeeService } from '../services/index'; 4 | 5 | export class EmployeeValidators { 6 | static checkEmailsMatch(control: AbstractControl) { 7 | const email = control.get('email'); 8 | const confirm = control.get('emailConfirm'); 9 | if (!(email && confirm)) { 10 | return null; 11 | } 12 | return email.value === confirm.value ? null : { nomatch: true }; 13 | } 14 | 15 | static emailValidator(control: AbstractControl): ValidationErrors | null { 16 | const val: string = control.value; 17 | if (!val || val.indexOf('@') > 0) { 18 | return null; 19 | } 20 | return { invalidemail: true }; 21 | } 22 | 23 | static checkEmailUnique(service: EmployeeService) { 24 | return (control: AbstractControl) => { 25 | return service.checkEmailUnique(control.value).pipe( 26 | map(res => { 27 | console.log(res); 28 | return res ? null : { emailNotUnique: true }; 29 | }) 30 | ); 31 | }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/layout/layout.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, Optional, SkipSelf } from '@angular/core'; 2 | 3 | import { StandardLayoutComponent } from './standard-layout/standard-layout.component'; 4 | import { WelcomeComponent } from './welcome/welcome.component'; 5 | import { SharedModule } from '../shared/shared.module'; 6 | import { RouterModule } from '@angular/router'; 7 | 8 | @NgModule({ 9 | imports: [RouterModule, SharedModule], 10 | exports: [], 11 | declarations: [StandardLayoutComponent, WelcomeComponent] 12 | }) 13 | export class LayoutModule { 14 | constructor( 15 | @Optional() 16 | @SkipSelf() 17 | parentModule: LayoutModule 18 | ) { 19 | if (parentModule) { 20 | throw new Error(`LayoutModule has already been loaded. Import Core modules in the AppModule only.`); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/layout/standard-layout/standard-layout.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Employee Portal

4 |
5 |
6 | 11 |
12 |
13 |
14 |
15 | 16 |
17 |
18 |
19 |
20 |

© 2020, Trivadis AG

21 |
22 |
23 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/layout/standard-layout/standard-layout.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | templateUrl: 'standard-layout.component.html' 5 | }) 6 | export class StandardLayoutComponent implements OnInit { 7 | constructor() { } 8 | 9 | ngOnInit() { } 10 | } 11 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/layout/welcome/welcome.component.html: -------------------------------------------------------------------------------- 1 |

Employee Portal

2 |

3 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut 4 | labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo 5 | dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit 6 | amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor 7 | invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et 8 | justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum 9 | dolor sit amet. 10 |

11 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/layout/welcome/welcome.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | templateUrl: 'welcome.component.html' 5 | }) 6 | export class WelcomeComponent implements OnInit { 7 | constructor() {} 8 | 9 | ngOnInit() {} 10 | } 11 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/shared/auth.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; 2 | import { Injectable } from '@angular/core'; 3 | import { Observable } from 'rxjs'; 4 | 5 | @Injectable() 6 | export class AuthInterceptor implements HttpInterceptor { 7 | intercept(req: HttpRequest, next: HttpHandler): Observable> { 8 | console.log('authentication'); 9 | const authReq = req.clone({ 10 | // headers: req.headers.set('Authorization', 'Bearer: XYZ') 11 | }); 12 | return next.handle(authReq); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/shared/directives/hotkey.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, EventEmitter, HostListener, Input, Output } from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: '[appHotkey]' 5 | }) 6 | export class HotkeyDirective { 7 | // tslint:disable-next-line:no-input-rename 8 | @Input('appHotkey') keyCode: number; 9 | 10 | @Output() keyClicked: EventEmitter = new EventEmitter(); 11 | 12 | constructor() {} 13 | 14 | @HostListener('window:keydown', ['$event']) 15 | handleKeyboardEvent(kbdEvent: KeyboardEvent) { 16 | if (kbdEvent.keyCode === this.keyCode && kbdEvent.ctrlKey) { 17 | kbdEvent.preventDefault(); 18 | this.keyClicked.emit(true); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/shared/directives/index.ts: -------------------------------------------------------------------------------- 1 | import { SelectDirective } from './select.directive'; 2 | import { HotkeyDirective } from './hotkey.directive'; 3 | 4 | export const directives: any[] = [SelectDirective, HotkeyDirective]; 5 | 6 | export * from './select.directive'; 7 | export * from './hotkey.directive'; 8 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/shared/directives/select.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, HostBinding, HostListener, Input, Renderer2 } from '@angular/core'; 2 | 3 | @Directive({ selector: '[appSelect]' }) 4 | export class SelectDirective { 5 | @Input() backgroundColor: string; 6 | 7 | @HostBinding('class.selected') isSelected = false; 8 | 9 | constructor(private el: ElementRef, private renderer2: Renderer2) {} 10 | 11 | @HostListener('dblclick') 12 | doubleClick() { 13 | this.renderer2.setStyle(this.el.nativeElement, 'backgroundColor', this.backgroundColor); 14 | } 15 | 16 | // @HostListener('mouseover') 17 | // @HostListener('mouseleave') 18 | @HostListener('click') 19 | setSelected() { 20 | this.isSelected = !this.isSelected; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/shared/http-error.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { 2 | HttpErrorResponse, 3 | HttpEvent, 4 | HttpHandler, 5 | HttpInterceptor, 6 | HttpRequest 7 | } from '@angular/common/http'; 8 | import { Injectable } from '@angular/core'; 9 | import { Observable, throwError as observableThrowError } from 'rxjs'; 10 | import { catchError } from 'rxjs/operators'; 11 | 12 | @Injectable() 13 | export class HttpErrorInterceptor implements HttpInterceptor { 14 | constructor() {} 15 | 16 | intercept(req: HttpRequest, next: HttpHandler): Observable> { 17 | return next.handle(req).pipe( 18 | catchError((error, caught) => { 19 | console.warn('test', error); 20 | if (error instanceof HttpErrorResponse) { 21 | const httpError: HttpErrorResponse = error; 22 | console.log( 23 | `Es ist ein Fehler beim Zugriff auf '${req.url}' aufgetreten: ${httpError.message}` 24 | ); 25 | } 26 | 27 | // return the error to the method that called it 28 | return observableThrowError(error); 29 | }) 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/app/shared/shared.module.ts: -------------------------------------------------------------------------------- 1 | import { HttpErrorInterceptor } from './http-error.interceptor'; 2 | import { NgModule, ModuleWithProviders } from '@angular/core'; 3 | import { CommonModule } from '@angular/common'; 4 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 5 | import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; 6 | 7 | import * as fromDirectives from './directives'; 8 | import { AuthInterceptor } from './auth.interceptor'; 9 | 10 | // https://angular.io/guide/styleguide#shared-feature-module 11 | 12 | @NgModule({ 13 | imports: [CommonModule], 14 | exports: [CommonModule, 15 | HttpClientModule, 16 | FormsModule, 17 | ReactiveFormsModule, 18 | ...fromDirectives.directives], 19 | declarations: [...fromDirectives.directives], 20 | providers: [ 21 | { 22 | provide: HTTP_INTERCEPTORS, 23 | useClass: AuthInterceptor, 24 | multi: true 25 | }, 26 | { 27 | provide: HTTP_INTERCEPTORS, 28 | useClass: HttpErrorInterceptor, 29 | multi: true 30 | } 31 | ] 32 | }) 33 | export class SharedModule { 34 | } 35 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trivadis/AdvancedAngular/b3cfb1fc8957894cf2dc9cc6ff7ce638f071d89f/finalOhneNgRx/ngTvdTraining/src/assets/.gitkeep -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | 4 | // Use Node Express 5 | apiBaseUrl: 'http://localhost:8180/api' 6 | 7 | // Use DENO 8 | // apiBaseUrl: 'http://localhost:8280' 9 | }; 10 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/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 | // Use Node Express 10 | apiBaseUrl: 'http://localhost:8180/api' 11 | 12 | // Use DENO 13 | // apiBaseUrl: 'http://localhost:8280' 14 | }; 15 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trivadis/AdvancedAngular/b3cfb1fc8957894cf2dc9cc6ff7ce638f071d89f/finalOhneNgRx/ngTvdTraining/src/favicon.ico -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NgTvdTraining 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic() 12 | .bootstrapModule(AppModule) 13 | .catch(err => console.log(err)); 14 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags.ts'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | /*************************************************************************************************** 61 | * APPLICATION IMPORTS 62 | */ 63 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/styles.scss: -------------------------------------------------------------------------------- 1 | body { 2 | // padding: 10px 25px; 3 | } 4 | 5 | a, 6 | a:hover, 7 | a:focus { 8 | color: #c92c21; 9 | } 10 | 11 | #pageBanner { 12 | background-image: url('https://m.trivadis.com/hubfs/TVD_Training/Landingpages/Angular/Angular-Training.jpg'); 13 | height: 280px; 14 | background-size: cover; 15 | background-repeat: no-repeat; 16 | background-position: center -165px; 17 | padding: 15px 30px; 18 | } 19 | #pageBanner h1 { 20 | font-size: 38px; 21 | color: #fff; 22 | font-weight: 700; 23 | line-height: 74px !important; 24 | margin-bottom: 0; 25 | text-shadow: 0 0 40px rgba(0, 0, 0, 0.75); 26 | } 27 | header { 28 | nav { 29 | margin: 5px 0; 30 | } 31 | nav { 32 | a { 33 | font-size: 1em; 34 | // background-color: #cdcdcd; 35 | padding: 5px; 36 | margin-right: 10px; 37 | } 38 | .active { 39 | color: black; 40 | } 41 | } 42 | } 43 | main { 44 | padding: 10px 0; 45 | } 46 | footer { 47 | font-size: 0.85em; 48 | padding-bottom: 2rem; 49 | padding-top: 1em; 50 | } 51 | .panel-primary > .panel-heading { 52 | background-color: #c92c21; 53 | border-color: #c92c21; 54 | } 55 | 56 | .btn { 57 | margin-right: 10px; 58 | margin-bottom: 10px; 59 | } 60 | .btn-primary { 61 | color: #fff; 62 | background-color: #c92c21; 63 | border-color: #c92c21; 64 | } 65 | .btn-primary:hover { 66 | color: #fff; 67 | background-color: #8c0000; 68 | border-color: #8c0000; 69 | transition: all 300ms linear; 70 | } 71 | 72 | .selected { 73 | background-color: #8c0000 !important; 74 | color: #fff !important; 75 | } 76 | 77 | /* Fade transition 78 | app-root main { 79 | animation-duration: .5s; 80 | opacity: 0; 81 | animation-name: fadeIn; 82 | animation-fill-mode: forwards; 83 | transition: transform .5s ease; 84 | } 85 | 86 | @keyframes fadeIn { 87 | from { 88 | opacity: 0; 89 | } 90 | 91 | to { 92 | opacity: 1; 93 | } 94 | } 95 | */ 96 | 97 | /* Slide transition */ 98 | app-root main { 99 | animation-duration: .5s; 100 | animation-name: slideIn; 101 | animation-fill-mode: forwards; 102 | transition: transform .5s ease; 103 | } 104 | 105 | @keyframes slideIn { 106 | from { 107 | transform: translate(-200px); 108 | } 109 | 110 | to { 111 | transform: translate(0px); 112 | } 113 | } 114 | 115 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/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/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": [ 8 | "main.ts", 9 | "polyfills.ts" 10 | ], 11 | "include": ["**/*.d.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "types": ["jasmine", "node"] 6 | }, 7 | "files": ["test.ts", "polyfills.ts"], 8 | "include": ["**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/src/typings.d.ts: -------------------------------------------------------------------------------- 1 | /* SystemJS module definition */ 2 | declare var module: NodeModule; 3 | interface NodeModule { 4 | id: string; 5 | } 6 | -------------------------------------------------------------------------------- /finalOhneNgRx/ngTvdTraining/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "moduleResolution": "node", 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "target": "es2015", 12 | "typeRoots": [ 13 | "node_modules/@types" 14 | ], 15 | "lib": [ 16 | "es2018", 17 | "dom" 18 | ], 19 | "module": "es2020", 20 | "paths": { 21 | "auto-focus": [ 22 | "dist/auto-focus" 23 | ], 24 | "auto-focus/*": [ 25 | "dist/auto-focus/*" 26 | ] 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /starter/ngTvdTraining/.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /starter/ngTvdTraining/.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 | quote_type = single 11 | 12 | [*.md] 13 | max_line_length = off 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /dist-server 6 | /tmp 7 | /out-tsc 8 | 9 | # dependencies 10 | /node_modules 11 | 12 | # IDEs and editors 13 | /.idea 14 | .project 15 | .classpath 16 | .c9/ 17 | *.launch 18 | .settings/ 19 | *.sublime-workspace 20 | 21 | # IDE - VSCode 22 | .vscode/* 23 | !.vscode/settings.json 24 | !.vscode/tasks.json 25 | !.vscode/launch.json 26 | !.vscode/extensions.json 27 | 28 | # misc 29 | /.sass-cache 30 | /connect.lock 31 | /coverage 32 | /libpeerconnection.log 33 | npm-debug.log 34 | yarn-error.log 35 | testem.log 36 | /typings 37 | 38 | # e2e 39 | /e2e/*.js 40 | /e2e/*.map 41 | 42 | # System Files 43 | .DS_Store 44 | Thumbs.db 45 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/README.md: -------------------------------------------------------------------------------- 1 | # Advanced Angular 2 | 3 | Contains the starter project for the Advanced Angular training at Trivadis: 4 | https://www.trivadis.com/de/training/advanced-angular-ad-angular-adv 5 | 6 | ``` 7 | https://m.trivadis.com/angular-schulung 8 | ``` 9 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('ng-tvd-training App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to app!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": [ 9 | "jasmine", 10 | "jasminewd2", 11 | "node" 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client:{ 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ], 20 | fixWebpackSourcePaths: true 21 | }, 22 | 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; 32 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ng-tvd-training", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "ng": "ng", 7 | "start": "ng serve", 8 | "build": "ng build --prod", 9 | "test": "ng test", 10 | "lint": "ng lint", 11 | "e2e": "ng e2e" 12 | }, 13 | "private": true, 14 | "dependencies": { 15 | "@angular/animations": "11.0.3", 16 | "@angular/common": "11.0.3", 17 | "@angular/compiler": "11.0.3", 18 | "@angular/core": "11.0.3", 19 | "@angular/forms": "11.0.3", 20 | "@angular/platform-browser": "11.0.3", 21 | "@angular/platform-browser-dynamic": "11.0.3", 22 | "@angular/router": "11.0.3", 23 | "@ngrx/effects": "10.1.0", 24 | "@ngrx/router-store": "10.1.0", 25 | "@ngrx/store": "10.1.0", 26 | "@ngrx/store-devtools": "10.1.0", 27 | "bootstrap": "4.5.3", 28 | "rxjs": "6.6.3", 29 | "tslib": "^2.0.0", 30 | "zone.js": "~0.11.3" 31 | }, 32 | "devDependencies": { 33 | "@angular-devkit/build-angular": "~0.1100.3", 34 | "@angular/cli": "~11.0.3", 35 | "@angular/compiler-cli": "~11.0.3", 36 | "@angular/language-service": "~11.0.3", 37 | "@types/node": "^12.11.1", 38 | "@types/jasmine": "~3.6.0", 39 | "@types/jasminewd2": "~2.0.6", 40 | "codelyzer": "^6.0.0", 41 | "jasmine-core": "~3.6.0", 42 | "jasmine-spec-reporter": "~6.0.0", 43 | "karma": "~5.1.1", 44 | "karma-chrome-launcher": "~3.1.0", 45 | "karma-firefox-launcher": "^2.1.0", 46 | "karma-coverage-istanbul-reporter": "~3.0.3", 47 | "karma-jasmine": "~4.0.1", 48 | "karma-jasmine-html-reporter": "^1.5.4", 49 | "protractor": "~7.0.0", 50 | "ts-node": "~7.0.0", 51 | "tslint": "~6.1.0", 52 | "typescript": "4.0.5" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/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 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './e2e/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: 'e2e/tsconfig.e2e.json' 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/about/about-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | 4 | import { AboutListComponent } from './containers/about-list/about-list.component'; 5 | 6 | const routes: Routes = [ 7 | { path: '', component: AboutListComponent }, 8 | ]; 9 | 10 | @NgModule({ 11 | imports: [RouterModule.forChild(routes)], 12 | exports: [RouterModule], 13 | }) 14 | export class AboutRoutingModule { } 15 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/about/about.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { AboutListComponent } from './containers/about-list/about-list.component'; 4 | import { AboutRoutingModule } from './about-routing.module'; 5 | 6 | @NgModule({ 7 | imports: [AboutRoutingModule], 8 | exports: [], 9 | declarations: [AboutListComponent], 10 | providers: [] 11 | }) 12 | export class AboutModule {} 13 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/about/containers/about-list/about-list.component.html: -------------------------------------------------------------------------------- 1 | About - Lazy Loading -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/about/containers/about-list/about-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | templateUrl: 'about-list.component.html' 5 | }) 6 | export class AboutListComponent implements OnInit { 7 | constructor() { } 8 | 9 | ngOnInit() { } 10 | } 11 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | import * as fromEmployeeContainer from './employee/containers'; 4 | import { StandardLayoutComponent } from './layout/standard-layout/standard-layout.component'; 5 | import { WelcomeComponent } from './layout/welcome/welcome.component'; 6 | 7 | const routes: Routes = [ 8 | { 9 | path: '', 10 | component: StandardLayoutComponent, 11 | children: [ 12 | { path: '', redirectTo: 'welcome', pathMatch: 'full' }, 13 | { path: 'welcome', component: WelcomeComponent }, 14 | { 15 | path: 'employees', 16 | component: fromEmployeeContainer.EmployeeListComponent 17 | }, 18 | { 19 | path: 'employees/new', 20 | component: fromEmployeeContainer.EmployeeComponent 21 | }, 22 | { 23 | path: 'employees/:employeeId', 24 | component: fromEmployeeContainer.EmployeeComponent 25 | }, 26 | { 27 | path: 'about', 28 | loadChildren: () => import('./about/about.module').then(m => m.AboutModule) 29 | } 30 | ] 31 | } 32 | ]; 33 | 34 | @NgModule({ 35 | imports: [RouterModule.forRoot(routes)], 36 | exports: [RouterModule], 37 | providers: [] 38 | }) 39 | export class AppRoutingModule {} 40 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, waitForAsync } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | describe('AppComponent', () => { 4 | beforeEach(waitForAsync(() => { 5 | TestBed.configureTestingModule({ 6 | declarations: [ 7 | AppComponent 8 | ], 9 | }).compileComponents(); 10 | })); 11 | it('should create the app', waitForAsync(() => { 12 | const fixture = TestBed.createComponent(AppComponent); 13 | const app = fixture.debugElement.componentInstance; 14 | expect(app).toBeTruthy(); 15 | })); 16 | }); 17 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | 4 | @Component({ 5 | selector: 'app-root', 6 | template: '' 7 | }) 8 | export class AppComponent { 9 | } 10 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 4 | import { AppRoutingModule } from './app-routing.module'; 5 | import { AppComponent } from './app.component'; 6 | import { CoreModule } from './core/core.module'; 7 | import * as fromEmployeeComponents from './employee/components'; 8 | import * as fromEmployeeContainers from './employee/containers'; 9 | import * as fromEmployeeGuards from './employee/guards'; 10 | import * as fromEmployeePipes from './employee/pipes'; 11 | import * as fromEmployeeServices from './employee/services'; 12 | import { IsEmailValidator } from './employee/validators/check-email-match.validator'; 13 | import { LayoutModule } from './layout/layout.module'; 14 | import { SharedModule } from './shared/shared.module'; 15 | 16 | @NgModule({ 17 | imports: [ 18 | BrowserModule, 19 | BrowserAnimationsModule, 20 | SharedModule, 21 | CoreModule, 22 | LayoutModule, 23 | AppRoutingModule 24 | ], 25 | declarations: [ 26 | AppComponent, 27 | ...fromEmployeeContainers.containers, 28 | ...fromEmployeeComponents.components, 29 | ...fromEmployeePipes.pipes, 30 | IsEmailValidator 31 | ], 32 | providers: [...fromEmployeeServices.services, ...fromEmployeeGuards.guards], 33 | bootstrap: [AppComponent] 34 | }) 35 | export class AppModule {} 36 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/core/core.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, Optional, SkipSelf } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { SuperService } from './super-service'; 4 | 5 | 6 | // https://angular.io/guide/styleguide#core-feature-module 7 | 8 | @NgModule({ 9 | imports: [CommonModule], 10 | exports: [], 11 | declarations: [], 12 | providers: [SuperService] 13 | }) 14 | export class CoreModule { } 15 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/core/super-service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable() 4 | export class SuperService { 5 | constructor() {} 6 | 7 | get(): Boolean { 8 | return true; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/employee/components/employee-form/employee-form.component.html: -------------------------------------------------------------------------------- 1 |

{{ title }}

2 |
3 |
4 | 5 |
6 | 7 |
8 | 13 |
14 |
15 | 16 |
17 | 18 |
19 | 20 |
21 |
22 | 23 |
24 | 25 |
26 | 27 |
28 |
29 | 30 |
31 | 32 |
33 | 34 |
35 | 36 |
37 | 40 | 43 | 46 |
47 |
-------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/employee/components/employee-form/employee-form.component.scss: -------------------------------------------------------------------------------- 1 | // .ng-valid:not(form), .ng-valid[required], .ng-valid.required { 2 | // border-left: 5px solid #42A948; /* green */ 3 | // } 4 | 5 | // .ng-invalid:not(form) { 6 | // border-left: 5px solid #a94442; /* red */ 7 | // } -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/employee/components/employee-form/employee-form.component.ts: -------------------------------------------------------------------------------- 1 | import { EmployeeValidators } from './../../validators/employee.validator'; 2 | import { Employee } from '../../model/employee.model'; 3 | 4 | import { 5 | Component, 6 | OnInit, 7 | Input, 8 | Output, 9 | EventEmitter, 10 | OnChanges, 11 | SimpleChanges, 12 | ChangeDetectionStrategy 13 | } from '@angular/core'; 14 | import { FormBuilder, Validators } from '@angular/forms'; 15 | import { FormGroup } from '@angular/forms'; 16 | import { EmployeeService } from '../../services/index'; 17 | 18 | @Component({ 19 | selector: 'app-employee-form', 20 | templateUrl: 'employee-form.component.html', 21 | styleUrls: ['employee-form.component.scss'] 22 | }) 23 | export class EmployeeFormComponent implements OnInit, OnChanges { 24 | @Input() employee: Employee; 25 | 26 | @Output() create = new EventEmitter(); 27 | @Output() update = new EventEmitter(); 28 | @Output() remove = new EventEmitter(); 29 | 30 | form: FormGroup = new FormGroup({}); 31 | 32 | isEdit = false; 33 | title = 'Create'; 34 | 35 | constructor(private fb: FormBuilder, private service: EmployeeService) {} 36 | 37 | ngOnInit() {} 38 | 39 | ngOnChanges(changes: SimpleChanges) { 40 | if (this.employee && this.employee.id) { 41 | this.isEdit = true; 42 | this.title = 'Edit'; 43 | } 44 | } 45 | 46 | createEmployee() { 47 | if (this.form.valid) { 48 | this.create.emit(this.form.value); 49 | } 50 | } 51 | 52 | updateEmployee() { 53 | if (this.form.touched && this.form.valid) { 54 | this.update.emit({ ...this.employee, ...this.form.value }); 55 | } 56 | } 57 | 58 | removeEmployee() { 59 | this.remove.emit({ ...this.employee, ...this.form.value }); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/employee/components/index.ts: -------------------------------------------------------------------------------- 1 | import { EmployeeFormComponent } from './employee-form/employee-form.component'; 2 | 3 | export const components: any[] = [EmployeeFormComponent]; 4 | 5 | export * from './employee-form/employee-form.component'; 6 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/employee/containers/employee-list/employee-list.component.html: -------------------------------------------------------------------------------- 1 |

Employees

2 | 7 |
8 |
Search:
9 |
10 | 11 |
12 |
13 |
14 |
15 | Filtered by: {{listFilter}} 16 |
17 |
18 |
19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 41 | 42 | 43 |
IdFirstnameLastnameE-Mail 
{{ employee.id }}{{ employee.firstname }}{{ employee.lastname }}{{ employee.email }} 37 | 38 | Edit 39 | 40 |
44 |
45 |
-------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/employee/containers/employee-list/employee-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs'; 2 | import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core'; 3 | import * as fromService from '../../services'; 4 | import { Store } from '@ngrx/store'; 5 | import { ActivatedRoute } from '@angular/router'; 6 | import { Employee } from '../../model/employee.model'; 7 | 8 | @Component({ 9 | templateUrl: 'employee-list.component.html' 10 | }) 11 | export class EmployeeListComponent implements OnInit { 12 | employees$: Observable; 13 | listFilter: string; 14 | 15 | constructor(private service: fromService.EmployeeService, private route: ActivatedRoute) {} 16 | 17 | ngOnInit() { 18 | this.employees$ = this.service.getEmployees(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/employee/containers/employee/employee.component.html: -------------------------------------------------------------------------------- 1 | 6 | > -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/employee/containers/employee/employee.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core'; 2 | 3 | import * as fromService from '../../services'; 4 | import { Observable } from 'rxjs'; 5 | import { Employee } from '../../model/employee.model'; 6 | import { Router, ActivatedRoute } from '@angular/router'; 7 | 8 | @Component({ 9 | templateUrl: 'employee.component.html' 10 | }) 11 | export class EmployeeComponent implements OnInit { 12 | employee$: Observable; 13 | 14 | constructor( 15 | private service: fromService.EmployeeService, 16 | private router: Router, 17 | private route: ActivatedRoute 18 | ) {} 19 | 20 | ngOnInit() { 21 | this.route.paramMap.subscribe(p => { 22 | const eId = +p.get('employeeId'); 23 | if (eId > 0) { 24 | this.employee$ = this.service.getEmployee(eId); 25 | } 26 | }); 27 | } 28 | 29 | onCreate(event: Employee) { 30 | this.service.createEmployee(event).subscribe(() => { 31 | this.router.navigate(['/employees']); 32 | }); 33 | } 34 | 35 | onUpdate(event: Employee) { 36 | this.service.updateEmployee(event).subscribe(() => { 37 | this.router.navigate(['/employees']); 38 | }); 39 | } 40 | 41 | onRemove(event: Employee) { 42 | const remove = window.confirm('Are you sure?'); 43 | if (remove) { 44 | this.service.removeEmployee(event).subscribe(() => { 45 | this.router.navigate(['/employees']); 46 | }); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/employee/containers/index.ts: -------------------------------------------------------------------------------- 1 | import { EmployeeListComponent } from './employee-list/employee-list.component'; 2 | import { EmployeeComponent } from './employee/employee.component'; 3 | 4 | export const containers: any[] = [EmployeeListComponent, EmployeeComponent]; 5 | 6 | export * from './employee-list/employee-list.component'; 7 | export * from './employee/employee.component'; 8 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/employee/guards/index.ts: -------------------------------------------------------------------------------- 1 | 2 | export const guards: any[] = []; 3 | 4 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/employee/model/device.model.ts: -------------------------------------------------------------------------------- 1 | export interface Device { 2 | id?: number; 3 | name?: string; 4 | price?: number; 5 | photo?: string; 6 | } 7 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/employee/model/employee.model.ts: -------------------------------------------------------------------------------- 1 | export interface Employee { 2 | id?: number; 3 | firstname?: string; 4 | lastname?: string; 5 | email?: string; 6 | } 7 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/employee/pipes/index.ts: -------------------------------------------------------------------------------- 1 | 2 | export const pipes: any[] = []; 3 | 4 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/employee/services/device.service.ts: -------------------------------------------------------------------------------- 1 | 2 | import { Injectable } from '@angular/core'; 3 | import { HttpClient } from '@angular/common/http'; 4 | 5 | import { Observable , throwError as _throw , of } from 'rxjs'; 6 | import { catchError, delay, map } from 'rxjs/operators'; 7 | import { environment } from '../../../environments/environment'; 8 | import { Device } from './../model/device.model'; 9 | 10 | @Injectable() 11 | export class DeviceService { 12 | 13 | constructor(private http: HttpClient) { } 14 | 15 | getDevices(): Observable { 16 | return this.http 17 | .get(`${environment.apiBaseUrl}/devices`) 18 | .pipe(catchError((error: any) => _throw(error))); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/employee/services/employee.service.ts: -------------------------------------------------------------------------------- 1 | import { Employee } from './../model/employee.model'; 2 | import { Injectable } from '@angular/core'; 3 | import { HttpClient } from '@angular/common/http'; 4 | 5 | import { Observable , of , throwError as _throw } from 'rxjs'; 6 | import { catchError, delay, map } from 'rxjs/operators'; 7 | import { environment } from '../../../environments/environment'; 8 | 9 | @Injectable() 10 | export class EmployeeService { 11 | 12 | constructor(private http: HttpClient) { } 13 | 14 | getEmployees(): Observable { 15 | return this.http 16 | .get(`${environment.apiBaseUrl}/employees`) 17 | .pipe(catchError((error: any) => _throw(error))); 18 | } 19 | 20 | 21 | getEmployee(id: number): Observable { 22 | return this.http 23 | .get(`${environment.apiBaseUrl}/employees/${id}`) 24 | .pipe(catchError((error: any) => _throw(error))); 25 | } 26 | 27 | createEmployee(payload: Employee): Observable { 28 | return this.http 29 | .post(`${environment.apiBaseUrl}/employees`, payload) 30 | .pipe(catchError((error: any) => _throw(error))); 31 | } 32 | 33 | updateEmployee(payload: Employee): Observable { 34 | return this.http 35 | .put(`${environment.apiBaseUrl}/employees/${payload.id}`, payload) 36 | .pipe(catchError((error: any) => _throw(error))); 37 | } 38 | 39 | removeEmployee(payload: Employee): Observable { 40 | return this.http 41 | .delete(`${environment.apiBaseUrl}/employees/${payload.id}`) 42 | .pipe(catchError((error: any) => _throw(error))); 43 | } 44 | 45 | checkEmailUnique(email: string) { 46 | // service call for example 47 | return of(email).pipe( 48 | delay(1000), 49 | map((e) => e !== 'info@trivadis.com') 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/employee/services/index.ts: -------------------------------------------------------------------------------- 1 | import { EmployeeService } from './employee.service'; 2 | import { DeviceService } from './device.service'; 3 | 4 | export const services: any[] = [EmployeeService, DeviceService]; 5 | 6 | export * from './employee.service'; 7 | export * from './device.service'; 8 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/employee/validators/check-email-match.validator.ts: -------------------------------------------------------------------------------- 1 | import { Directive } from '@angular/core'; 2 | import { Validator, ValidatorFn, FormControl, AbstractControl, NG_VALIDATORS } from '@angular/forms'; 3 | import { EmployeeValidators } from './employee.validator'; 4 | 5 | @Directive({ 6 | selector: '[appIsEmails][ngModel]', 7 | providers: [ 8 | { provide: NG_VALIDATORS, useExisting: IsEmailValidator, multi: true } 9 | ] 10 | }) 11 | export class IsEmailValidator implements Validator { 12 | validate(c: AbstractControl) { 13 | return EmployeeValidators.emailValidator(c); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/employee/validators/employee.validator.ts: -------------------------------------------------------------------------------- 1 | import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms'; 2 | import { map } from 'rxjs/operators'; 3 | import { EmployeeService } from '../services/index'; 4 | 5 | export class EmployeeValidators { 6 | static checkEmailsMatch(control: AbstractControl) { 7 | const email = control.get('email'); 8 | const confirm = control.get('emailConfirm'); 9 | if (!(email && confirm)) { 10 | return null; 11 | } 12 | return email.value === confirm.value ? null : { nomatch: true }; 13 | } 14 | 15 | static emailValidator(control: AbstractControl): ValidationErrors | null { 16 | const val: string = control.value; 17 | if (!val || val.indexOf('@') > 0) { 18 | return null; 19 | } 20 | return { invalidemail: true }; 21 | } 22 | 23 | static checkEmailUnique(service: EmployeeService): ValidatorFn { 24 | return (control: AbstractControl) => { 25 | return service.checkEmailUnique(control.value).pipe( 26 | map(res => { 27 | console.log(res); 28 | return res ? null : { emailNotUnique: true }; 29 | }) 30 | ); 31 | }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/layout/layout.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, Optional, SkipSelf } from '@angular/core'; 2 | 3 | import { StandardLayoutComponent } from './standard-layout/standard-layout.component'; 4 | import { WelcomeComponent } from './welcome/welcome.component'; 5 | import { SharedModule } from '../shared/shared.module'; 6 | import { RouterModule } from '@angular/router'; 7 | 8 | @NgModule({ 9 | imports: [RouterModule, SharedModule], 10 | exports: [], 11 | declarations: [StandardLayoutComponent, WelcomeComponent], 12 | providers: [] 13 | }) 14 | export class LayoutModule { 15 | constructor( 16 | @Optional() 17 | @SkipSelf() 18 | parentModule: LayoutModule 19 | ) { 20 | if (parentModule) { 21 | throw new Error(`LayoutModule has already been loaded. Import Core modules in the AppModule only.`); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/layout/standard-layout/standard-layout.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Byod Portal

4 |
5 |
6 | 11 |
12 |
13 |
14 |
15 | 16 |
17 |
18 |
19 |
20 |

© 2020, Trivadis AG

21 |
22 |
-------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/layout/standard-layout/standard-layout.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | templateUrl: 'standard-layout.component.html' 5 | }) 6 | export class StandardLayoutComponent implements OnInit { 7 | constructor() { } 8 | 9 | ngOnInit() { } 10 | } 11 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/layout/welcome/welcome.component.html: -------------------------------------------------------------------------------- 1 |

Byod Portal

2 |

3 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna 4 | aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, 5 | no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed 6 | diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam 7 | et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. 8 |

-------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/layout/welcome/welcome.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | templateUrl: 'welcome.component.html' 5 | }) 6 | export class WelcomeComponent implements OnInit { 7 | constructor() {} 8 | 9 | ngOnInit() {} 10 | } 11 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/shared/directives/index.ts: -------------------------------------------------------------------------------- 1 | // import { SelectDirective } from './select.directive'; 2 | 3 | export const directives: any[] = []; 4 | 5 | // export * from './select.directive'; 6 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/app/shared/shared.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, ModuleWithProviders } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { HttpClientModule } from '@angular/common/http'; 5 | 6 | import * as fromDirectives from './directives'; 7 | 8 | // https://angular.io/guide/styleguide#shared-feature-module 9 | 10 | @NgModule({ 11 | imports: [CommonModule], 12 | exports: [CommonModule, 13 | HttpClientModule, 14 | FormsModule, 15 | ReactiveFormsModule, 16 | ...fromDirectives.directives], 17 | declarations: [...fromDirectives.directives], 18 | providers: [] 19 | }) 20 | export class SharedModule { 21 | } 22 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trivadis/AdvancedAngular/b3cfb1fc8957894cf2dc9cc6ff7ce638f071d89f/starter/ngTvdTraining/src/assets/.gitkeep -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | 4 | // Use Node Express 5 | apiBaseUrl: 'http://localhost:8180/api' 6 | 7 | // Use DENO 8 | // apiBaseUrl: 'http://localhost:8280' 9 | }; 10 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/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 | // Use Node Express 9 | apiBaseUrl: 'http://localhost:8180/api' 10 | 11 | // Use DENO 12 | // apiBaseUrl: 'http://localhost:8280' 13 | }; 14 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Trivadis/AdvancedAngular/b3cfb1fc8957894cf2dc9cc6ff7ce638f071d89f/starter/ngTvdTraining/src/favicon.ico -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NgTvdTraining 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.log(err)); 13 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags.ts'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | /*************************************************************************************************** 61 | * APPLICATION IMPORTS 62 | */ 63 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/styles.scss: -------------------------------------------------------------------------------- 1 | body { 2 | // padding: 10px 25px; 3 | } 4 | 5 | a, 6 | a:hover, 7 | a:focus { 8 | color: #c92c21; 9 | } 10 | 11 | #pageBanner { 12 | background-image: url('https://m.trivadis.com/hubfs/TVD_Training/Landingpages/Angular/Angular-Training.jpg'); 13 | height: 150px; 14 | background-size: cover; 15 | background-repeat: no-repeat; 16 | background-position: center -165px; 17 | padding: 15px 30px; 18 | } 19 | #pageBanner h1 { 20 | font-size: 38px; 21 | color: #fff; 22 | font-weight: 700; 23 | line-height: 74px !important; 24 | margin-bottom: 0; 25 | text-shadow: 0 0 40px rgba(0, 0, 0, 0.75); 26 | } 27 | header { 28 | nav { 29 | margin: 5px 0; 30 | } 31 | nav { 32 | a { 33 | font-size: 1em; 34 | // background-color: #cdcdcd; 35 | padding: 5px; 36 | margin-right: 10px; 37 | } 38 | .active { 39 | color: black; 40 | } 41 | } 42 | } 43 | main { 44 | padding: 10px 0; 45 | } 46 | footer { 47 | font-size: 0.85em; 48 | padding-bottom: 2rem; 49 | padding-top: 1em; 50 | } 51 | .panel-primary > .panel-heading { 52 | background-color: #c92c21; 53 | border-color: #c92c21; 54 | } 55 | 56 | .btn { 57 | margin-right: 10px; 58 | margin-bottom: 10px; 59 | } 60 | .btn-primary { 61 | color: #fff; 62 | background-color: #c92c21; 63 | border-color: #c92c21; 64 | } 65 | .btn-primary:hover { 66 | color: #fff; 67 | background-color: #8c0000; 68 | border-color: #8c0000; 69 | transition: all 300ms linear; 70 | } 71 | 72 | 73 | .selected { 74 | background-color: #8c0000 !important; 75 | color: #fff !important; 76 | } 77 | 78 | /* Fade transition 79 | app-root main { 80 | animation-duration: .5s; 81 | opacity: 0; 82 | animation-name: fadeIn; 83 | animation-fill-mode: forwards; 84 | transition: transform .5s ease; 85 | } 86 | 87 | @keyframes fadeIn { 88 | from { 89 | opacity: 0; 90 | } 91 | 92 | to { 93 | opacity: 1; 94 | } 95 | } 96 | */ 97 | 98 | /* Slide transition 99 | app-root main { 100 | animation-duration: .5s; 101 | animation-name: slideIn; 102 | animation-fill-mode: forwards; 103 | transition: transform .5s ease; 104 | } 105 | 106 | @keyframes slideIn { 107 | from { 108 | transform: translate(-200px); 109 | } 110 | 111 | to { 112 | transform: translate(0px); 113 | } 114 | } 115 | */ 116 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/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/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": [ 8 | "main.ts", 9 | "polyfills.ts" 10 | ], 11 | "include": ["**/*.d.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "types": ["jasmine", "node"] 6 | }, 7 | "files": ["test.ts", "polyfills.ts"], 8 | "include": ["**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/src/typings.d.ts: -------------------------------------------------------------------------------- 1 | /* SystemJS module definition */ 2 | declare var module: NodeModule; 3 | interface NodeModule { 4 | id: string; 5 | } 6 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "moduleResolution": "node", 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "target": "es2015", 12 | "typeRoots": ["node_modules/@types"], 13 | "lib": ["es2018", "dom"], 14 | "module": "es2020" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /starter/ngTvdTraining/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "align": { 5 | "options": [ 6 | "parameters", 7 | "statements" 8 | ] 9 | }, 10 | "array-type": false, 11 | "arrow-parens": false, 12 | "arrow-return-shorthand": true, 13 | "deprecation": { 14 | "severity": "warn" 15 | }, 16 | "component-class-suffix": true, 17 | "contextual-lifecycle": true, 18 | "curly": true, 19 | "directive-class-suffix": true, 20 | "directive-selector": [true, "attribute", "app", "camelCase"], 21 | "component-selector": [ 22 | true, 23 | "element", 24 | "app", 25 | "kebab-case" 26 | ], 27 | "eofline": true, 28 | "import-blacklist": [ 29 | true, 30 | "rxjs/Rx" 31 | ], 32 | "import-spacing": true, 33 | "indent": { 34 | "options": [ 35 | "spaces" 36 | ] 37 | }, 38 | "interface-name": false, 39 | "max-classes-per-file": false, 40 | "max-line-length": [true, 140], 41 | "member-access": false, 42 | "member-ordering": [ 43 | true, 44 | { 45 | "order": ["static-field", "instance-field", "static-method", "instance-method"] 46 | } 47 | ], 48 | "no-consecutive-blank-lines": false, 49 | "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], 50 | "no-empty": false, 51 | "no-inferrable-types": [true, "ignore-params"], 52 | "no-non-null-assertion": true, 53 | "no-redundant-jsdoc": true, 54 | "no-switch-case-fall-through": true, 55 | "no-var-requires": false, 56 | "object-literal-key-quotes": [true, "as-needed"], 57 | "object-literal-sort-keys": false, 58 | "ordered-imports": false, 59 | "quotemark": [true, "single"], 60 | "trailing-comma": false, 61 | "no-conflicting-lifecycle": true, 62 | "no-host-metadata-property": true, 63 | "no-input-rename": true, 64 | "no-inputs-metadata-property": true, 65 | "no-output-native": true, 66 | "no-output-on-prefix": true, 67 | "no-output-rename": true, 68 | "semicolon": { 69 | "options": [ 70 | "always" 71 | ] 72 | }, 73 | "space-before-function-paren": { 74 | "options": { 75 | "anonymous": "never", 76 | "asyncArrow": "always", 77 | "constructor": "never", 78 | "method": "never", 79 | "named": "never" 80 | } 81 | }, 82 | "no-outputs-metadata-property": true, 83 | "template-banana-in-box": true, 84 | "template-no-negated-async": true, 85 | "typedef-whitespace": { 86 | "options": [ 87 | { 88 | "call-signature": "nospace", 89 | "index-signature": "nospace", 90 | "parameter": "nospace", 91 | "property-declaration": "nospace", 92 | "variable-declaration": "nospace" 93 | }, 94 | { 95 | "call-signature": "onespace", 96 | "index-signature": "onespace", 97 | "parameter": "onespace", 98 | "property-declaration": "onespace", 99 | "variable-declaration": "onespace" 100 | } 101 | ] 102 | }, 103 | "use-lifecycle-interface": true, 104 | "use-pipe-transform-interface": true, 105 | "variable-name": { 106 | "options": [ 107 | "ban-keywords", 108 | "check-format", 109 | "allow-pascal-case" 110 | ] 111 | }, 112 | "whitespace": { 113 | "options": [ 114 | "check-branch", 115 | "check-decl", 116 | "check-operator", 117 | "check-separator", 118 | "check-type", 119 | "check-typecast" 120 | ] 121 | } 122 | }, 123 | "rulesDirectory": ["codelyzer"] 124 | } 125 | --------------------------------------------------------------------------------