├── .docker ├── nginx.conf └── nginx.dockerfile ├── .editorconfig ├── .gitignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── README.md ├── angular.json ├── docker-compose.yml ├── e2e ├── app.e2e-spec.ts ├── app.po.ts └── tsconfig.e2e.json ├── karma.conf.js ├── microservices ├── aspnet_core │ ├── .docker │ │ ├── aspnetcore.development.dockerfile │ │ ├── aspnetcore.production.dockerfile │ │ └── wait-for-postgres.sh │ ├── APIs │ │ └── CustomersServiceController.cs │ ├── AspNetCorePostgreSQLDockerApp.csproj │ ├── Controllers │ │ ├── CustomersController.cs │ │ └── HomeController.cs │ ├── Models │ │ ├── ApiResponse.cs │ │ ├── Customer.cs │ │ ├── DockerCommand.cs │ │ ├── DockerCommandExample.cs │ │ ├── Order.cs │ │ ├── PagingResult.cs │ │ └── State.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Repository │ │ ├── CustomersDbContext.cs │ │ ├── CustomersDbSeeder.cs │ │ ├── CustomersRepository.cs │ │ ├── DockerCommandsDbContext.cs │ │ ├── DockerCommandsDbSeeder.cs │ │ ├── DockerCommandsRepository.cs │ │ ├── ICustomersRepository.cs │ │ └── IDockerCommandsRepository.cs │ ├── Startup.cs │ └── appsettings.json └── node │ ├── .docker │ ├── node.development.dockerfile │ └── useful-commands.md │ ├── .vscode │ ├── launch.json │ └── settings.json │ ├── config │ └── config.development.json │ ├── controllers │ └── api │ │ ├── customers │ │ └── customers.controller.js │ │ └── states │ │ └── states.controller.js │ ├── lib │ ├── configLoader.js │ ├── customersRepository.js │ ├── database.js │ ├── dbSeeder.js │ └── statesRepository.js │ ├── models │ ├── customer.js │ └── state.js │ ├── package-lock.json │ ├── package.json │ ├── routes │ └── router.js │ └── server.js ├── package-lock.json ├── package.json ├── protractor.conf.js ├── solvedIssues.md ├── src ├── app │ ├── app-routing.module.ts │ ├── app.component.js │ ├── app.component.js.map │ ├── app.component.ts │ ├── app.module.js │ ├── app.module.js.map │ ├── app.module.ts │ ├── app.routing.js │ ├── app.routing.js.map │ ├── core │ │ ├── core.module.js │ │ ├── core.module.js.map │ │ ├── core.module.ts │ │ ├── data-filter.service.js │ │ ├── data-filter.service.js.map │ │ ├── data-filter.service.ts │ │ ├── data.service.js │ │ ├── data.service.js.map │ │ ├── data.service.ts │ │ ├── sorter.js │ │ ├── sorter.js.map │ │ ├── sorter.ts │ │ ├── trackby.service.js │ │ ├── trackby.service.js.map │ │ └── trackby.service.ts │ ├── customers │ │ ├── customer-edit-reactive.component.js │ │ ├── customer-edit-reactive.component.js.map │ │ ├── customer-edit.component.html │ │ ├── customer-edit.component.js │ │ ├── customer-edit.component.js.map │ │ ├── customer-edit.component.ts │ │ ├── customers-grid.component.html │ │ ├── customers-grid.component.js │ │ ├── customers-grid.component.js.map │ │ ├── customers-grid.component.ts │ │ ├── customers.component.html │ │ ├── customers.component.js │ │ ├── customers.component.js.map │ │ └── customers.component.ts │ └── shared │ │ ├── ensureModuleLoadedOnceGuard.js │ │ ├── ensureModuleLoadedOnceGuard.js.map │ │ ├── ensureModuleLoadedOnceGuard.ts │ │ ├── filter-textbox │ │ ├── filter-textbox.component.js │ │ ├── filter-textbox.component.js.map │ │ └── filter-textbox.component.ts │ │ ├── interfaces.js │ │ ├── interfaces.js.map │ │ ├── interfaces.ts │ │ ├── pagination │ │ ├── pagination.component.css │ │ ├── pagination.component.html │ │ ├── pagination.component.js │ │ ├── pagination.component.js.map │ │ └── pagination.component.ts │ │ ├── pipes │ │ ├── capitalize.pipe.js │ │ ├── capitalize.pipe.js.map │ │ ├── capitalize.pipe.ts │ │ ├── trim.pipe.js │ │ ├── trim.pipe.js.map │ │ └── trim.pipe.ts │ │ ├── property-resolver.js │ │ ├── property-resolver.js.map │ │ ├── property-resolver.ts │ │ ├── shared.module.js │ │ ├── shared.module.js.map │ │ ├── shared.module.ts │ │ ├── validation.service.js │ │ ├── validation.service.js.map │ │ └── validation.service.ts ├── assets │ ├── images │ │ ├── favicon.ico │ │ ├── female.png │ │ ├── male.png │ │ └── people.png │ └── styles │ │ └── styles.css ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── main.ts ├── polyfills.ts ├── test.ts ├── tsconfig.app.json ├── tsconfig.spec.json └── typings.d.ts ├── tsconfig.json └── tslint.json /.docker/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 4; 2 | 3 | events { worker_connections 1024; } 4 | 5 | http { 6 | ssl_session_cache shared:SSL:10m; 7 | ssl_session_timeout 30m; 8 | 9 | #See http://blog.argteam.com/coding/hardening-node-js-for-production-part-2-using-nginx-to-avoid-node-js-load 10 | proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=one:8m max_size=3000m inactive=600m; 11 | proxy_temp_path /var/tmp; 12 | include mime.types; 13 | default_type application/octet-stream; 14 | sendfile on; 15 | keepalive_timeout 65; 16 | 17 | gzip on; 18 | gzip_comp_level 6; 19 | gzip_vary on; 20 | gzip_min_length 1000; 21 | gzip_proxied any; 22 | gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; 23 | gzip_buffers 16 8k; 24 | 25 | server { 26 | listen 80; 27 | server_name localhost; 28 | 29 | location / { 30 | root /usr/share/nginx/html; 31 | index index.html; 32 | expires -1; 33 | add_header Pragma "no-cache"; 34 | add_header Cache-Control "no-store, no-cache, must-revalidate, post-check=0, pre-check=0"; 35 | try_files $uri$args $uri$args/ $uri $uri/ /index.html =404; 36 | } 37 | } 38 | 39 | 40 | } -------------------------------------------------------------------------------- /.docker/nginx.dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:alpine 2 | 3 | LABEL author="Dan Wahlin" 4 | 5 | # Copy custom nginx config 6 | COPY ./.docker/nginx.conf /etc/nginx/nginx.conf 7 | 8 | EXPOSE 80 443 9 | 10 | ENTRYPOINT ["nginx", "-g", "daemon off;"] -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | bin 4 | obj 5 | 6 | # compiled output 7 | /dist 8 | /tmp 9 | /out-tsc 10 | 11 | # dependencies 12 | node_modules 13 | 14 | # IDEs and editors 15 | /.idea 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # IDE - VSCode 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | !.vscode/launch.json 28 | !.vscode/extensions.json 29 | 30 | # misc 31 | /.sass-cache 32 | /connect.lock 33 | /coverage 34 | /libpeerconnection.log 35 | npm-debug.log 36 | testem.log 37 | /typings 38 | 39 | # e2e 40 | /e2e/*.js 41 | /e2e/*.map 42 | 43 | # System Files 44 | .DS_Store 45 | Thumbs.db 46 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (web)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceRoot}/microservices/aspnet_core/bin/Debug/netcoreapp2.0/AspNetCorePostgreSQLDockerApp.dll", 14 | "args": [], 15 | "cwd": "${workspaceRoot}/microservices/aspnet_core", 16 | "stopAtEntry": false, 17 | "internalConsoleOptions": "openOnSessionStart", 18 | "launchBrowser": { 19 | "enabled": true, 20 | "args": "${auto-detect-url}", 21 | "windows": { 22 | "command": "cmd.exe", 23 | "args": "/C start ${auto-detect-url}" 24 | }, 25 | "osx": { 26 | "command": "open" 27 | }, 28 | "linux": { 29 | "command": "xdg-open" 30 | } 31 | }, 32 | "env": { 33 | "ASPNETCORE_ENVIRONMENT": "Development" 34 | }, 35 | "sourceFileMap": { 36 | "/Views": "${workspaceRoot}/Views" 37 | } 38 | }, 39 | { 40 | "name": ".NET Core Attach", 41 | "type": "coreclr", 42 | "request": "attach", 43 | "processId": "${command:pickProcess}" 44 | } 45 | ] 46 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "**/app/**/*.js.map": true, 5 | "**/app/**/*.js": true, 6 | 7 | "**/config/*.js.map": true, 8 | "**/config/*.js": { 9 | "when": "$(basename).js.map" 10 | } 11 | }, 12 | "typescript.check.tscVersion": false 13 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | "command": "dotnet", 4 | "isShellCommand": true, 5 | "args": [], 6 | "tasks": [ 7 | { 8 | "taskName": "build", 9 | "args": [ 10 | "${workspaceRoot}/microservices/aspnet_core/AspNetCorePostgreSQLDockerApp.csproj" 11 | ], 12 | "isBuildCommand": true, 13 | "problemMatcher": "$msCompile" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular, Docker and Microservices 2 | 3 | **NOTE**: If you're using Docker CE on Windows, remove the cAdvisor service defined at the bottom of the 4 | docker-compose.yml file before running the project. 5 | 6 | To run the project (development mode): 7 | 8 | 1. Install Docker CE for Mac or Windows (http://docker.com) 9 | 10 | 1. Install Angular CLI: `npm install @angular/cli -g` 11 | 12 | 1. Run `npm install` at the root of the project 13 | 14 | 1. Run `npm install` in ./microservices/node 15 | 16 | 1. Move back to the project root 17 | 18 | 1. Run `ng build` 19 | 20 | 1. Run `docker-compose build` 21 | 22 | 1. Run `docker-compose up` 23 | 24 | 1. Navigate to http://localhost 25 | 26 | 1. To see your container CPU utilization, memory, etc. visit the 27 | cAdvisor URL: http://localhost:8080 -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "test-project": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "architect": { 11 | "build": { 12 | "builder": "@angular-devkit/build-angular:browser", 13 | "options": { 14 | "outputPath": "dist", 15 | "index": "src/index.html", 16 | "main": "src/main.ts", 17 | "tsConfig": "src/tsconfig.app.json", 18 | "polyfills": "src/polyfills.ts", 19 | "assets": [ 20 | "src/assets", 21 | "src/favicon.ico" 22 | ], 23 | "styles": [ 24 | "src/assets/styles/styles.css" 25 | ], 26 | "scripts": [] 27 | }, 28 | "configurations": { 29 | "production": { 30 | "optimization": true, 31 | "outputHashing": "all", 32 | "sourceMap": false, 33 | "extractCss": true, 34 | "namedChunks": false, 35 | "aot": true, 36 | "extractLicenses": true, 37 | "vendorChunk": false, 38 | "buildOptimizer": true, 39 | "fileReplacements": [ 40 | { 41 | "replace": "src/environments/environment.ts", 42 | "with": "src/environments/environment.prod.ts" 43 | } 44 | ] 45 | } 46 | } 47 | }, 48 | "serve": { 49 | "builder": "@angular-devkit/build-angular:dev-server", 50 | "options": { 51 | "browserTarget": "test-project:build" 52 | }, 53 | "configurations": { 54 | "production": { 55 | "browserTarget": "test-project:build:production" 56 | } 57 | } 58 | }, 59 | "extract-i18n": { 60 | "builder": "@angular-devkit/build-angular:extract-i18n", 61 | "options": { 62 | "browserTarget": "test-project:build" 63 | } 64 | }, 65 | "test": { 66 | "builder": "@angular-devkit/build-angular:karma", 67 | "options": { 68 | "main": "src/test.ts", 69 | "karmaConfig": "./karma.conf.js", 70 | "polyfills": "src/polyfills.ts", 71 | "tsConfig": "src/tsconfig.spec.json", 72 | "scripts": [], 73 | "styles": [ 74 | "src/assets/styles/styles.css" 75 | ], 76 | "assets": [ 77 | "src/assets", 78 | "src/favicon.ico" 79 | ] 80 | } 81 | }, 82 | "lint": { 83 | "builder": "@angular-devkit/build-angular:tslint", 84 | "options": { 85 | "tsConfig": [ 86 | "src/tsconfig.app.json", 87 | "src/tsconfig.spec.json" 88 | ], 89 | "exclude": [] 90 | } 91 | } 92 | } 93 | }, 94 | "test-project-e2e": { 95 | "root": "", 96 | "sourceRoot": "", 97 | "projectType": "application", 98 | "architect": { 99 | "e2e": { 100 | "builder": "@angular-devkit/build-angular:protractor", 101 | "options": { 102 | "protractorConfig": "./protractor.conf.js", 103 | "devServerTarget": "test-project:serve" 104 | } 105 | }, 106 | "lint": { 107 | "builder": "@angular-devkit/build-angular:tslint", 108 | "options": { 109 | "tsConfig": [ 110 | "e2e/tsconfig.e2e.json" 111 | ], 112 | "exclude": [] 113 | } 114 | } 115 | } 116 | } 117 | }, 118 | "defaultProject": "test-project", 119 | "schematics": { 120 | "@schematics/angular:component": { 121 | "prefix": "app", 122 | "styleext": "css" 123 | }, 124 | "@schematics/angular:directive": { 125 | "prefix": "app" 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | # Run docker-compose build 2 | # Run docker-compose up 3 | # Live long and prosper 4 | 5 | version: '3.1' 6 | 7 | services: 8 | 9 | nginx: 10 | container_name: nginx 11 | image: nginx 12 | build: 13 | context: . 14 | dockerfile: .docker/nginx.dockerfile 15 | volumes: 16 | - ./dist:/usr/share/nginx/html 17 | ports: 18 | - "80:80" 19 | - "443:443" 20 | 21 | networks: 22 | - app-network 23 | 24 | node: 25 | container_name: nodeapp 26 | image: nodeapp 27 | build: 28 | context: ./microservices/node 29 | dockerfile: .docker/node.development.dockerfile 30 | volumes: 31 | - ./microservices/node:/var/www/angularnoderestfulservice 32 | environment: 33 | - NODE_ENV=development 34 | ports: 35 | - "3000:3000" 36 | depends_on: 37 | - mongodb 38 | networks: 39 | - app-network 40 | 41 | #No authentication is provided here - JUST A DEMO! That would absolutely 42 | #be needed for a "real" application 43 | mongodb: 44 | container_name: mongodb 45 | image: mongo 46 | networks: 47 | - app-network 48 | 49 | aspnet: 50 | container_name: 'aspnetcoreapp' 51 | image: 'aspnetcoreapp' 52 | build: 53 | context: ./microservices/aspnet_core 54 | dockerfile: .docker/aspnetcore.development.dockerfile 55 | volumes: 56 | - ./microservices/aspnet_core:/var/www/aspnetcoreapp 57 | ports: 58 | - "5000:5000" 59 | depends_on: 60 | - "postgres" 61 | networks: 62 | - app-network 63 | 64 | postgres: 65 | container_name: 'postgres' 66 | image: postgres 67 | environment: 68 | POSTGRES_PASSWORD: password 69 | networks: 70 | - app-network 71 | 72 | cadvisor: 73 | container_name: cadvisor 74 | image: google/cadvisor 75 | volumes: 76 | - /:/rootfs:ro 77 | - /var/run:/var/run:rw 78 | - /sys:/sys:ro 79 | - /var/lib/docker/:/var/lib/docker:ro 80 | ports: 81 | - "8080:8080" 82 | networks: 83 | - app-network 84 | 85 | networks: 86 | app-network: 87 | driver: bridge -------------------------------------------------------------------------------- /e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { MyProjectPage } from './app.po'; 2 | 3 | describe('my-project App', () => { 4 | let page: MyProjectPage; 5 | 6 | beforeEach(() => { 7 | page = new MyProjectPage(); 8 | }); 9 | 10 | it('should display message saying app works', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('my works!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, element, by } from 'protractor'; 2 | 3 | export class MyProjectPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('my-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types":[ 8 | "jasmine", 9 | "node" 10 | ] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/0.13/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-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 | files: [ 19 | 20 | ], 21 | preprocessors: { 22 | 23 | }, 24 | mime: { 25 | 'text/x-typescript': ['ts','tsx'] 26 | }, 27 | coverageIstanbulReporter: { 28 | dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ], 29 | fixWebpackSourcePaths: true 30 | }, 31 | angularCli: { 32 | environment: 'dev' 33 | }, 34 | reporters: config.angularCli && config.angularCli.codeCoverage 35 | ? ['progress', 'coverage-istanbul'] 36 | : ['progress', 'kjhtml'], 37 | port: 9876, 38 | colors: true, 39 | logLevel: config.LOG_INFO, 40 | autoWatch: true, 41 | browsers: ['Chrome'], 42 | singleRun: false 43 | }); 44 | }; 45 | -------------------------------------------------------------------------------- /microservices/aspnet_core/.docker/aspnetcore.development.dockerfile: -------------------------------------------------------------------------------- 1 | FROM microsoft/dotnet:sdk 2 | 3 | LABEL author="Dan Wahlin" 4 | 5 | ENV DOTNET_USE_POLLING_FILE_WATCHER=1 6 | ENV ASPNETCORE_URLS=http://*:5000 7 | ENV ASPNETCORE_ENVIRONMENT=development 8 | 9 | EXPOSE 5000 10 | 11 | WORKDIR /var/www/aspnetcoreapp 12 | 13 | CMD ["/bin/bash", "-c", "dotnet restore && dotnet watch run"] 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | # Build the image: 24 | # docker build -f aspnetcore.development.dockerfile -t [yourDockerHubID]/dotnet:1.0.0 25 | 26 | # Option 1 27 | # Start PostgreSQL and ASP.NET Core (link ASP.NET core to ProgreSQL container with legacy linking) 28 | 29 | # docker run -d --name my-postgres -e POSTGRES_PASSWORD=password postgres 30 | # docker run -d -p 5000:5000 --link my-postgres:postgres [yourDockerHubID]/dotnet:1.0.0 31 | 32 | # Option 2: Create a custom bridge network and add containers into it 33 | 34 | # docker network create --driver bridge isolated_network 35 | # docker run -d --net=isolated_network --name postgres -e POSTGRES_PASSWORD=password postgres 36 | # docker run -d --net=isolated_network --name aspnetcoreapp -p 5000:5000 [yourDockerHubID]/dotnet:1.0.0 -------------------------------------------------------------------------------- /microservices/aspnet_core/.docker/aspnetcore.production.dockerfile: -------------------------------------------------------------------------------- 1 | FROM microsoft/dotnet:sdk as publish 2 | WORKDIR /publish 3 | COPY AspNetCorePostgreSQLDockerApp.csproj . 4 | RUN dotnet restore 5 | COPY . . 6 | RUN dotnet publish --output ./out 7 | 8 | FROM microsoft/dotnet:aspnetcore-runtime 9 | LABEL author="Dan Wahlin" 10 | WORKDIR /var/www/aspnetcoreapp 11 | COPY --from=publish /publish/out . 12 | ENV ASPNETCORE_URLS=http://*:5000 13 | ENV ASPNETCORE_ENVIRONMENT=production 14 | EXPOSE 5000 15 | ENTRYPOINT ["dotnet", "AspNetCorePostgreSQLDockerApp.dll"] 16 | 17 | 18 | # Build the image: 19 | # docker build -f aspnetcore.production.dockerfile -t [yourDockerHubID]/dotnet:1.0.0 20 | # docker push 21 | 22 | # Option 1 23 | # Start PostgreSQL and ASP.NET Core (link ASP.NET core to ProgreSQL container with legacy linking) 24 | 25 | # docker run -d --name my-postgres -e POSTGRES_PASSWORD=password postgres 26 | # docker run -d -p 5000:5000 --link my-postgres:postgres [yourDockerHubID]/dotnet:1.0.0 27 | 28 | # Option 2: Create a custom bridge network and add containers into it 29 | 30 | # docker network create --driver bridge isolated_network 31 | # docker run -d --net=isolated_network --name postgres -e POSTGRES_PASSWORD=password postgres 32 | # docker run -d --net=isolated_network --name aspnetcoreapp -p 5000:5000 [yourDockerHubID]/dotnet:1.0.0 -------------------------------------------------------------------------------- /microservices/aspnet_core/.docker/wait-for-postgres.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | host="$1" 6 | shift 7 | cmd="$@" 8 | 9 | until psql -h "$host" -U "postgres" -c '\l'; do 10 | >&2 echo "Postgres is unavailable - sleeping" 11 | sleep 1 12 | done 13 | 14 | >&2 echo "Postgres is up - executing command" 15 | exec $cmd 16 | 17 | #Can call from docker-compose.yml by adding the following to dependent service entrypoint: ./.docker/wait-for-postgres.sh postgres:5432 -------------------------------------------------------------------------------- /microservices/aspnet_core/APIs/CustomersServiceController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using AspNetCorePostgreSQLDockerApp.Models; 7 | using AspNetCorePostgreSQLDockerApp.Repository; 8 | using Microsoft.AspNetCore.Http; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace AspNetCorePostgreSQLDockerApp.Apis 12 | { 13 | [Route("api/customers")] 14 | public class CustomersApiController : Controller 15 | { 16 | ICustomersRepository _CustomersRepository; 17 | ILogger _Logger; 18 | 19 | public CustomersApiController(ICustomersRepository customersRepo, ILoggerFactory loggerFactory) { 20 | _CustomersRepository = customersRepo; 21 | _Logger = loggerFactory.CreateLogger(nameof(CustomersApiController)); 22 | } 23 | 24 | // GET api/customers 25 | [HttpGet] 26 | [ProducesResponseType(typeof(List), 200)] 27 | [ProducesResponseType(typeof(ApiResponse), 400)] 28 | public async Task Customers() 29 | { 30 | try 31 | { 32 | var customers = await _CustomersRepository.GetCustomersAsync(); 33 | return Ok(customers); 34 | } 35 | catch (Exception exp) 36 | { 37 | _Logger.LogError(exp.Message); 38 | return BadRequest(new ApiResponse { Status = false }); 39 | } 40 | } 41 | 42 | // GET api/customers/page/10/10 43 | [HttpGet("page/{skip}/{take}")] 44 | [ProducesResponseType(typeof(List), 200)] 45 | [ProducesResponseType(typeof(ApiResponse), 400)] 46 | public async Task CustomersPage(int skip, int take) 47 | { 48 | try 49 | { 50 | var pagingResult = await _CustomersRepository.GetCustomersPageAsync(skip, take); 51 | Response.Headers.Add("X-InlineCount", pagingResult.TotalRecords.ToString()); 52 | return Ok(pagingResult.Records); 53 | } 54 | catch (Exception exp) 55 | { 56 | _Logger.LogError(exp.Message); 57 | return BadRequest(new ApiResponse { Status = false }); 58 | } 59 | } 60 | 61 | // GET api/customers/5 62 | [HttpGet("{id}", Name = "GetCustomerRoute")] 63 | [ProducesResponseType(typeof(Customer), 200)] 64 | [ProducesResponseType(typeof(ApiResponse), 400)] 65 | public async Task Customers(int id) 66 | { 67 | try 68 | { 69 | var customer = await _CustomersRepository.GetCustomerAsync(id); 70 | return Ok(customer); 71 | } 72 | catch (Exception exp) 73 | { 74 | _Logger.LogError(exp.Message); 75 | return BadRequest(new ApiResponse { Status = false }); 76 | } 77 | } 78 | 79 | // POST api/customers 80 | [HttpPost] 81 | [ProducesResponseType(typeof(ApiResponse), 201)] 82 | [ProducesResponseType(typeof(ApiResponse), 400)] 83 | public async Task CreateCustomer([FromBody]Customer customer) 84 | { 85 | if (!ModelState.IsValid) 86 | { 87 | return BadRequest(new ApiResponse { Status = false, ModelState = ModelState }); 88 | } 89 | 90 | try 91 | { 92 | var newCustomer = await _CustomersRepository.InsertCustomerAsync(customer); 93 | if (newCustomer == null) 94 | { 95 | return BadRequest(new ApiResponse { Status = false }); 96 | } 97 | return CreatedAtRoute("GetCustomerRoute", new { id = newCustomer.Id }, 98 | new ApiResponse { Status = true, Customer = newCustomer }); 99 | } 100 | catch (Exception exp) 101 | { 102 | _Logger.LogError(exp.Message); 103 | return BadRequest(new ApiResponse { Status = false }); 104 | } 105 | } 106 | 107 | // PUT api/customers/5 108 | [HttpPut("{id}")] 109 | [ProducesResponseType(typeof(ApiResponse), 200)] 110 | [ProducesResponseType(typeof(ApiResponse), 400)] 111 | public async Task UpdateCustomer(int id, [FromBody]Customer customer) 112 | { 113 | if (!ModelState.IsValid) 114 | { 115 | return BadRequest(new ApiResponse { Status = false, ModelState = ModelState }); 116 | } 117 | 118 | try 119 | { 120 | var status = await _CustomersRepository.UpdateCustomerAsync(customer); 121 | if (!status) 122 | { 123 | return BadRequest(new ApiResponse { Status = false }); 124 | } 125 | return Ok(new ApiResponse { Status = true, Customer = customer }); 126 | } 127 | catch (Exception exp) 128 | { 129 | _Logger.LogError(exp.Message); 130 | return BadRequest(new ApiResponse { Status = false }); 131 | } 132 | } 133 | 134 | // DELETE api/customers/5 135 | [HttpDelete("{id}")] 136 | [ProducesResponseType(typeof(ApiResponse), 200)] 137 | [ProducesResponseType(typeof(ApiResponse), 400)] 138 | public async Task DeleteCustomer(int id) 139 | { 140 | try 141 | { 142 | var status = await _CustomersRepository.DeleteCustomerAsync(id); 143 | if (!status) 144 | { 145 | return BadRequest(new ApiResponse { Status = false }); 146 | } 147 | return Ok(new ApiResponse { Status = true }); 148 | } 149 | catch (Exception exp) 150 | { 151 | _Logger.LogError(exp.Message); 152 | return BadRequest(new ApiResponse { Status = false }); 153 | } 154 | } 155 | 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /microservices/aspnet_core/AspNetCorePostgreSQLDockerApp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /microservices/aspnet_core/Controllers/CustomersController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | 7 | namespace AspNetCorePostgreSQLDockerApp.Controllers 8 | { 9 | public class CustomersController : Controller 10 | { 11 | 12 | public ActionResult Index() 13 | { 14 | return View(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /microservices/aspnet_core/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | 7 | using AspNetCorePostgreSQLDockerApp.Repository; 8 | 9 | namespace AspNetCorePostgreSQLDockerApp.Controllers 10 | { 11 | public class HomeController : Controller 12 | { 13 | 14 | IDockerCommandsRepository _repo; 15 | 16 | public HomeController(IDockerCommandsRepository repo) { 17 | _repo = repo; 18 | } 19 | 20 | public async Task Index() 21 | { 22 | //Call into PostgreSQL 23 | var commands = await _repo.GetDockerCommandsAsync(); 24 | return View(commands); 25 | } 26 | 27 | public IActionResult About() 28 | { 29 | ViewData["Message"] = "Your application description page."; 30 | 31 | return View(); 32 | } 33 | 34 | public IActionResult Contact() 35 | { 36 | ViewData["Message"] = "Your contact page."; 37 | 38 | return View(); 39 | } 40 | 41 | // public IActionResult Error() 42 | // { 43 | // return View(); 44 | // } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /microservices/aspnet_core/Models/ApiResponse.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.ModelBinding; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace AspNetCorePostgreSQLDockerApp.Models 8 | { 9 | public class ApiResponse 10 | { 11 | public bool Status { get; set; } 12 | public Customer Customer { get; set; } 13 | public ModelStateDictionary ModelState { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /microservices/aspnet_core/Models/Customer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace AspNetCorePostgreSQLDockerApp.Models { 4 | public class Customer 5 | { 6 | public int Id { get; set; } 7 | public string FirstName { get; set; } 8 | public string LastName { get; set; } 9 | public string Email { get; set; } 10 | public string Address { get; set; } 11 | public string City { get; set; } 12 | public int StateId { get; set; } 13 | public State State { get; set; } 14 | public int Zip { get; set; } 15 | public string Gender { get; set; } 16 | public int OrderCount { get; set; } 17 | public List Orders { get; set; } 18 | } 19 | } -------------------------------------------------------------------------------- /microservices/aspnet_core/Models/DockerCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace AspNetCorePostgreSQLDockerApp.Models { 5 | 6 | public class DockerCommand { 7 | public int Id { get; set; } 8 | public string Command { get; set; } 9 | public string Description { get; set; } 10 | public List Examples { get; set; } 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /microservices/aspnet_core/Models/DockerCommandExample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AspNetCorePostgreSQLDockerApp.Models { 4 | 5 | public class DockerCommandExample { 6 | public int Id { get; set; } 7 | public string Example { get; set; } 8 | public string Description { get; set; } 9 | } 10 | 11 | } -------------------------------------------------------------------------------- /microservices/aspnet_core/Models/Order.cs: -------------------------------------------------------------------------------- 1 | namespace AspNetCorePostgreSQLDockerApp.Models { 2 | public class Order { 3 | public int Id { get; set; } 4 | public string Product { get; set; } 5 | public int Quantity { get; set; } 6 | public decimal Price { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /microservices/aspnet_core/Models/PagingResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace AspNetCorePostgreSQLDockerApp.Models 7 | { 8 | public struct PagingResult 9 | { 10 | public IEnumerable Records { get; set; } 11 | public int TotalRecords { get; set; } 12 | 13 | public PagingResult(IEnumerable items, int totalRecords) 14 | { 15 | TotalRecords = totalRecords; 16 | Records = items; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /microservices/aspnet_core/Models/State.cs: -------------------------------------------------------------------------------- 1 | namespace AspNetCorePostgreSQLDockerApp.Models { 2 | public class State { 3 | public int Id { get; set; } 4 | public string Abbreviation { get; set; } 5 | public string Name { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /microservices/aspnet_core/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore; 2 | using Microsoft.AspNetCore.Hosting; 3 | 4 | namespace AspNetCorePostgreSQLDockerApp 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateWebHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 14 | WebHost.CreateDefaultBuilder(args) 15 | .UseStartup(); 16 | } 17 | } -------------------------------------------------------------------------------- /microservices/aspnet_core/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:50389/", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "AspNetCorePostgreSQLDockerApp": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "launchUrl": "http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /microservices/aspnet_core/Repository/CustomersDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using AspNetCorePostgreSQLDockerApp.Models; 3 | 4 | namespace AspNetCorePostgreSQLDockerApp.Repository 5 | { 6 | public class CustomersDbContext : DbContext 7 | { 8 | public DbSet Customers { get; set; } 9 | public DbSet Orders { get; set; } 10 | public DbSet States { get; set; } 11 | 12 | public CustomersDbContext (DbContextOptions options) : base(options) { } 13 | } 14 | } -------------------------------------------------------------------------------- /microservices/aspnet_core/Repository/CustomersRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.Extensions.Logging; 7 | using AspNetCorePostgreSQLDockerApp.Models; 8 | 9 | namespace AspNetCorePostgreSQLDockerApp.Repository 10 | { 11 | public class CustomersRepository : ICustomersRepository 12 | { 13 | 14 | private readonly CustomersDbContext _Context; 15 | private readonly ILogger _Logger; 16 | 17 | public CustomersRepository(CustomersDbContext context, ILoggerFactory loggerFactory) { 18 | _Context = context; 19 | _Logger = loggerFactory.CreateLogger("CustomersRepository"); 20 | } 21 | 22 | public async Task> GetCustomersAsync() 23 | { 24 | return await _Context.Customers.OrderBy(c => c.LastName) 25 | .Include(c => c.State).ToListAsync(); 26 | } 27 | 28 | public async Task> GetCustomersPageAsync(int skip, int take) 29 | { 30 | var totalRecords = await _Context.Customers.CountAsync(); 31 | var customers = await _Context.Customers 32 | .OrderBy(c => c.LastName) 33 | .Include(c => c.State) 34 | .Include(c => c.Orders) 35 | .Skip(skip) 36 | .Take(take) 37 | .ToListAsync(); 38 | return new PagingResult(customers, totalRecords); 39 | } 40 | 41 | public async Task GetCustomerAsync(int id) 42 | { 43 | return await _Context.Customers 44 | .Include(c => c.State) 45 | .SingleOrDefaultAsync(c => c.Id == id); 46 | } 47 | 48 | public async Task InsertCustomerAsync(Customer customer) 49 | { 50 | _Context.Add(customer); 51 | try 52 | { 53 | await _Context.SaveChangesAsync(); 54 | } 55 | catch (System.Exception exp) 56 | { 57 | _Logger.LogError($"Error in {nameof(InsertCustomerAsync)}: " + exp.Message); 58 | } 59 | 60 | return customer; 61 | } 62 | 63 | public async Task UpdateCustomerAsync(Customer customer) 64 | { 65 | //Will update all properties of the Customer 66 | _Context.Customers.Attach(customer); 67 | _Context.Entry(customer).State = EntityState.Modified; 68 | try 69 | { 70 | return (await _Context.SaveChangesAsync() > 0 ? true : false); 71 | } 72 | catch (Exception exp) 73 | { 74 | _Logger.LogError($"Error in {nameof(UpdateCustomerAsync)}: " + exp.Message); 75 | } 76 | return false; 77 | } 78 | 79 | public async Task DeleteCustomerAsync(int id) 80 | { 81 | //Extra hop to the database but keeps it nice and simple for this demo 82 | //Including orders since there's a foreign-key constraint and we need 83 | //to remove the orders in addition to the customer 84 | var customer = await _Context.Customers 85 | .Include(c => c.Orders) 86 | .SingleOrDefaultAsync(c => c.Id == id); 87 | _Context.Remove(customer); 88 | try 89 | { 90 | return (await _Context.SaveChangesAsync() > 0 ? true : false); 91 | } 92 | catch (System.Exception exp) 93 | { 94 | _Logger.LogError($"Error in {nameof(DeleteCustomerAsync)}: " + exp.Message); 95 | } 96 | return false; 97 | } 98 | 99 | } 100 | } -------------------------------------------------------------------------------- /microservices/aspnet_core/Repository/DockerCommandsDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using AspNetCorePostgreSQLDockerApp.Models; 3 | 4 | namespace AspNetCorePostgreSQLDockerApp.Repository 5 | { 6 | public class DockerCommandsDbContext : DbContext 7 | { 8 | public DbSet DockerCommands { get; set; } 9 | 10 | public DockerCommandsDbContext(DbContextOptions options) : base(options) { } 11 | } 12 | } -------------------------------------------------------------------------------- /microservices/aspnet_core/Repository/DockerCommandsDbSeeder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using System.Linq; 6 | using Microsoft.Extensions.Logging; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using AspNetCorePostgreSQLDockerApp.Models; 9 | 10 | namespace AspNetCorePostgreSQLDockerApp.Repository 11 | { 12 | public class DockerCommandsDbSeeder 13 | { 14 | readonly ILogger _logger; 15 | 16 | public DockerCommandsDbSeeder(ILoggerFactory loggerFactory) 17 | { 18 | _logger = loggerFactory.CreateLogger("DockerCommandsDbSeederLogger"); 19 | } 20 | 21 | public async Task SeedAsync(IServiceProvider serviceProvider) 22 | { 23 | //Based on EF team's example at https://github.com/aspnet/MusicStore/blob/dev/samples/MusicStore/Models/SampleData.cs 24 | using (var serviceScope = serviceProvider.GetRequiredService().CreateScope()) 25 | { 26 | var dockerDb = serviceScope.ServiceProvider.GetService(); 27 | var customersDb = serviceScope.ServiceProvider.GetService(); 28 | 29 | if (await dockerDb.Database.EnsureCreatedAsync()) 30 | { 31 | if (!await dockerDb.DockerCommands.AnyAsync()) { 32 | await InsertDockerSampleData(dockerDb); 33 | } 34 | } 35 | } 36 | } 37 | 38 | public async Task InsertDockerSampleData(DockerCommandsDbContext db) 39 | { 40 | var commands = GetDockerCommands(); 41 | db.DockerCommands.AddRange(commands); 42 | 43 | try 44 | { 45 | await db.SaveChangesAsync(); 46 | } 47 | catch (Exception exp) 48 | { 49 | _logger.LogError($"Error in {nameof(DockerCommandsDbSeeder)}: " + exp.Message); 50 | } 51 | 52 | } 53 | 54 | private List GetDockerCommands() 55 | { 56 | var cmd1 = new DockerCommand { 57 | Command = "run", 58 | Description = "Runs a Docker container", 59 | Examples = new List { 60 | new DockerCommandExample { 61 | Example = "docker run imageName", 62 | Description = "Creates a running container from the image. Pulls it from Docker Hub if the image is not local" 63 | }, 64 | new DockerCommandExample { 65 | Example = "docker run -d -p 8080:3000 imageName", 66 | Description = "Runs a container in 'daemon' mode with an external port of 8080 and a container port of 3000." 67 | } 68 | } 69 | }; 70 | 71 | var cmd2 = new DockerCommand { 72 | Command = "ps", 73 | Description = "Lists containers", 74 | Examples = new List { 75 | new DockerCommandExample { 76 | Example = "docker ps", 77 | Description = "Lists all running containers" 78 | }, 79 | new DockerCommandExample { 80 | Example = "docker ps -a", 81 | Description = "Lists all containers (even if they are not running)" 82 | } 83 | } 84 | }; 85 | 86 | return new List { cmd1, cmd2 }; 87 | } 88 | 89 | } 90 | } -------------------------------------------------------------------------------- /microservices/aspnet_core/Repository/DockerCommandsRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | using Microsoft.Extensions.Logging; 8 | using AspNetCorePostgreSQLDockerApp.Models; 9 | 10 | namespace AspNetCorePostgreSQLDockerApp.Repository 11 | { 12 | public class DockerCommandsRepository : IDockerCommandsRepository 13 | { 14 | private readonly DockerCommandsDbContext _context; 15 | private readonly ILogger _logger; 16 | 17 | public DockerCommandsRepository(DockerCommandsDbContext context, ILoggerFactory loggerFactory) { 18 | _context = context; 19 | _logger = loggerFactory.CreateLogger("DockerCommandsRepository"); 20 | } 21 | 22 | public async Task> GetDockerCommandsAsync() { 23 | return await _context.DockerCommands.Include(dc => dc.Examples).ToListAsync(); 24 | } 25 | 26 | public async Task InsertDockerCommandAsync(DockerCommand command) { 27 | _context.DockerCommands.Add(command); 28 | try { 29 | await _context.SaveChangesAsync(); 30 | } 31 | catch (Exception exp) { 32 | _logger.LogError($"Error in {nameof(InsertDockerCommandAsync)}: " + exp.Message); 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /microservices/aspnet_core/Repository/ICustomersRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using AspNetCorePostgreSQLDockerApp.Models; 5 | 6 | namespace AspNetCorePostgreSQLDockerApp.Repository 7 | { 8 | public interface ICustomersRepository 9 | { 10 | Task> GetCustomersAsync(); 11 | Task> GetCustomersPageAsync(int skip, int take); 12 | Task GetCustomerAsync(int id); 13 | 14 | Task InsertCustomerAsync(Customer customer); 15 | Task UpdateCustomerAsync(Customer customer); 16 | Task DeleteCustomerAsync(int id); 17 | } 18 | } -------------------------------------------------------------------------------- /microservices/aspnet_core/Repository/IDockerCommandsRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | using AspNetCorePostgreSQLDockerApp.Models; 5 | 6 | namespace AspNetCorePostgreSQLDockerApp.Repository 7 | { 8 | public interface IDockerCommandsRepository 9 | { 10 | Task> GetDockerCommandsAsync(); 11 | 12 | Task InsertDockerCommandAsync(DockerCommand command); 13 | } 14 | } -------------------------------------------------------------------------------- /microservices/aspnet_core/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Microsoft.AspNetCore.Builder; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.EntityFrameworkCore; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Logging; 9 | using Microsoft.Extensions.FileProviders; 10 | using Swashbuckle.AspNetCore.Swagger; 11 | using Swashbuckle.AspNetCore.SwaggerUI; 12 | using AspNetCorePostgreSQLDockerApp.Repository; 13 | 14 | namespace AspNetCorePostgreSQLDockerApp 15 | { 16 | public class Startup 17 | { 18 | public Startup(IConfiguration configuration) 19 | { 20 | Configuration = configuration; 21 | } 22 | 23 | public IConfiguration Configuration { get; set; } 24 | 25 | // This method gets called by the runtime. Use this method to add services to the container. 26 | public void ConfigureServices(IServiceCollection services) 27 | { 28 | 29 | //Add PostgreSQL support 30 | services.AddEntityFrameworkNpgsql() 31 | .AddDbContext(options => 32 | options.UseNpgsql(Configuration["Data:DbContext:DockerCommandsConnectionString"])) 33 | .AddDbContext(options => 34 | options.UseNpgsql(Configuration["Data:DbContext:CustomersConnectionString"])); 35 | 36 | 37 | services.AddMvc(); 38 | 39 | // Add our PostgreSQL Repositories (scoped to each request) 40 | services.AddScoped(); 41 | services.AddScoped(); 42 | 43 | //Transient: Created each time they're needed 44 | services.AddTransient(); 45 | services.AddTransient(); 46 | 47 | //https://github.com/domaindrivendev/Swashbuckle.AspNetCore 48 | services.AddSwaggerGen(options => 49 | { 50 | options.SwaggerDoc("v1", new Info 51 | { 52 | Version = "v1", 53 | Title = "ASP.NET Core Customers API", 54 | Description = "ASP.NET Core/Angular Swagger Documentation", 55 | TermsOfService = "None", 56 | Contact = new Contact { Name = "Dan Wahlin", Url = "http://twitter.com/danwahlin" }, 57 | License = new License { Name = "MIT", Url = "https://en.wikipedia.org/wiki/MIT_License" } 58 | }); 59 | 60 | //Add XML comment document by uncommenting the following 61 | // var filePath = Path.Combine(PlatformServices.Default.Application.ApplicationBasePath, "MyApi.xml"); 62 | // options.IncludeXmlComments(filePath); 63 | 64 | }); 65 | } 66 | 67 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 68 | public void Configure(IApplicationBuilder app, 69 | IHostingEnvironment env, 70 | DockerCommandsDbSeeder dockerCommandsDbSeeder, 71 | CustomersDbSeeder customersDbSeeder) 72 | { 73 | if (env.IsDevelopment()) 74 | { 75 | app.UseDeveloperExceptionPage(); 76 | } 77 | else 78 | { 79 | app.UseExceptionHandler("/Home/Error"); 80 | } 81 | 82 | // Enable middleware to serve generated Swagger as a JSON endpoint 83 | app.UseSwagger(); 84 | 85 | // Enable middleware to serve swagger-ui assets (HTML, JS, CSS etc.) 86 | // Visit http://localhost:5000/swagger 87 | app.UseSwaggerUI(c => 88 | { 89 | c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); 90 | }); 91 | 92 | //This would need to be locked down as needed (very open right now) 93 | app.UseCors((corsPolicyBuilder) => 94 | { 95 | corsPolicyBuilder.AllowAnyOrigin(); 96 | corsPolicyBuilder.AllowAnyMethod(); 97 | corsPolicyBuilder.AllowAnyHeader(); 98 | corsPolicyBuilder.WithExposedHeaders("X-InlineCount"); 99 | }); 100 | 101 | app.UseMvc(routes => 102 | { 103 | routes.MapRoute( 104 | name: "default", 105 | template: "{controller=Home}/{action=Index}/{id?}"); 106 | }); 107 | 108 | customersDbSeeder.SeedAsync(app.ApplicationServices).Wait(); 109 | dockerCommandsDbSeeder.SeedAsync(app.ApplicationServices).Wait(); 110 | 111 | } 112 | 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /microservices/aspnet_core/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | }, 10 | "Data": { 11 | "DbContext": { 12 | "LocalConnectionString": "User ID=;Password=;Server=127.0.0.1;Port=5432;Database=;Pooling=true;", 13 | "ConnectionString": "User ID=postgres;Password=password;Server=postgres;Port=5432;Database=POSTGRES_USER;Integrated Security=true;Pooling=true;", 14 | "DockerCommandsConnectionString": "User ID=postgres;Password=password;Server=postgres;Port=5432;Database=DockerCommands;Integrated Security=true;Pooling=true;", 15 | "CustomersConnectionString": "User ID=postgres;Password=password;Server=postgres;Port=5432;Database=Customers;Integrated Security=true;Pooling=true;" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /microservices/node/.docker/node.development.dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:alpine 2 | 3 | LABEL author="Dan Wahlin" 4 | 5 | WORKDIR /var/www/angularnoderestfulservice 6 | 7 | RUN npm install nodemon -g 8 | 9 | EXPOSE 3000 10 | 11 | ENTRYPOINT ["nodemon", "server.js"] 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | # Build: docker build -f node.dockerfile -t danwahlin/node . 26 | 27 | # Option 1 28 | # Start MongoDB and Node (link Node to MongoDB container with legacy linking) 29 | 30 | # docker run -d --name mongodb mongo 31 | # docker run -d -p 3000:3000 --link mongodb --name nodeapp danwahlin/node 32 | 33 | # Option 2: Create a custom bridge network and add containers into it 34 | 35 | # docker network create --driver bridge isolated_network 36 | # docker run -d --net=isolated_network --name mongodb mongo 37 | # docker run -d --net=isolated_network --name nodeapp -p 3000:3000 danwahlin/node 38 | 39 | # Option 3: Use Docker Compose 40 | 41 | # docker-compose build 42 | # docker-compose up 43 | 44 | # Seed the database with sample database 45 | # Run: docker exec nodeapp node lib/dbSeeder.js -------------------------------------------------------------------------------- /microservices/node/.docker/useful-commands.md: -------------------------------------------------------------------------------- 1 | #Useful Docker Commands 2 | 3 | ##Docker Machine 4 | 5 | - `docker-machine start` - Start VM 6 | - `docker-machine stop` - Stop VM 7 | - `docker-machine env` - Display Docker client setup commands 8 | 9 | ##Docker Client 10 | 11 | - `docker --help` - Get help on a specific command 12 | - `docker pull ` - Pull image from Docker Hub 13 | - `docker images` - Show all images 14 | - `docker rmi ` - Remove specific images 15 | - `docker rmi $(docker images | grep "^" | awk "{print $3}")` - Remove untagged images - 16 | - `docker ps -a` - Show all containers 17 | - `docker rm ` -Remove specific container 18 | - `docker rm $(docker ps -a -q)` Remove all containers 19 | - `docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}'` - Formatted list of containers 20 | - `docker run -d --name -p : ` - Run a container 21 | - `docker build -f -t .` - Build an image from a Dockerfile 22 | - `docker login` - Login using your Docker credentials 23 | - `docker push ` - Push an image to Docker hub 24 | 25 | ## Docker Compose 26 | 27 | - `docker-compose build` - Build images based on docker-compose 28 | - `docker-compose up -d` - Start in daemon mode 29 | - `docker-compose logs` - Show logs from containers 30 | - `docker-compose up` - Start containers based on docker-compose 31 | - `docker-compose stop` - Stop containers 32 | - `docker-compose down` - Stop and remove containers 33 | 34 | -------------------------------------------------------------------------------- /microservices/node/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible Node.js debug attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "program": "${workspaceRoot}/server.js", 12 | "cwd": "${workspaceRoot}", 13 | "outFiles": [], 14 | "sourceMaps": true 15 | }, 16 | { 17 | "type": "node", 18 | "request": "attach", 19 | "name": "Attach to Process", 20 | "port": 5858, 21 | "outFiles": [], 22 | "sourceMaps": true 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /microservices/node/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "**/app/**/*.js.map": true, 5 | "**/app/**/*.js": true 6 | } 7 | } -------------------------------------------------------------------------------- /microservices/node/config/config.development.json: -------------------------------------------------------------------------------- 1 | { 2 | "databaseConfig": { 3 | "host": "mongodb", 4 | "database": "customermanager" 5 | } 6 | } -------------------------------------------------------------------------------- /microservices/node/controllers/api/customers/customers.controller.js: -------------------------------------------------------------------------------- 1 | const customersRepo = require('../../../lib/customersRepository'), 2 | statesRepo = require('../../../lib/statesRepository'), 3 | util = require('util'); 4 | 5 | class CustomersController { 6 | 7 | constructor(router) { 8 | router.get('/', this.getCustomers.bind(this)); 9 | router.get('/page/:skip/:top', this.getCustomersPage.bind(this)); 10 | router.get('/:id', this.getCustomer.bind(this)); 11 | router.post('/', this.insertCustomer.bind(this)); 12 | router.put('/:id', this.updateCustomer.bind(this)); 13 | router.delete('/:id', this.deleteCustomer.bind(this)); 14 | } 15 | 16 | getCustomers(req, res) { 17 | console.log('*** getCustomers'); 18 | customersRepo.getCustomers((err, data) => { 19 | if (err) { 20 | console.log('*** getCustomers error: ' + util.inspect(err)); 21 | res.json(null); 22 | } else { 23 | console.log('*** getCustomers ok'); 24 | res.json(data.customers); 25 | } 26 | }); 27 | } 28 | 29 | getCustomersPage(req, res) { 30 | console.log('*** getCustomersPage'); 31 | const topVal = req.params.top, 32 | skipVal = req.params.skip, 33 | top = (isNaN(topVal)) ? 10 : +topVal, 34 | skip = (isNaN(skipVal)) ? 0 : +skipVal; 35 | 36 | customersRepo.getPagedCustomers(skip, top, (err, data) => { 37 | if (err) { 38 | console.log('*** getCustomersPage error: ' + util.inspect(err)); 39 | res.json(null); 40 | } else { 41 | res.setHeader('X-InlineCount', data.count); 42 | console.log('*** getCustomersPage ok'); 43 | res.json(data.customers); 44 | } 45 | }); 46 | } 47 | 48 | getCustomer(req, res) { 49 | console.log('*** getCustomer'); 50 | const id = req.params.id; 51 | console.log(id); 52 | 53 | customersRepo.getCustomer(id, (err, customer) => { 54 | if (err) { 55 | console.log('*** getCustomer error: ' + util.inspect(err)); 56 | res.json(null); 57 | } else { 58 | console.log('*** getCustomer ok'); 59 | res.json(customer); 60 | } 61 | }); 62 | } 63 | 64 | insertCustomer(req, res) { 65 | console.log('*** insertCustomer'); 66 | statesRepo.getState(req.body.stateId, (err, state) => { 67 | if (err) { 68 | console.log('*** statesRepo.getState error: ' + util.inspect(err)); 69 | res.json({ status: false, error: 'State not found', customer: null }); 70 | } else { 71 | customersRepo.insertCustomer(req.body, state, (err, customer) => { 72 | if (err) { 73 | console.log('*** customersRepo.insertCustomer error: ' + util.inspect(err)); 74 | res.json({status: false, error: 'Insert failed', customer: null}); 75 | } else { 76 | console.log('*** insertCustomer ok'); 77 | res.json({ status: true, error: null, customer: customer }); 78 | } 79 | }); 80 | } 81 | }); 82 | } 83 | 84 | updateCustomer(req, res) { 85 | console.log('*** updateCustomer'); 86 | console.log('*** req.body'); 87 | console.log(req.body); 88 | 89 | if (!req.body || !req.body.stateId) { 90 | throw new Error('Customer and associated stateId required'); 91 | } 92 | 93 | statesRepo.getState(req.body.stateId, (err, state) => { 94 | if (err) { 95 | console.log('*** statesRepo.getState error: ' + util.inspect(err)); 96 | res.json({ status: false, error: 'State not found', customer: null }); 97 | } else { 98 | customersRepo.updateCustomer(req.params.id, req.body, state, (err, customer) => { 99 | if (err) { 100 | console.log('*** updateCustomer error: ' + util.inspect(err)); 101 | res.json({ status: false, error: 'Update failed', customer: null }); 102 | } else { 103 | console.log('*** updateCustomer ok'); 104 | res.json({ status: true, error: null, customer: customer }); 105 | } 106 | }); 107 | } 108 | }); 109 | } 110 | 111 | deleteCustomer(req, res) { 112 | console.log('*** deleteCustomer'); 113 | 114 | customersRepo.deleteCustomer(req.params.id, (err) => { 115 | if (err) { 116 | console.log('*** deleteCustomer error: ' + util.inspect(err)); 117 | res.json({ status: false }); 118 | } else { 119 | console.log('*** deleteCustomer ok'); 120 | res.json({ status: true }); 121 | } 122 | }); 123 | } 124 | 125 | } 126 | 127 | module.exports = CustomersController; -------------------------------------------------------------------------------- /microservices/node/controllers/api/states/states.controller.js: -------------------------------------------------------------------------------- 1 | const statesRepo = require('../../../lib/statesRepository'), 2 | util = require('util'); 3 | 4 | class StatesController { 5 | 6 | constructor(router) { 7 | router.get('/', this.getStates.bind(this)); 8 | } 9 | 10 | getStates(req, res) { 11 | console.log('*** getStates'); 12 | 13 | statesRepo.getStates((err, data) => { 14 | if (err) { 15 | console.log('*** getStates error: ' + util.inspect(err)); 16 | res.json({ 17 | states: null 18 | }); 19 | } else { 20 | console.log('*** getStates ok'); 21 | res.json(data); 22 | } 23 | }); 24 | } 25 | 26 | } 27 | 28 | module.exports = StatesController; -------------------------------------------------------------------------------- /microservices/node/lib/configLoader.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | if (!process.env.NODE_ENV) process.env.NODE_ENV = 'development'; 4 | 5 | const env = process.env.NODE_ENV; 6 | 7 | console.log(`Node environment: ${env}`); 8 | console.log(`loading config.${env}.json`); 9 | 10 | module.exports = require(`../config/config.${env}.json`); -------------------------------------------------------------------------------- /microservices/node/lib/customersRepository.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'), 2 | Schema = mongoose.Schema, 3 | Customer = require('../models/customer'); 4 | 5 | class CustomersRepository { 6 | 7 | // get all the customers 8 | getCustomers(callback) { 9 | console.log('*** CustomersRepository.getCustomers'); 10 | Customer.count((err, custsCount) => { 11 | var count = custsCount; 12 | console.log(`Customers count: ${count}`); 13 | 14 | Customer.find({}, (err, customers) => { 15 | if (err) { 16 | console.log(`*** CustomersRepository.getCustomers error: ${err}`); 17 | return callback(err); 18 | } 19 | callback(null, { 20 | count: count, 21 | customers: customers 22 | }); 23 | }); 24 | 25 | }); 26 | } 27 | 28 | getPagedCustomers(skip, top, callback) { 29 | console.log('*** CustomersRepository.getPagedCustomers'); 30 | Customer.count((err, custsCount) => { 31 | var count = custsCount; 32 | console.log(`Skip: ${skip} Top: ${top}`); 33 | console.log(`Customers count: ${count}`); 34 | 35 | Customer.find({}) 36 | .sort({lastName: 1}) 37 | .skip(skip) 38 | .limit(top) 39 | .exec((err, customers) => { 40 | if (err) { 41 | console.log(`*** CustomersRepository.getPagedCustomers error: ${err}`); 42 | return callback(err); 43 | } 44 | callback(null, { 45 | count: count, 46 | customers: customers 47 | }); 48 | }); 49 | 50 | }); 51 | } 52 | 53 | // get the customer summary 54 | getCustomersSummary(skip, top, callback) { 55 | console.log('*** CustomersRepository.getCustomersSummary'); 56 | Customer.count((err, custsCount) => { 57 | var count = custsCount; 58 | console.log(`Customers count: ${count}`); 59 | 60 | Customer.find({}, { '_id': 0, 'firstName': 1, 'lastName': 1, 'city': 1, 'state': 1, 'orderCount': 1, 'gender': 1 }) 61 | .skip(skip) 62 | .limit(top) 63 | .exec((err, customersSummary) => { 64 | callback(null, { 65 | count: count, 66 | customersSummary: customersSummary 67 | }); 68 | }); 69 | 70 | }); 71 | } 72 | 73 | // get a customer 74 | getCustomer(id, callback) { 75 | console.log('*** CustomersRepository.getCustomer'); 76 | Customer.findById(id, (err, customer) => { 77 | if (err) { 78 | console.log(`*** CustomersRepository.getCustomer error: ${err}`); 79 | return callback(err); 80 | } 81 | callback(null, customer); 82 | }); 83 | } 84 | 85 | // insert a customer 86 | insertCustomer(body, state, callback) { 87 | console.log('*** CustomersRepository.insertCustomer'); 88 | console.log(state); 89 | var customer = new Customer(); 90 | var newState = { 'id': state[0].id, 'abbreviation': state[0].abbreviation, 'name': state[0].name } 91 | console.log(body); 92 | 93 | customer.firstName = body.firstName; 94 | customer.lastName = body.lastName; 95 | customer.email = body.email; 96 | customer.address = body.address; 97 | customer.city = body.city; 98 | customer.state = newState; 99 | customer.stateId = newState.id; 100 | customer.zip = body.zip; 101 | customer.gender = body.gender; 102 | 103 | customer.save((err, customer) => { 104 | if (err) { 105 | console.log(`*** CustomersRepository insertCustomer error: ${err}`); 106 | return callback(err, null); 107 | } 108 | 109 | callback(null, customer); 110 | }); 111 | } 112 | 113 | updateCustomer(id, body, state, callback) { 114 | console.log('*** CustomersRepository.editCustomer'); 115 | 116 | var state = { 'id': state[0].id, 'abbreviation': state[0].abbreviation, 'name': state[0].name } 117 | 118 | Customer.findById(id, (err, customer) => { 119 | if (err) { 120 | console.log(`*** CustomersRepository.editCustomer error: ${err}`); 121 | return callback(err); 122 | } 123 | 124 | customer.firstName = body.firstName || customer.firstName; 125 | customer.lastName = body.lastName || customer.lastName; 126 | customer.email = body.email || customer.email; 127 | customer.address = body.address || customer.address; 128 | customer.city = body.city || customer.city; 129 | customer.state = state; 130 | customer.stateId = state.id; 131 | customer.zip = body.zip || customer.zip; 132 | customer.gender = body.gender || customer.gender; 133 | 134 | 135 | customer.save((err, customer) => { 136 | if (err) { 137 | console.log(`*** CustomersRepository.updateCustomer error: ${err}`); 138 | return callback(err, null); 139 | } 140 | 141 | callback(null, customer); 142 | }); 143 | 144 | }); 145 | } 146 | 147 | // delete a customer 148 | deleteCustomer(id, callback) { 149 | console.log('*** CustomersRepository.deleteCustomer'); 150 | Customer.remove({ '_id': id }, (err, customer) => { 151 | if (err) { 152 | console.log(`*** CustomersRepository.deleteCustomer error: ${err}`); 153 | return callback(err, null); 154 | } 155 | callback(null, customer); 156 | }); 157 | } 158 | 159 | } 160 | 161 | module.exports = new CustomersRepository(); -------------------------------------------------------------------------------- /microservices/node/lib/database.js: -------------------------------------------------------------------------------- 1 | // Module dependencies 2 | const mongoose = require('mongoose'), 3 | dbConfig = require('./configLoader').databaseConfig, 4 | connectionString = 'mongodb://' + dbConfig.host + '/' + dbConfig.database; 5 | 6 | let connection = null; 7 | 8 | class Database { 9 | 10 | open(callback) { 11 | var options = { 12 | useMongoClient: true, 13 | promiseLibrary: global.Promise 14 | }; 15 | mongoose.connect(connectionString, options, (err) => { 16 | if (err) { 17 | console.log('mongoose.connect() failed: ' + err); 18 | } 19 | }); 20 | connection = mongoose.connection; 21 | mongoose.Promise = global.Promise; 22 | 23 | mongoose.connection.on('error', (err) => { 24 | console.log('Error connecting to MongoDB: ' + err); 25 | callback(err, false); 26 | }); 27 | 28 | mongoose.connection.once('open', () => { 29 | console.log('We have connected to mongodb'); 30 | callback(null, true); 31 | }); 32 | 33 | } 34 | 35 | // disconnect from database 36 | close() { 37 | connection.close(() => { 38 | console.log('Mongoose default connection disconnected through app termination'); 39 | process.exit(0); 40 | }); 41 | } 42 | 43 | } 44 | 45 | module.exports = new Database(); 46 | -------------------------------------------------------------------------------- /microservices/node/lib/dbSeeder.js: -------------------------------------------------------------------------------- 1 | // Module dependencies 2 | const mongoose = require('mongoose'), 3 | Customer = require('../models/customer'), 4 | State = require('../models/state'), 5 | dbConfig = require('./configLoader').databaseConfig, 6 | connectionString = `mongodb://${dbConfig.host}/${dbConfig.database}`, 7 | connection = null; 8 | 9 | class DBSeeder { 10 | 11 | init() { 12 | mongoose.connection.db.listCollections({name: 'customers'}) 13 | .next((err, collinfo) => { 14 | if (!collinfo) { 15 | console.log('Starting dbSeeder...'); 16 | this.seed(); 17 | } 18 | }); 19 | } 20 | 21 | seed() { 22 | 23 | console.log('Seeding data....'); 24 | 25 | //Customers 26 | var customerNames = 27 | [ 28 | "Marcus,HighTower,Male,acmecorp.com", 29 | "Jesse,Smith,Female,gmail.com", 30 | "Albert,Einstein,Male,outlook.com", 31 | "Dan,Wahlin,Male,yahoo.com", 32 | "Ward,Bell,Male,gmail.com", 33 | "Brad,Green,Male,gmail.com", 34 | "Igor,Minar,Male,gmail.com", 35 | "Miško,Hevery,Male,gmail.com", 36 | "Michelle,Avery,Female,acmecorp.com", 37 | "Heedy,Wahlin,Female,hotmail.com", 38 | "Thomas,Martin,Male,outlook.com", 39 | "Jean,Martin,Female,outlook.com", 40 | "Robin,Cleark,Female,acmecorp.com", 41 | "Juan,Paulo,Male,yahoo.com", 42 | "Gene,Thomas,Male,gmail.com", 43 | "Pinal,Dave,Male,gmail.com", 44 | "Fred,Roberts,Male,outlook.com", 45 | "Tina,Roberts,Female,outlook.com", 46 | "Cindy,Jamison,Female,gmail.com", 47 | "Robyn,Flores,Female,yahoo.com", 48 | "Jeff,Wahlin,Male,gmail.com", 49 | "Danny,Wahlin,Male,gmail.com", 50 | "Elaine,Jones,Female,yahoo.com", 51 | "John,Papa,Male,gmail.com" 52 | ]; 53 | var addresses = 54 | [ 55 | "1234 Anywhere St.", 56 | "435 Main St.", 57 | "1 Atomic St.", 58 | "85 Cedar Dr.", 59 | "12 Ocean View St.", 60 | "1600 Amphitheatre Parkway", 61 | "1604 Amphitheatre Parkway", 62 | "1607 Amphitheatre Parkway", 63 | "346 Cedar Ave.", 64 | "4576 Main St.", 65 | "964 Point St.", 66 | "98756 Center St.", 67 | "35632 Richmond Circle Apt B", 68 | "2352 Angular Way", 69 | "23566 Directive Pl.", 70 | "235235 Yaz Blvd.", 71 | "7656 Crescent St.", 72 | "76543 Moon Ave.", 73 | "84533 Hardrock St.", 74 | "5687534 Jefferson Way", 75 | "346346 Blue Pl.", 76 | "23423 Adams St.", 77 | "633 Main St.", 78 | "899 Mickey Way" 79 | ]; 80 | 81 | var citiesStates = 82 | [ 83 | "Phoenix,AZ,Arizona", 84 | "Encinitas,CA,California", 85 | "Seattle,WA,Washington", 86 | "Chandler,AZ,Arizona", 87 | "Dallas,TX,Texas", 88 | "Orlando,FL,Florida", 89 | "Carey,NC,North Carolina", 90 | "Anaheim,CA,California", 91 | "Dallas,TX,Texas", 92 | "New York,NY,New York", 93 | "White Plains,NY,New York", 94 | "Las Vegas,NV,Nevada", 95 | "Los Angeles,CA,California", 96 | "Portland,OR,Oregon", 97 | "Seattle,WA,Washington", 98 | "Houston,TX,Texas", 99 | "Chicago,IL,Illinois", 100 | "Atlanta,GA,Georgia", 101 | "Chandler,AZ,Arizona", 102 | "Buffalo,NY,New York", 103 | "Albuquerque,AZ,Arizona", 104 | "Boise,ID,Idaho", 105 | "Salt Lake City,UT,Utah", 106 | "Orlando,FL,Florida" 107 | ]; 108 | 109 | var citiesIds = [5, 9, 44, 5, 36, 17, 16, 9, 36, 14, 14, 6, 9, 24, 44, 36, 25, 19, 5, 14, 5, 23, 38, 17]; 110 | 111 | 112 | var zip = 85229; 113 | 114 | var orders = 115 | [ 116 | { "product": "Basket", "price": 29.99, "quantity": 1 }, 117 | { "product": "Yarn", "price": 9.99, "quantity": 1 }, 118 | { "product": "Needes", "price": 5.99, "quantity": 1 }, 119 | { "product": "Speakers", "price": 499.99, "quantity": 1 }, 120 | { "product": "iPod", "price": 399.99, "quantity": 1 }, 121 | { "product": "Table", "price": 329.99, "quantity": 1 }, 122 | { "product": "Chair", "price": 129.99, "quantity": 4 }, 123 | { "product": "Lamp", "price": 89.99, "quantity": 5 }, 124 | { "product": "Call of Duty", "price": 59.99, "quantity": 1 }, 125 | { "product": "Controller", "price": 49.99, "quantity": 1 }, 126 | { "product": "Gears of War", "price": 49.99, "quantity": 1 }, 127 | { "product": "Lego City", "price": 49.99, "quantity": 1 }, 128 | { "product": "Baseball", "price": 9.99, "quantity": 5 }, 129 | { "product": "Bat", "price": 19.99, "quantity": 1 } 130 | ]; 131 | 132 | Customer.remove({}); 133 | 134 | var l = customerNames.length, 135 | i, 136 | j, 137 | firstOrder, 138 | lastOrder, 139 | tempOrder, 140 | n = orders.length; 141 | 142 | for (i = 0; i < l; i++) { 143 | var nameGenderHost = customerNames[i].split(','); 144 | var cityState = citiesStates[i].split(','); 145 | var state = { 'id': citiesIds[i], 'abbreviation': cityState[1], 'name': cityState[2] }; 146 | var customer = new Customer({ 147 | 'firstName': nameGenderHost[0], 148 | 'lastName': nameGenderHost[1], 149 | 'email': nameGenderHost[0] + '.' + nameGenderHost[1] + '@' + nameGenderHost[3], 150 | 'address': addresses[i], 151 | 'city': cityState[0], 152 | 'state': state, 153 | 'stateId': citiesIds[i], 154 | 'zip': zip + i, 155 | 'gender': nameGenderHost[2], 156 | 'orderCount': 0 157 | }); 158 | firstOrder = Math.floor(Math.random() * orders.length); 159 | lastOrder = Math.floor(Math.random() * orders.length); 160 | if (firstOrder > lastOrder) { 161 | tempOrder = firstOrder; 162 | firstOrder = lastOrder; 163 | lastOrder = tempOrder; 164 | } 165 | 166 | customer.orders = []; 167 | //console.log('firstOrder: ' + firstOrder + ", lastOrder: " + lastOrder); 168 | for (j = firstOrder; j <= lastOrder && j < n; j++) { 169 | var today = new Date(); 170 | var tomorrow = new Date(); 171 | tomorrow.setDate(today.getDate() + (Math.random() * 100)); 172 | 173 | var o = { 174 | "product": orders[j].product, 175 | "price": orders[j].price, 176 | "quantity": orders[j].quantity, 177 | "date": tomorrow 178 | }; 179 | customer.orders.push(o); 180 | } 181 | customer.orderCount = customer.orders.length; 182 | 183 | customer.save((err, cust) => { 184 | if (err) { 185 | console.log(err); 186 | } else { 187 | console.log('inserted customer: ' + cust.firstName + ' ' + cust.lastName); 188 | } 189 | }); 190 | } 191 | 192 | //States 193 | var states = [ 194 | { "name": "Alabama", "abbreviation": "AL" }, 195 | { "name": "Montana", "abbreviation": "MT" }, 196 | { "name": "Alaska", "abbreviation": "AK" }, 197 | { "name": "Nebraska", "abbreviation": "NE" }, 198 | { "name": "Arizona", "abbreviation": "AZ" }, 199 | { "name": "Nevada", "abbreviation": "NV" }, 200 | { "name": "Arkansas", "abbreviation": "AR" }, 201 | { "name": "New Hampshire", "abbreviation": "NH" }, 202 | { "name": "California", "abbreviation": "CA" }, 203 | { "name": "New Jersey", "abbreviation": "NJ" }, 204 | { "name": "Colorado", "abbreviation": "CO" }, 205 | { "name": "New Mexico", "abbreviation": "NM" }, 206 | { "name": "Connecticut", "abbreviation": "CT" }, 207 | { "name": "New York", "abbreviation": "NY" }, 208 | { "name": "Delaware", "abbreviation": "DE" }, 209 | { "name": "North Carolina", "abbreviation": "NC" }, 210 | { "name": "Florida", "abbreviation": "FL" }, 211 | { "name": "North Dakota", "abbreviation": "ND" }, 212 | { "name": "Georgia", "abbreviation": "GA" }, 213 | { "name": "Ohio", "abbreviation": "OH" }, 214 | { "name": "Hawaii", "abbreviation": "HI" }, 215 | { "name": "Oklahoma", "abbreviation": "OK" }, 216 | { "name": "Idaho", "abbreviation": "ID" }, 217 | { "name": "Oregon", "abbreviation": "OR" }, 218 | { "name": "Illinois", "abbreviation": "IL" }, 219 | { "name": "Pennsylvania", "abbreviation": "PA" }, 220 | { "name": "Indiana", "abbreviation": "IN" }, 221 | { "name": "Rhode Island", "abbreviation": "RI" }, 222 | { "name": "Iowa", "abbreviation": "IA" }, 223 | { "name": "South Carolina", "abbreviation": "SC" }, 224 | { "name": "Kansas", "abbreviation": "KS" }, 225 | { "name": "South Dakota", "abbreviation": "SD" }, 226 | { "name": "Kentucky", "abbreviation": "KY" }, 227 | { "name": "Tennessee", "abbreviation": "TN" }, 228 | { "name": "Louisiana", "abbreviation": "LA" }, 229 | { "name": "Texas", "abbreviation": "TX" }, 230 | { "name": "Maine", "abbreviation": "ME" }, 231 | { "name": "Utah", "abbreviation": "UT" }, 232 | { "name": "Maryland", "abbreviation": "MD" }, 233 | { "name": "Vermont", "abbreviation": "VT" }, 234 | { "name": "Massachusetts", "abbreviation": "MA" }, 235 | { "name": "Virginia", "abbreviation": "VA" }, 236 | { "name": "Michigan", "abbreviation": "MI" }, 237 | { "name": "Washington", "abbreviation": "WA" }, 238 | { "name": "Minnesota", "abbreviation": "MN" }, 239 | { "name": "West Virginia", "abbreviation": "WV" }, 240 | { "name": "Mississippi", "abbreviation": "MS" }, 241 | { "name": "Wisconsin", "abbreviation": "WI" }, 242 | { "name": "Missouri", "abbreviation": "MO" }, 243 | { "name": "Wyoming", "abbreviation": "WY" } 244 | ]; 245 | 246 | var l = states.length, 247 | i; 248 | 249 | State.remove({}); 250 | 251 | for (i = 0; i < l; i++) { 252 | var state = new State ({ 'id': i + 1, 'name': states[i].name, 'abbreviation': states[i].abbreviation }); 253 | state.save(); 254 | } 255 | } 256 | } 257 | 258 | module.exports = new DBSeeder(); 259 | 260 | 261 | 262 | 263 | -------------------------------------------------------------------------------- /microservices/node/lib/statesRepository.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'), 2 | Schema = mongoose.Schema, 3 | State = require('../models/state'); 4 | 5 | class StatesRepository { 6 | // get all the states 7 | getStates(callback) { 8 | console.log('*** StatesRepository.getStates'); 9 | State.find({}, {}, { sort: { name: 1 } }, (err, states) => { 10 | if (err) { 11 | console.log(`*** StatesRepository.getStates err: ${err}`); 12 | return callback(err); 13 | } 14 | callback(null, states); 15 | }); 16 | } 17 | 18 | // get a state 19 | getState(stateId, callback) { 20 | console.log('*** StatesRepository.getState'); 21 | State.find({ 'id': stateId }, {}, (err, state) => { 22 | if (err) { 23 | console.log(`*** StatesRepository.getState err: ${err}`); 24 | return callback(err); 25 | } 26 | callback(null, state); 27 | }); 28 | } 29 | } 30 | 31 | module.exports = new StatesRepository(); 32 | 33 | -------------------------------------------------------------------------------- /microservices/node/models/customer.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'), 2 | Schema = mongoose.Schema, 3 | State = require('./state'); 4 | 5 | //console.log(State); 6 | const OrderSchema = new Schema({ 7 | product : { type : String, required: true, trim: true }, 8 | price : { type : Number }, 9 | quantity : { type : Number } 10 | }); 11 | 12 | const CustomerSchema = new Schema({ 13 | firstName : { type : String, required: true, trim: true }, 14 | lastName : { type : String, required: true, trim: true }, 15 | email : { type : String, required: true, trim: true }, 16 | address : { type : String, required: true, trim: true }, 17 | city : { type : String, required: true, trim: true }, 18 | stateId : { type : Number, required: true }, 19 | state : State.schema , 20 | zip : { type : Number, required: true }, 21 | gender : { type : String }, 22 | orderCount : { type : Number }, 23 | orders : [ OrderSchema ], 24 | }); 25 | 26 | module.exports = mongoose.model('Customer', CustomerSchema, 'customers'); 27 | -------------------------------------------------------------------------------- /microservices/node/models/state.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'), 2 | Schema = mongoose.Schema, 3 | ObjectId = Schema.ObjectId; 4 | 5 | const StateSchema = new Schema({ 6 | id : { type : Number, required: true }, 7 | abbreviation : { type : String, required: true, trim: true }, 8 | name : { type : String, required: true, trim: true } 9 | }); 10 | 11 | module.exports = mongoose.model('State', StateSchema); 12 | 13 | -------------------------------------------------------------------------------- /microservices/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodejs-mongodb-customersservice", 3 | "version": "1.0.0", 4 | "repository": { 5 | "url": "https://github.com/DanWahlin/Angular-NodeJS-MongoDB-CustomersService" 6 | }, 7 | "license": "MIT", 8 | "scripts": {}, 9 | "dependencies": { 10 | "cookie-parser": "^1.4.3", 11 | "body-parser": "^1.18.2", 12 | "csurf": "^1.9.0", 13 | "errorhandler": "^1.5.0", 14 | "express": "^4.16.3", 15 | "express-session": "^1.15.6", 16 | "mongoose": "^5.0.11", 17 | "morgan": "~1.9.0", 18 | "serve-favicon": "~2.4.5", 19 | "express-convention-routes": "^1.0.5" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /microservices/node/routes/router.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'), 2 | path = require('path'), 3 | express = require('express'); 4 | 5 | class Router { 6 | 7 | constructor() { 8 | this.startFolder = null; 9 | } 10 | 11 | //Called once during initial server startup 12 | load(app, folderName) { 13 | 14 | if (!this.startFolder) this.startFolder = path.basename(folderName); 15 | 16 | fs.readdirSync(folderName).forEach((file) => { 17 | 18 | const fullName = path.join(folderName, file); 19 | const stat = fs.lstatSync(fullName); 20 | 21 | if (stat.isDirectory()) { 22 | //Recursively walk-through folders 23 | this.load(app, fullName); 24 | } else if (file.toLowerCase().indexOf('.js')) { 25 | //Grab path to JavaScript file and use it to construct the route 26 | let dirs = path.dirname(fullName).split(path.sep); 27 | 28 | if (dirs[0].toLowerCase() === this.startFolder.toLowerCase()) { 29 | dirs.splice(0, 1); 30 | } 31 | 32 | const router = express.Router(); 33 | //Generate the route 34 | const baseRoute = '/' + dirs.join('/'); 35 | console.log('Created route: ' + baseRoute + ' for ' + fullName); 36 | 37 | //Load the JavaScript file ("controller") and pass the router to it 38 | const controllerClass = require('../' + fullName); 39 | const controller = new controllerClass(router); 40 | //Associate the route with the router 41 | app.use(baseRoute, router); 42 | } 43 | }); 44 | } 45 | 46 | } 47 | 48 | module.exports = new Router(); 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /microservices/node/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'), 2 | bodyParser = require('body-parser'), 3 | cookieParser = require('cookie-parser'), 4 | errorhandler = require('errorhandler'), 5 | csrf = require('csurf'), 6 | morgan = require('morgan'), 7 | favicon = require('serve-favicon'), 8 | 9 | database = require('./lib/database'), 10 | seeder = require('./lib/dbSeeder'), 11 | app = express(), 12 | router = require('express-convention-routes'), 13 | port = 3000; 14 | 15 | class Server { 16 | 17 | constructor() { 18 | this.initExpressMiddleWare(); 19 | this.initCustomMiddleware(); 20 | this.initDbSeeder(); 21 | this.initRoutes(); 22 | this.start(); 23 | } 24 | 25 | start() { 26 | app.listen(port, (err) => { 27 | console.log('[%s] Listening on http://localhost:%d', process.env.NODE_ENV, port); 28 | }); 29 | } 30 | 31 | initExpressMiddleWare() { 32 | app.use(morgan('dev')); 33 | app.use(bodyParser.urlencoded({ extended: true })); 34 | app.use(bodyParser.json()); 35 | app.use(errorhandler()); 36 | app.use(cookieParser()); 37 | //app.use(csrf({ cookie: true })); 38 | 39 | // app.use((req, res, next) => { 40 | // var csrfToken = req.csrfToken(); 41 | // res.locals._csrf = csrfToken; 42 | // res.cookie('XSRF-TOKEN', csrfToken); 43 | // next(); 44 | // }); 45 | 46 | //CORS 47 | app.use(function(req, res, next) { 48 | res.header("Access-Control-Allow-Origin", "*"); 49 | res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, X-XSRF-TOKEN, Content-Type, Accept"); 50 | res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,PATCH'); 51 | next(); 52 | }); 53 | 54 | process.on('uncaughtException', (err) => { 55 | if (err) console.log(err, err.stack); 56 | }); 57 | } 58 | 59 | initCustomMiddleware() { 60 | if (process.platform === "win32") { 61 | require("readline").createInterface({ 62 | input: process.stdin, 63 | output: process.stdout 64 | }).on("SIGINT", () => { 65 | console.log('SIGINT: Closing MongoDB connection'); 66 | database.close(); 67 | }); 68 | } 69 | 70 | process.on('SIGINT', () => { 71 | console.log('SIGINT: Closing MongoDB connection'); 72 | database.close(); 73 | }); 74 | } 75 | 76 | initDbSeeder() { 77 | database.open(() => { 78 | //Set NODE_ENV to 'development' and uncomment the following if to only run 79 | //the seeder when in dev mode 80 | //if (process.env.NODE_ENV === 'development') { 81 | // seeder.init(); 82 | //} 83 | seeder.init(); 84 | }); 85 | } 86 | 87 | initRoutes() { 88 | router.load(app, { 89 | //Defaults to "./controllers" but showing for example 90 | routesDirectory: './controllers', 91 | 92 | //Root directory where your server is running 93 | rootDirectory: __dirname, 94 | 95 | //Do you want the created routes to be shown in the console? 96 | logRoutes: true 97 | }); 98 | } 99 | 100 | } 101 | 102 | var server = new Server(); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-project", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "ng": "ng", 7 | "start": "ng serve", 8 | "build": "ng build", 9 | "test": "ng test", 10 | "lint": "ng lint", 11 | "e2e": "ng e2e" 12 | }, 13 | "private": true, 14 | "dependencies": { 15 | "@angular/animations": "^6.0.0", 16 | "@angular/common": "^6.0.0", 17 | "@angular/compiler": "^6.0.0", 18 | "@angular/core": "^6.0.0", 19 | "@angular/forms": "^6.0.0", 20 | "@angular/http": "^6.0.0", 21 | "@angular/platform-browser": "^6.0.0", 22 | "@angular/platform-browser-dynamic": "^6.0.0", 23 | "@angular/router": "^6.0.0", 24 | "core-js": "^2.4.1", 25 | "rxjs": "^6.1.0", 26 | "zone.js": "^0.8.26" 27 | }, 28 | "devDependencies": { 29 | "@angular/cli": "^6.0.0", 30 | "@angular/compiler-cli": "^6.0.0", 31 | "@angular/language-service": "^6.0.0", 32 | "@types/jasmine": "~2.8.3", 33 | "@types/jasminewd2": "~2.0.2", 34 | "@types/node": "~6.0.60", 35 | "codelyzer": "^4.0.1", 36 | "jasmine-core": "~2.8.0", 37 | "jasmine-spec-reporter": "~4.2.1", 38 | "karma": "~2.0.0", 39 | "karma-chrome-launcher": "~2.2.0", 40 | "karma-coverage-istanbul-reporter": "^1.2.1", 41 | "karma-jasmine": "~1.1.0", 42 | "karma-jasmine-html-reporter": "^0.2.2", 43 | "protractor": "~5.1.2", 44 | "ts-node": "~4.1.0", 45 | "tslint": "~5.9.1", 46 | "typescript": "2.7.2", 47 | "@angular-devkit/build-angular": "~0.6.0" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /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 | beforeLaunch: function() { 23 | require('ts-node').register({ 24 | project: 'e2e/tsconfig.e2e.json' 25 | }); 26 | }, 27 | onPrepare() { 28 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /solvedIssues.md: -------------------------------------------------------------------------------- 1 | ## Running ng build causes nginx container to 404 2 | 3 | When you run ng build it is re-creating the dist folder which 4 | then breaks your volume if doing something like: 5 | 6 | docker run -d -p 8080:80 -v $(pwd):/usr/share/nginx/html nginx:alpine 7 | 8 | It's OK to update the content but NOT blow away the folder 9 | due to the volume that's created above. -------------------------------------------------------------------------------- /src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | 4 | import { CustomersComponent } from './customers/customers.component'; 5 | import { CustomerEditComponent } from './customers/customer-edit.component'; 6 | import { CustomersGridComponent } from './customers/customers-grid.component'; 7 | 8 | import { SharedModule } from 'app/shared/shared.module'; 9 | 10 | const routes: Routes = [ 11 | { path: 'customers', component: CustomersComponent}, 12 | { path: 'customers/:id', component: CustomerEditComponent }, 13 | { path: '**', pathMatch:'full', redirectTo: '/customers' } //catch any unfound routes and redirect to home page 14 | ]; 15 | 16 | @NgModule({ 17 | imports: [ SharedModule, RouterModule.forRoot(routes) ], 18 | exports: [ RouterModule, CustomersGridComponent, CustomersComponent, CustomerEditComponent ], 19 | declarations: [ CustomersGridComponent, CustomersComponent, CustomerEditComponent ] 20 | }) 21 | export class AppRoutingModule { 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/app/app.component.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | Object.defineProperty(exports, "__esModule", { value: true }); 9 | var core_1 = require("@angular/core"); 10 | var AppComponent = (function () { 11 | function AppComponent() { 12 | } 13 | return AppComponent; 14 | }()); 15 | AppComponent = __decorate([ 16 | core_1.Component({ 17 | moduleId: module.id, 18 | selector: 'app-container', 19 | template: "" 20 | }) 21 | ], AppComponent); 22 | exports.AppComponent = AppComponent; 23 | //# sourceMappingURL=app.component.js.map -------------------------------------------------------------------------------- /src/app/app.component.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"app.component.js","sourceRoot":"","sources":["app.component.ts"],"names":[],"mappings":";;;;;;;;AAAA,sCAA0C;AAO1C,IAAa,YAAY;IAAzB;IAEA,CAAC;IAAD,mBAAC;AAAD,CAAC,AAFD,IAEC;AAFY,YAAY;IALxB,gBAAS,CAAC;QACT,QAAQ,EAAE,MAAM,CAAC,EAAE;QACnB,QAAQ,EAAE,eAAe;QACzB,QAAQ,EAAE,iCAAiC;KAC5C,CAAC;GACW,YAAY,CAExB;AAFY,oCAAY"} -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | moduleId: module.id, 5 | selector: 'app-container', 6 | template: `` 7 | }) 8 | export class AppComponent { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/app/app.module.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | Object.defineProperty(exports, "__esModule", { value: true }); 9 | var core_1 = require("@angular/core"); 10 | var platform_browser_1 = require("@angular/platform-browser"); 11 | var app_component_1 = require("./app.component"); 12 | var app_routing_1 = require("./app.routing"); 13 | var core_module_1 = require("./core/core.module"); 14 | var shared_module_1 = require("./shared/shared.module"); 15 | var AppModule = (function () { 16 | function AppModule() { 17 | } 18 | return AppModule; 19 | }()); 20 | AppModule = __decorate([ 21 | core_1.NgModule({ 22 | imports: [ 23 | platform_browser_1.BrowserModule, 24 | app_routing_1.appRouting.routes, 25 | core_module_1.CoreModule, 26 | shared_module_1.SharedModule //Shared (multi-instance) objects 27 | ], 28 | declarations: [app_component_1.AppComponent, app_routing_1.appRouting.components], 29 | bootstrap: [app_component_1.AppComponent] 30 | }) 31 | ], AppModule); 32 | exports.AppModule = AppModule; 33 | //# sourceMappingURL=app.module.js.map -------------------------------------------------------------------------------- /src/app/app.module.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"app.module.js","sourceRoot":"","sources":["app.module.ts"],"names":[],"mappings":";;;;;;;;AAAA,sCAA8C;AAC9C,8DAA0D;AAE1D,iDAAgD;AAChD,6CAA2C;AAC3C,kDAAkD;AAClD,wDAAwD;AAYxD,IAAa,SAAS;IAAtB;IAAyB,CAAC;IAAD,gBAAC;AAAD,CAAC,AAA1B,IAA0B;AAAb,SAAS;IAVrB,eAAQ,CAAC;QACR,OAAO,EAAE;YACP,gCAAa;YACb,wBAAU,CAAC,MAAM;YACjB,wBAAU;YACV,4BAAY,CAAE,iCAAiC;SAChD;QACD,YAAY,EAAE,CAAE,4BAAY,EAAE,wBAAU,CAAC,UAAU,CAAE;QACrD,SAAS,EAAK,CAAE,4BAAY,CAAE;KAC/B,CAAC;GACW,SAAS,CAAI;AAAb,8BAAS"} -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | 4 | import { AppComponent } from './app.component'; 5 | import { AppRoutingModule } from './app-routing.module'; 6 | import { CoreModule } from './core/core.module'; 7 | import { SharedModule } from './shared/shared.module'; 8 | 9 | @NgModule({ 10 | imports: [ 11 | BrowserModule, 12 | CoreModule, //Singleton objects 13 | SharedModule, //Shared (multi-instance) objects 14 | AppRoutingModule 15 | ], 16 | declarations: [ AppComponent ], 17 | bootstrap: [ AppComponent ] 18 | }) 19 | export class AppModule { } -------------------------------------------------------------------------------- /src/app/app.routing.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var router_1 = require("@angular/router"); 4 | var customers_component_1 = require("./customers/customers.component"); 5 | var customers_grid_component_1 = require("./customers/customers-grid.component"); 6 | var customer_edit_component_1 = require("./customers/customer-edit.component"); 7 | var customer_edit_reactive_component_1 = require("./customers/customer-edit-reactive.component"); 8 | var routes = [ 9 | { path: 'customers', component: customers_component_1.CustomersComponent }, 10 | { path: 'customers/:id', component: customer_edit_component_1.CustomerEditComponent }, 11 | //{ path: 'customers/:id', component: CustomerEditReactiveComponent }, 12 | { path: '**', pathMatch: 'full', redirectTo: '/customers' } //catch any unfound routes and redirect to home page 13 | ]; 14 | exports.appRouting = { 15 | routes: router_1.RouterModule.forRoot(routes), 16 | components: [customers_component_1.CustomersComponent, customer_edit_component_1.CustomerEditComponent, customer_edit_reactive_component_1.CustomerEditReactiveComponent, customers_grid_component_1.CustomersGridComponent] 17 | }; 18 | //# sourceMappingURL=app.routing.js.map -------------------------------------------------------------------------------- /src/app/app.routing.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"app.routing.js","sourceRoot":"","sources":["app.routing.ts"],"names":[],"mappings":";;AAAA,0CAAuD;AAEvD,uEAAqE;AACrE,iFAA8E;AAC9E,+EAA4E;AAC5E,iGAA6F;AAG7F,IAAM,MAAM,GAAW;IACrB,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,wCAAkB,EAAC;IACnD,EAAE,IAAI,EAAE,eAAe,EAAE,SAAS,EAAE,+CAAqB,EAAC;IAC1D,sEAAsE;IACtE,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAC,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,oDAAoD;CAChH,CAAC;AAEW,QAAA,UAAU,GAAa;IAChC,MAAM,EAAE,qBAAY,CAAC,OAAO,CAAC,MAAM,CAAC;IACpC,UAAU,EAAE,CAAE,wCAAkB,EAAE,+CAAqB,EAAE,gEAA6B,EAAE,iDAAsB,CAAE;CACnH,CAAC"} -------------------------------------------------------------------------------- /src/app/core/core.module.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __extends = (this && this.__extends) || (function () { 3 | var extendStatics = Object.setPrototypeOf || 4 | ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 5 | function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 6 | return function (d, b) { 7 | extendStatics(d, b); 8 | function __() { this.constructor = d; } 9 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 10 | }; 11 | })(); 12 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 13 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 14 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 15 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 16 | return c > 3 && r && Object.defineProperty(target, key, r), r; 17 | }; 18 | var __metadata = (this && this.__metadata) || function (k, v) { 19 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 20 | }; 21 | var __param = (this && this.__param) || function (paramIndex, decorator) { 22 | return function (target, key) { decorator(target, key, paramIndex); } 23 | }; 24 | Object.defineProperty(exports, "__esModule", { value: true }); 25 | var core_1 = require("@angular/core"); 26 | var http_1 = require("@angular/http"); 27 | var data_service_1 = require("./data.service"); 28 | var data_filter_service_1 = require("./data-filter.service"); 29 | var sorter_1 = require("./sorter"); 30 | var trackby_service_1 = require("./trackby.service"); 31 | var ensureModuleLoadedOnceGuard_1 = require("../shared/ensureModuleLoadedOnceGuard"); 32 | var CoreModule = (function (_super) { 33 | __extends(CoreModule, _super); 34 | //Looks for the module in the parent injector to see if it's already been loaded (only want it loaded once) 35 | function CoreModule(parentModule) { 36 | return _super.call(this, parentModule) || this; 37 | } 38 | return CoreModule; 39 | }(ensureModuleLoadedOnceGuard_1.EnsureModuleLoadedOnceGuard)); 40 | CoreModule = __decorate([ 41 | core_1.NgModule({ 42 | imports: [http_1.HttpModule], 43 | providers: [ 44 | //Default XSRF provider setup (change cookie or header name if needed): 45 | //{ provide: XSRFStrategy, useValue: new CookieXSRFStrategy('XSRF-TOKEN', 'X-XSRF-TOKEN') }, 46 | data_service_1.DataService, data_filter_service_1.DataFilterService, sorter_1.Sorter, trackby_service_1.TrackByService 47 | ] // these should be singleton 48 | }), 49 | __param(0, core_1.Optional()), __param(0, core_1.SkipSelf()), 50 | __metadata("design:paramtypes", [CoreModule]) 51 | ], CoreModule); 52 | exports.CoreModule = CoreModule; 53 | //# sourceMappingURL=core.module.js.map -------------------------------------------------------------------------------- /src/app/core/core.module.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"core.module.js","sourceRoot":"","sources":["core.module.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAAA,sCAA6D;AAC7D,sCAA6E;AAE7E,+CAA6C;AAC7C,6DAA0D;AAC1D,mCAAkC;AAClC,qDAAmD;AACnD,qFAAoF;AASpF,IAAa,UAAU;IAAS,8BAA2B;IAEzD,2GAA2G;IAC3G,oBAAqC,YAAwB;eAC3D,kBAAM,YAAY,CAAC;IACrB,CAAC;IAEH,iBAAC;AAAD,CAAC,AAPD,CAAgC,yDAA2B,GAO1D;AAPY,UAAU;IAPtB,eAAQ,CAAC;QACR,OAAO,EAAE,CAAE,iBAAU,CAAE;QACvB,SAAS,EAAE;YACT,wEAAwE;YACxE,4FAA4F;YAC5F,0BAAW,EAAE,uCAAiB,EAAE,eAAM,EAAE,gCAAc;SAAC,CAAC,4BAA4B;KACvF,CAAC;IAIc,WAAA,eAAQ,EAAE,CAAA,EAAE,WAAA,eAAQ,EAAE,CAAA;qCAAe,UAAU;GAHlD,UAAU,CAOtB;AAPY,gCAAU"} -------------------------------------------------------------------------------- /src/app/core/core.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, Optional, SkipSelf } from '@angular/core'; 2 | import { HttpClientModule, HttpClientXsrfModule } from '@angular/common/http'; 3 | 4 | import { DataService } from './data.service'; 5 | import { DataFilterService } from './data-filter.service'; 6 | import { Sorter } from './sorter'; 7 | import { TrackByService } from './trackby.service'; 8 | import { EnsureModuleLoadedOnceGuard } from '../shared/ensureModuleLoadedOnceGuard'; 9 | 10 | @NgModule({ 11 | imports: [ 12 | HttpClientModule, 13 | //Can use to override default names for XSRF cookie and header 14 | // HttpClientXsrfModule.withOptions({ 15 | // cookieName: 'My-XSRF-TOKEN', 16 | // headerName: 'My-X-XSRF-TOKEN', 17 | // }) 18 | ], 19 | providers: [ 20 | //Default XSRF provider setup (change cookie or header name if needed): 21 | //{ provide: XSRFStrategy, useValue: new CookieXSRFStrategy('XSRF-TOKEN', 'X-XSRF-TOKEN') }, 22 | DataService, DataFilterService, Sorter, TrackByService] // these should be singleton 23 | }) 24 | export class CoreModule extends EnsureModuleLoadedOnceGuard { //Ensure that CoreModule is only loaded into AppModule 25 | 26 | //Looks for the module in the parent injector to see if it's already been loaded (only want it loaded once) 27 | constructor( @Optional() @SkipSelf() parentModule: CoreModule) { 28 | super(parentModule); 29 | } 30 | 31 | } 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/app/core/data-filter.service.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | Object.defineProperty(exports, "__esModule", { value: true }); 9 | var core_1 = require("@angular/core"); 10 | var property_resolver_1 = require("../shared/property-resolver"); 11 | var DataFilterService = (function () { 12 | function DataFilterService() { 13 | } 14 | DataFilterService.prototype.filter = function (datasource, filterProperties, filterData) { 15 | if (datasource && filterProperties && filterData) { 16 | filterData = filterData.toUpperCase(); 17 | var filtered = datasource.filter(function (item) { 18 | var match = false; 19 | for (var _i = 0, filterProperties_1 = filterProperties; _i < filterProperties_1.length; _i++) { 20 | var prop = filterProperties_1[_i]; 21 | var propVal = ''; 22 | //Account for nested properties like 'state.name' 23 | if (prop.indexOf('.') > -1) { 24 | propVal = property_resolver_1.propertyResolver.resolve(prop, item); 25 | if (propVal) { 26 | propVal = propVal.toString().toUpperCase(); 27 | } 28 | } 29 | else { 30 | if (item[prop]) { 31 | propVal = item[prop].toString().toUpperCase(); 32 | } 33 | } 34 | if (propVal.indexOf(filterData) > -1) { 35 | match = true; 36 | break; 37 | } 38 | } 39 | ; 40 | return match; 41 | }); 42 | return filtered; 43 | } 44 | else { 45 | return datasource; 46 | } 47 | }; 48 | return DataFilterService; 49 | }()); 50 | DataFilterService = __decorate([ 51 | core_1.Injectable() 52 | ], DataFilterService); 53 | exports.DataFilterService = DataFilterService; 54 | //# sourceMappingURL=data-filter.service.js.map -------------------------------------------------------------------------------- /src/app/core/data-filter.service.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"data-filter.service.js","sourceRoot":"","sources":["data-filter.service.ts"],"names":[],"mappings":";;;;;;;;AAAA,sCAA2C;AAC3C,iEAA+D;AAG/D,IAAa,iBAAiB;IAA9B;IAqCA,CAAC;IAnCG,kCAAM,GAAN,UAAO,UAAiB,EAAE,gBAA0B,EAAE,UAAkB;QACpE,EAAE,CAAC,CAAC,UAAU,IAAI,gBAAgB,IAAI,UAAU,CAAC,CAAC,CAAC;YAC/C,UAAU,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC;YACtC,IAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,CAAC,UAAA,IAAI;gBACnC,IAAI,KAAK,GAAG,KAAK,CAAC;gBAClB,GAAG,CAAC,CAAe,UAAgB,EAAhB,qCAAgB,EAAhB,8BAAgB,EAAhB,IAAgB;oBAA9B,IAAM,IAAI,yBAAA;oBACX,IAAI,OAAO,GAAQ,EAAE,CAAC;oBAEtB,iDAAiD;oBACjD,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;wBACzB,OAAO,GAAG,oCAAgB,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;wBAC/C,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;4BACV,OAAO,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE,CAAC;wBAC/C,CAAC;oBACL,CAAC;oBACD,IAAI,CAAC,CAAC;wBACF,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;4BACb,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE,CAAC;wBAClD,CAAC;oBACL,CAAC;oBAED,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;wBACnC,KAAK,GAAG,IAAI,CAAC;wBACb,KAAK,CAAC;oBACV,CAAC;iBACJ;gBAAA,CAAC;gBACF,MAAM,CAAC,KAAK,CAAC;YACjB,CAAC,CAAC,CAAC;YACH,MAAM,CAAC,QAAQ,CAAC;QACpB,CAAC;QACD,IAAI,CAAC,CAAC;YACF,MAAM,CAAC,UAAU,CAAC;QACtB,CAAC;IACL,CAAC;IAEL,wBAAC;AAAD,CAAC,AArCD,IAqCC;AArCY,iBAAiB;IAD7B,iBAAU,EAAE;GACA,iBAAiB,CAqC7B;AArCY,8CAAiB"} -------------------------------------------------------------------------------- /src/app/core/data-filter.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { propertyResolver } from '../shared/property-resolver'; 3 | 4 | @Injectable() 5 | export class DataFilterService { 6 | 7 | filter(datasource: any[], filterProperties: string[], filterData: string) { 8 | if (datasource && filterProperties && filterData) { 9 | filterData = filterData.toUpperCase(); 10 | const filtered = datasource.filter(item => { 11 | let match = false; 12 | for (const prop of filterProperties) { 13 | let propVal: any = ''; 14 | 15 | //Account for nested properties like 'state.name' 16 | if (prop.indexOf('.') > -1) { 17 | propVal = propertyResolver.resolve(prop, item); 18 | if (propVal) { 19 | propVal = propVal.toString().toUpperCase(); 20 | } 21 | } 22 | else { 23 | if (item[prop]) { 24 | propVal = item[prop].toString().toUpperCase(); 25 | } 26 | } 27 | 28 | if (propVal.indexOf(filterData) > -1) { 29 | match = true; 30 | break; 31 | } 32 | }; 33 | return match; 34 | }); 35 | return filtered; 36 | } 37 | else { 38 | return datasource; 39 | } 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /src/app/core/data.service.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | var core_1 = require("@angular/core"); 13 | var http_1 = require("@angular/http"); 14 | //Grab everything with import 'rxjs/Rx'; 15 | var Observable_1 = require("rxjs/Observable"); 16 | require("rxjs/add/observable/throw"); 17 | require("rxjs/add/operator/map"); 18 | require("rxjs/add/operator/catch"); 19 | var DataService = (function () { 20 | function DataService(http) { 21 | this.http = http; 22 | this.baseUrl = '/api/customers'; 23 | } 24 | DataService.prototype.getCustomers = function () { 25 | var _this = this; 26 | return this.http.get(this.baseUrl) 27 | .map(function (res) { 28 | var customers = res.json(); 29 | _this.calculateCustomersOrderTotal(customers); 30 | return customers; 31 | }) 32 | .catch(this.handleError); 33 | }; 34 | DataService.prototype.getCustomersPage = function (page, pageSize) { 35 | var _this = this; 36 | return this.http.get(this.baseUrl + "/page/" + page + "/" + pageSize) 37 | .map(function (res) { 38 | var totalRecords = +res.headers.get('x-inlinecount'); 39 | var customers = res.json(); 40 | _this.calculateCustomersOrderTotal(customers); 41 | return { 42 | results: customers, 43 | totalRecords: totalRecords 44 | }; 45 | }) 46 | .catch(this.handleError); 47 | }; 48 | DataService.prototype.getCustomer = function (id) { 49 | return this.http.get(this.baseUrl + '/' + id) 50 | .map(function (res) { return res.json(); }) 51 | .catch(this.handleError); 52 | }; 53 | DataService.prototype.insertCustomer = function (customer) { 54 | return this.http.post(this.baseUrl, customer) 55 | .map(function (res) { 56 | var data = res.json(); 57 | console.log('insertCustomer status: ' + data.status); 58 | return data.customer; 59 | }) 60 | .catch(this.handleError); 61 | }; 62 | DataService.prototype.updateCustomer = function (customer) { 63 | return this.http.put(this.baseUrl + '/' + customer._id, customer) 64 | .map(function (res) { 65 | var data = res.json(); 66 | console.log('updateCustomer status: ' + data.status); 67 | return data.customer; 68 | }) 69 | .catch(this.handleError); 70 | }; 71 | DataService.prototype.deleteCustomer = function (id) { 72 | return this.http.delete(this.baseUrl + '/' + id) 73 | .map(function (res) { return res.json().status; }) 74 | .catch(this.handleError); 75 | }; 76 | //Not used but could be called to pass "options" (3rd parameter) to 77 | //appropriate POST/PUT/DELETE calls made with http 78 | DataService.prototype.getRequestOptions = function () { 79 | var csrfToken = ''; //would retrieve from cookie or from page 80 | var options = new http_1.RequestOptions({ 81 | headers: new http_1.Headers({ 'x-xsrf-token': csrfToken }) 82 | }); 83 | return options; 84 | }; 85 | DataService.prototype.getStates = function () { 86 | return this.http.get('/api/states') 87 | .map(function (res) { return res.json(); }) 88 | .catch(this.handleError); 89 | }; 90 | DataService.prototype.calculateCustomersOrderTotal = function (customers) { 91 | for (var _i = 0, customers_1 = customers; _i < customers_1.length; _i++) { 92 | var customer = customers_1[_i]; 93 | if (customer && customer.orders) { 94 | var total = 0; 95 | for (var _a = 0, _b = customer.orders; _a < _b.length; _a++) { 96 | var order = _b[_a]; 97 | total += (order.price * order.quantity); 98 | } 99 | customer.orderTotal = total; 100 | } 101 | } 102 | }; 103 | DataService.prototype.handleError = function (error) { 104 | console.error('server error:', error); 105 | if (error instanceof http_1.Response) { 106 | var errMessage = ''; 107 | try { 108 | errMessage = error.json().error; 109 | } 110 | catch (err) { 111 | errMessage = error.statusText; 112 | } 113 | return Observable_1.Observable.throw(errMessage); 114 | // Use the following instead if using lite-server 115 | //return Observable.throw(err.text() || 'backend server error'); 116 | } 117 | return Observable_1.Observable.throw(error || 'Node.js server error'); 118 | }; 119 | return DataService; 120 | }()); 121 | DataService = __decorate([ 122 | core_1.Injectable(), 123 | __metadata("design:paramtypes", [http_1.Http]) 124 | ], DataService); 125 | exports.DataService = DataService; 126 | //# sourceMappingURL=data.service.js.map -------------------------------------------------------------------------------- /src/app/core/data.service.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"data.service.js","sourceRoot":"","sources":["data.service.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /src/app/core/data.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { HttpClient, HttpResponse, HttpErrorResponse } from '@angular/common/http'; 4 | 5 | import { Observable } from 'rxjs'; 6 | import { map, catchError } from 'rxjs/operators'; 7 | 8 | import { ICustomer, IOrder, IState, 9 | IPagedResults, ICustomerResponse } from '../shared/interfaces'; 10 | 11 | @Injectable() 12 | export class DataService { 13 | 14 | //Call ASP.NET Core 'microservice' 15 | baseUrl: string = 'http://localhost:5000/api/customers'; 16 | //Call Node.js 'microservice' 17 | statesUrl: string = 'http://localhost:3000/api/states'; 18 | 19 | constructor(private http: HttpClient) { } 20 | 21 | getCustomers() : Observable { 22 | return this.http.get(this.baseUrl) 23 | .pipe( 24 | map((customers: ICustomer[]) => { 25 | this.calculateCustomersOrderTotal(customers); 26 | return customers; 27 | }), 28 | catchError(this.handleError) 29 | ); 30 | } 31 | 32 | getCustomersPage(page: number, pageSize: number) : Observable> { 33 | return this.http.get( 34 | `${this.baseUrl}/page/${page}/${pageSize}`, {observe: 'response'}) 35 | .pipe( 36 | map(res => { 37 | //Need to observe response in order to get to this header (see {observe: 'response'} above) 38 | const totalRecords = +res.headers.get('X-InlineCount'); 39 | let customers = res.body as ICustomer[]; 40 | this.calculateCustomersOrderTotal(customers); 41 | return { 42 | results: customers, 43 | totalRecords: totalRecords 44 | }; 45 | }), 46 | catchError(this.handleError) 47 | ); 48 | } 49 | 50 | getCustomer(id: string) : Observable { 51 | return this.http.get(this.baseUrl + '/' + id) 52 | .pipe( 53 | catchError(this.handleError) 54 | ); 55 | } 56 | 57 | insertCustomer(customer: ICustomer) : Observable { 58 | return this.http.post(this.baseUrl, customer) 59 | .pipe( 60 | map((data) => { 61 | console.log('insertCustomer status: ' + data.status); 62 | return data.customer; 63 | }), 64 | catchError(this.handleError) 65 | ); 66 | } 67 | 68 | updateCustomer(customer: ICustomer) : Observable { 69 | return this.http.put(this.baseUrl + '/' + customer.id, customer) 70 | .pipe( 71 | map((data) => { 72 | console.log('updateCustomer status: ' + data.status); 73 | return data.customer; 74 | }), 75 | catchError(this.handleError) 76 | ); 77 | } 78 | 79 | deleteCustomer(id: string) : Observable { 80 | return this.http.delete(this.baseUrl + '/' + id) 81 | .pipe( 82 | catchError(this.handleError) 83 | ); 84 | } 85 | 86 | getStates(): Observable { 87 | return this.http.get(this.statesUrl) 88 | .pipe( 89 | catchError(this.handleError) 90 | ); 91 | } 92 | 93 | calculateCustomersOrderTotal(customers: ICustomer[]) { 94 | for (let customer of customers) { 95 | if (customer && customer.orders) { 96 | let total = 0; 97 | for (let order of customer.orders) { 98 | total += (order.price * order.quantity); 99 | } 100 | customer.orderTotal = total; 101 | } 102 | } 103 | } 104 | 105 | private handleError(error: HttpErrorResponse) { 106 | console.error('server error:', error); 107 | if (error.error instanceof Error) { 108 | let errMessage = error.error.message; 109 | return Observable.throw(errMessage); 110 | // Use the following instead if using lite-server 111 | //return Observable.throw(err.text() || 'backend server error'); 112 | } 113 | return Observable.throw(error || 'Server error'); 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /src/app/core/sorter.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | Object.defineProperty(exports, "__esModule", { value: true }); 9 | var core_1 = require("@angular/core"); 10 | var property_resolver_1 = require("../shared/property-resolver"); 11 | var Sorter = (function () { 12 | function Sorter() { 13 | this.property = null; 14 | this.direction = 1; 15 | } 16 | Sorter.prototype.sort = function (collection, prop) { 17 | var _this = this; 18 | this.property = prop; 19 | this.direction = (this.property === prop) ? this.direction * -1 : 1; 20 | collection.sort(function (a, b) { 21 | var aVal; 22 | var bVal; 23 | //Handle resolving complex properties such as 'state.name' for prop value 24 | if (prop && prop.indexOf('.') > -1) { 25 | aVal = property_resolver_1.propertyResolver.resolve(prop, a); 26 | bVal = property_resolver_1.propertyResolver.resolve(prop, b); 27 | } 28 | else { 29 | aVal = a[prop]; 30 | bVal = b[prop]; 31 | } 32 | //Fix issues that spaces before/after string value can cause such as ' San Francisco' 33 | if (_this.isString(aVal)) 34 | aVal = aVal.trim().toUpperCase(); 35 | if (_this.isString(bVal)) 36 | bVal = bVal.trim().toUpperCase(); 37 | if (aVal === bVal) { 38 | return 0; 39 | } 40 | else if (aVal > bVal) { 41 | return _this.direction * -1; 42 | } 43 | else { 44 | return _this.direction * 1; 45 | } 46 | }); 47 | }; 48 | Sorter.prototype.isString = function (val) { 49 | return (val && (typeof val === 'string' || val instanceof String)); 50 | }; 51 | return Sorter; 52 | }()); 53 | Sorter = __decorate([ 54 | core_1.Injectable() 55 | ], Sorter); 56 | exports.Sorter = Sorter; 57 | //# sourceMappingURL=sorter.js.map -------------------------------------------------------------------------------- /src/app/core/sorter.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"sorter.js","sourceRoot":"","sources":["sorter.ts"],"names":[],"mappings":";;;;;;;;AAAA,sCAA2C;AAC3C,iEAA+D;AAG/D,IAAa,MAAM;IADnB;QAGC,aAAQ,GAAW,IAAI,CAAC;QACxB,cAAS,GAAW,CAAC,CAAC;IAwCvB,CAAC;IAtCG,qBAAI,GAAJ,UAAK,UAAiB,EAAE,IAAS;QAAjC,iBAgCC;QA/BG,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,SAAS,GAAG,CAAC,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QAEpE,UAAU,CAAC,IAAI,CAAC,UAAC,CAAM,EAAC,CAAM;YAC1B,IAAI,IAAS,CAAC;YACd,IAAI,IAAS,CAAC;YAEd,yEAAyE;YACzE,EAAE,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;gBACnC,IAAI,GAAG,oCAAgB,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;gBACzC,IAAI,GAAG,oCAAgB,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YAC3C,CAAC;YACD,IAAI,CAAC,CAAC;gBACJ,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;gBACf,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;YACjB,CAAC;YAED,qFAAqF;YACrF,EAAE,CAAC,CAAC,KAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;gBAAC,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAC1D,EAAE,CAAC,CAAC,KAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;gBAAC,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAE1D,EAAE,CAAA,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA,CAAC;gBACd,MAAM,CAAC,CAAC,CAAC;YACb,CAAC;YACD,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,CAAA,CAAC;gBAClB,MAAM,CAAC,KAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;YAC/B,CAAC;YACD,IAAI,CAAC,CAAC;gBACF,MAAM,CAAC,KAAI,CAAC,SAAS,GAAG,CAAC,CAAC;YAC9B,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC;IAED,yBAAQ,GAAR,UAAS,GAAQ;QACf,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,YAAY,MAAM,CAAC,CAAC,CAAC;IACrE,CAAC;IAEL,aAAC;AAAD,CAAC,AA3CD,IA2CC;AA3CY,MAAM;IADlB,iBAAU,EAAE;GACA,MAAM,CA2ClB;AA3CY,wBAAM"} -------------------------------------------------------------------------------- /src/app/core/sorter.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { propertyResolver } from '../shared/property-resolver'; 3 | 4 | @Injectable() 5 | export class Sorter { 6 | 7 | property: string = null; 8 | direction: number = 1; 9 | 10 | sort(collection: any[], prop: any) { 11 | this.property = prop; 12 | this.direction = (this.property === prop) ? this.direction * -1 : 1; 13 | 14 | collection.sort((a: any,b: any) => { 15 | let aVal: any; 16 | let bVal: any; 17 | 18 | //Handle resolving complex properties such as 'state.name' for prop value 19 | if (prop && prop.indexOf('.') > -1) { 20 | aVal = propertyResolver.resolve(prop, a); 21 | bVal = propertyResolver.resolve(prop, b); 22 | } 23 | else { 24 | aVal = a[prop]; 25 | bVal = b[prop]; 26 | } 27 | 28 | //Fix issues that spaces before/after string value can cause such as ' San Francisco' 29 | if (this.isString(aVal)) aVal = aVal.trim().toUpperCase(); 30 | if (this.isString(bVal)) bVal = bVal.trim().toUpperCase(); 31 | 32 | if(aVal === bVal){ 33 | return 0; 34 | } 35 | else if (aVal > bVal){ 36 | return this.direction * -1; 37 | } 38 | else { 39 | return this.direction * 1; 40 | } 41 | }); 42 | } 43 | 44 | isString(val: any) : boolean { 45 | return (val && (typeof val === 'string' || val instanceof String)); 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /src/app/core/trackby.service.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | Object.defineProperty(exports, "__esModule", { value: true }); 9 | var core_1 = require("@angular/core"); 10 | var TrackByService = (function () { 11 | function TrackByService() { 12 | } 13 | TrackByService.prototype.customer = function (index, customer) { 14 | return customer._id; 15 | }; 16 | return TrackByService; 17 | }()); 18 | TrackByService = __decorate([ 19 | core_1.Injectable() 20 | ], TrackByService); 21 | exports.TrackByService = TrackByService; 22 | //# sourceMappingURL=trackby.service.js.map -------------------------------------------------------------------------------- /src/app/core/trackby.service.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"trackby.service.js","sourceRoot":"","sources":["trackby.service.ts"],"names":[],"mappings":";;;;;;;;AAAA,sCAA2C;AAK3C,IAAa,cAAc;IAA3B;IAMA,CAAC;IAJC,iCAAQ,GAAR,UAAS,KAAa,EAAE,QAAmB;QACzC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;IACtB,CAAC;IAEH,qBAAC;AAAD,CAAC,AAND,IAMC;AANY,cAAc;IAD1B,iBAAU,EAAE;GACA,cAAc,CAM1B;AANY,wCAAc"} -------------------------------------------------------------------------------- /src/app/core/trackby.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { ICustomer } from '../shared/interfaces'; 4 | 5 | @Injectable() 6 | export class TrackByService { 7 | 8 | customer(index: number, customer: ICustomer) { 9 | return customer.id; 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /src/app/customers/customer-edit-reactive.component.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | var core_1 = require("@angular/core"); 13 | var router_1 = require("@angular/router"); 14 | var forms_1 = require("@angular/forms"); 15 | var data_service_1 = require("../core/data.service"); 16 | var validation_service_1 = require("../shared/validation.service"); 17 | var CustomerEditReactiveComponent = (function () { 18 | function CustomerEditReactiveComponent(router, route, dataService, formBuilder) { 19 | this.router = router; 20 | this.route = route; 21 | this.dataService = dataService; 22 | this.formBuilder = formBuilder; 23 | this.customer = { 24 | firstName: '', 25 | lastName: '', 26 | gender: '', 27 | address: '', 28 | email: '', 29 | city: '', 30 | zip: 0 31 | }; 32 | this.operationText = 'Insert'; 33 | } 34 | CustomerEditReactiveComponent.prototype.ngOnInit = function () { 35 | var id = this.route.snapshot.params['id']; 36 | if (id !== '0') { 37 | this.operationText = 'Update'; 38 | this.getCustomer(id); 39 | } 40 | this.getStates(); 41 | this.buildForm(); 42 | }; 43 | CustomerEditReactiveComponent.prototype.getCustomer = function (id) { 44 | var _this = this; 45 | this.dataService.getCustomer(id) 46 | .subscribe(function (customer) { 47 | //Quick and dirty clone used in case user cancels out of form 48 | var cust = JSON.stringify(customer); 49 | _this.customer = JSON.parse(cust); 50 | _this.buildForm(); 51 | }, function (err) { return console.log(err); }); 52 | }; 53 | CustomerEditReactiveComponent.prototype.buildForm = function () { 54 | this.customerForm = this.formBuilder.group({ 55 | firstName: [this.customer.firstName, forms_1.Validators.required], 56 | lastName: [this.customer.lastName, forms_1.Validators.required], 57 | gender: [this.customer.gender, forms_1.Validators.required], 58 | email: [this.customer.email, [forms_1.Validators.required, validation_service_1.ValidationService.emailValidator]], 59 | address: [this.customer.address, forms_1.Validators.required], 60 | city: [this.customer.city, forms_1.Validators.required], 61 | stateId: [this.customer.stateId, forms_1.Validators.required] 62 | }); 63 | }; 64 | CustomerEditReactiveComponent.prototype.getStates = function () { 65 | var _this = this; 66 | this.dataService.getStates().subscribe(function (states) { return _this.states = states; }); 67 | }; 68 | CustomerEditReactiveComponent.prototype.submit = function (_a) { 69 | var _this = this; 70 | var value = _a.value, valid = _a.valid; 71 | value._id = this.customer._id; 72 | value.zip = this.customer.zip || 0; 73 | // var customer: ICustomer = { 74 | // _id: this.customer._id, 75 | // }; 76 | if (value._id) { 77 | this.dataService.updateCustomer(value) 78 | .subscribe(function (customer) { 79 | if (customer) { 80 | _this.router.navigate(['/customers']); 81 | } 82 | else { 83 | _this.errorMessage = 'Unable to save customer'; 84 | } 85 | }, function (err) { return console.log(err); }); 86 | } 87 | else { 88 | this.dataService.insertCustomer(value) 89 | .subscribe(function (customer) { 90 | if (customer) { 91 | _this.router.navigate(['/customers']); 92 | } 93 | else { 94 | _this.errorMessage = 'Unable to add customer'; 95 | } 96 | }, function (err) { return console.log(err); }); 97 | } 98 | }; 99 | CustomerEditReactiveComponent.prototype.cancel = function (event) { 100 | event.preventDefault(); 101 | this.router.navigate(['/customers']); 102 | }; 103 | CustomerEditReactiveComponent.prototype.delete = function (event) { 104 | var _this = this; 105 | event.preventDefault(); 106 | this.dataService.deleteCustomer(this.customer._id) 107 | .subscribe(function (status) { 108 | if (status) { 109 | _this.router.navigate(['/customers']); 110 | } 111 | else { 112 | _this.errorMessage = 'Unable to delete customer'; 113 | } 114 | }, function (err) { return console.log(err); }); 115 | }; 116 | return CustomerEditReactiveComponent; 117 | }()); 118 | CustomerEditReactiveComponent = __decorate([ 119 | core_1.Component({ 120 | moduleId: module.id, 121 | selector: 'customer-edit-reactive', 122 | templateUrl: 'customer-edit-reactive.component.html' 123 | }), 124 | __metadata("design:paramtypes", [router_1.Router, 125 | router_1.ActivatedRoute, 126 | data_service_1.DataService, 127 | forms_1.FormBuilder]) 128 | ], CustomerEditReactiveComponent); 129 | exports.CustomerEditReactiveComponent = CustomerEditReactiveComponent; 130 | //# sourceMappingURL=customer-edit-reactive.component.js.map -------------------------------------------------------------------------------- /src/app/customers/customer-edit-reactive.component.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"customer-edit-reactive.component.js","sourceRoot":"","sources":["customer-edit-reactive.component.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /src/app/customers/customer-edit.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | 5 | {{ customer.firstName }} {{ customer.lastName }} 6 |

7 |
8 |
9 |
10 |
11 | 12 | 13 |
First Name is required
14 |
15 |
16 | 17 | 18 |
Last Name is required
19 |
20 |
21 | 22 |
23 |
24 | 28 |
29 |
30 | 34 |
35 |
36 |
37 | 38 | 39 |
Email is required and must be valid
40 |
41 |
42 | 43 | 44 |
Address is required
45 |
46 |
47 | 48 | 49 |
City is required
50 |
51 |
52 | 53 | 56 |
57 |
58 | 59 |
60 |
61 | Delete Customer?     62 | 63 |
64 |    65 | 66 |
67 |    68 | 69 |
70 |
71 |
72 |
73 |
{{ errorMessage }}
74 | 75 |
76 |
-------------------------------------------------------------------------------- /src/app/customers/customer-edit.component.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | var core_1 = require("@angular/core"); 13 | var router_1 = require("@angular/router"); 14 | var data_service_1 = require("../core/data.service"); 15 | var CustomerEditComponent = (function () { 16 | function CustomerEditComponent(router, route, dataService) { 17 | this.router = router; 18 | this.route = route; 19 | this.dataService = dataService; 20 | this.customer = { 21 | firstName: '', 22 | lastName: '', 23 | gender: '', 24 | address: '', 25 | email: '', 26 | city: '', 27 | zip: 0 28 | }; 29 | this.operationText = 'Insert'; 30 | } 31 | CustomerEditComponent.prototype.ngOnInit = function () { 32 | var id = this.route.snapshot.params['id']; 33 | if (id !== '0') { 34 | this.operationText = 'Update'; 35 | this.getCustomer(id); 36 | } 37 | this.getStates(); 38 | }; 39 | CustomerEditComponent.prototype.getCustomer = function (id) { 40 | var _this = this; 41 | this.dataService.getCustomer(id) 42 | .subscribe(function (customer) { 43 | //Quick and dirty clone used in case user cancels out of form 44 | var cust = JSON.stringify(customer); 45 | _this.customer = JSON.parse(cust); 46 | }, function (err) { return console.log(err); }); 47 | }; 48 | CustomerEditComponent.prototype.getStates = function () { 49 | var _this = this; 50 | this.dataService.getStates().subscribe(function (states) { return _this.states = states; }); 51 | }; 52 | CustomerEditComponent.prototype.submit = function () { 53 | var _this = this; 54 | if (this.customer._id) { 55 | this.dataService.updateCustomer(this.customer) 56 | .subscribe(function (customer) { 57 | if (customer) { 58 | _this.router.navigate(['/customers']); 59 | } 60 | else { 61 | _this.errorMessage = 'Unable to save customer'; 62 | } 63 | }, function (err) { return console.log(err); }); 64 | } 65 | else { 66 | this.dataService.insertCustomer(this.customer) 67 | .subscribe(function (customer) { 68 | if (customer) { 69 | _this.router.navigate(['/customers']); 70 | } 71 | else { 72 | _this.errorMessage = 'Unable to add customer'; 73 | } 74 | }, function (err) { return console.log(err); }); 75 | } 76 | }; 77 | CustomerEditComponent.prototype.cancel = function (event) { 78 | event.preventDefault(); 79 | this.router.navigate(['/']); 80 | }; 81 | CustomerEditComponent.prototype.delete = function (event) { 82 | var _this = this; 83 | event.preventDefault(); 84 | this.dataService.deleteCustomer(this.customer._id) 85 | .subscribe(function (status) { 86 | if (status) { 87 | _this.router.navigate(['/customers']); 88 | } 89 | else { 90 | _this.errorMessage = 'Unable to delete customer'; 91 | } 92 | }, function (err) { return console.log(err); }); 93 | }; 94 | return CustomerEditComponent; 95 | }()); 96 | CustomerEditComponent = __decorate([ 97 | core_1.Component({ 98 | moduleId: module.id, 99 | selector: 'customer-edit', 100 | templateUrl: 'customer-edit.component.html' 101 | }), 102 | __metadata("design:paramtypes", [router_1.Router, 103 | router_1.ActivatedRoute, 104 | data_service_1.DataService]) 105 | ], CustomerEditComponent); 106 | exports.CustomerEditComponent = CustomerEditComponent; 107 | //# sourceMappingURL=customer-edit.component.js.map -------------------------------------------------------------------------------- /src/app/customers/customer-edit.component.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"customer-edit.component.js","sourceRoot":"","sources":["customer-edit.component.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /src/app/customers/customer-edit.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Router, ActivatedRoute } from '@angular/router'; 3 | import { FormBuilder, FormGroup, Validators } from '@angular/forms'; 4 | 5 | import { DataService } from '../core/data.service'; 6 | import { ICustomer, IState } from '../shared/interfaces'; 7 | import { ValidationService } from '../shared/validation.service'; 8 | 9 | @Component({ 10 | selector: 'customer-edit', 11 | templateUrl: 'customer-edit.component.html' 12 | }) 13 | export class CustomerEditComponent implements OnInit { 14 | 15 | customerForm: FormGroup; 16 | customer: ICustomer = { 17 | firstName: '', 18 | lastName: '', 19 | gender: '', 20 | address: '', 21 | email: '', 22 | city: '', 23 | zip: 0, 24 | state: { id: '0', abbreviation: '', name: ''} 25 | }; 26 | states: IState[]; 27 | errorMessage: string; 28 | deleteMessageEnabled: boolean; 29 | operationText: string = 'Insert'; 30 | 31 | constructor(private router: Router, 32 | private route: ActivatedRoute, 33 | private dataService: DataService, 34 | private formBuilder: FormBuilder) { } 35 | 36 | ngOnInit() { 37 | let id = this.route.snapshot.params['id']; 38 | if (id !== '0') { 39 | this.operationText = 'Update'; 40 | this.getCustomer(id); 41 | } 42 | 43 | this.getStates(); 44 | this.buildForm(); 45 | } 46 | 47 | getCustomer(id: string) { 48 | this.dataService.getCustomer(id) 49 | .subscribe((customer: ICustomer) => { 50 | //Quick and dirty clone used in case user cancels out of form 51 | const cust = JSON.stringify(customer); 52 | this.customer = JSON.parse(cust); 53 | this.buildForm(); 54 | }, 55 | (err) => console.log(err)); 56 | } 57 | 58 | buildForm() { 59 | this.customerForm = this.formBuilder.group({ 60 | firstName: [this.customer.firstName, Validators.required], 61 | lastName: [this.customer.lastName, Validators.required], 62 | gender: [this.customer.gender, Validators.required], 63 | email: [this.customer.email, [Validators.required, ValidationService.emailValidator]], 64 | address: [this.customer.address, Validators.required], 65 | city: [this.customer.city, Validators.required], 66 | stateId: [this.customer.state.id, Validators.required] 67 | }); 68 | } 69 | 70 | getStates() { 71 | this.dataService.getStates().subscribe((states: IState[]) => this.states = states); 72 | } 73 | 74 | submit({ value, valid }: { value: ICustomer, valid: boolean }) { 75 | 76 | value.id = this.customer.id; 77 | value.zip = this.customer.zip || 0; 78 | // var customer: ICustomer = { 79 | // id: this.customer.id, 80 | // }; 81 | 82 | if (value.id) { 83 | 84 | this.dataService.updateCustomer(value) 85 | .subscribe((customer: ICustomer) => { 86 | if (customer) { 87 | this.router.navigate(['/customers']); 88 | } 89 | else { 90 | this.errorMessage = 'Unable to save customer'; 91 | } 92 | }, 93 | (err) => console.log(err)); 94 | 95 | } else { 96 | 97 | this.dataService.insertCustomer(value) 98 | .subscribe((customer: ICustomer) => { 99 | if (customer) { 100 | this.router.navigate(['/customers']); 101 | } 102 | else { 103 | this.errorMessage = 'Unable to add customer'; 104 | } 105 | }, 106 | (err) => console.log(err)); 107 | 108 | } 109 | } 110 | 111 | cancel(event: Event) { 112 | event.preventDefault(); 113 | this.router.navigate(['/customers']); 114 | } 115 | 116 | delete(event: Event) { 117 | event.preventDefault(); 118 | this.dataService.deleteCustomer(this.customer.id) 119 | .subscribe((status: boolean) => { 120 | if (status) { 121 | this.router.navigate(['/customers']); 122 | } 123 | else { 124 | this.errorMessage = 'Unable to delete customer'; 125 | } 126 | }, 127 | (err) => console.log(err)); 128 | } 129 | 130 | } -------------------------------------------------------------------------------- /src/app/customers/customers-grid.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
 First NameLast NameAddressCityStateOrder Total
Customer Image{{ customer.firstName | capitalize }}{{ customer.lastName | capitalize }}{{ customer.address }}{{ customer.city | trim }}{{ customer.state.name }}{{ customer.orderTotal | currency:'USD':true }}
 No Records Found
34 |
35 |
36 |
37 |
38 | -------------------------------------------------------------------------------- /src/app/customers/customers-grid.component.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | var core_1 = require("@angular/core"); 13 | var sorter_1 = require("../core/sorter"); 14 | var trackby_service_1 = require("../core/trackby.service"); 15 | var CustomersGridComponent = (function () { 16 | function CustomersGridComponent(sorter, trackby) { 17 | this.sorter = sorter; 18 | this.trackby = trackby; 19 | this.customers = []; 20 | } 21 | CustomersGridComponent.prototype.ngOnInit = function () { 22 | }; 23 | CustomersGridComponent.prototype.sort = function (prop) { 24 | this.sorter.sort(this.customers, prop); 25 | }; 26 | return CustomersGridComponent; 27 | }()); 28 | __decorate([ 29 | core_1.Input(), 30 | __metadata("design:type", Array) 31 | ], CustomersGridComponent.prototype, "customers", void 0); 32 | CustomersGridComponent = __decorate([ 33 | core_1.Component({ 34 | moduleId: module.id, 35 | selector: 'customers-grid', 36 | templateUrl: 'customers-grid.component.html', 37 | //When using OnPush detectors, then the framework will check an OnPush 38 | //component when any of its input properties changes, when it fires 39 | //an event, or when an observable fires an event ~ Victor Savkin (Angular Team) 40 | changeDetection: core_1.ChangeDetectionStrategy.OnPush 41 | }), 42 | __metadata("design:paramtypes", [sorter_1.Sorter, trackby_service_1.TrackByService]) 43 | ], CustomersGridComponent); 44 | exports.CustomersGridComponent = CustomersGridComponent; 45 | //# sourceMappingURL=customers-grid.component.js.map -------------------------------------------------------------------------------- /src/app/customers/customers-grid.component.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"customers-grid.component.js","sourceRoot":"","sources":["customers-grid.component.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,sCAAkF;AAGlF,yCAAwC;AACxC,2DAAyD;AAWzD,IAAa,sBAAsB;IAIjC,gCAAoB,MAAc,EAAS,OAAuB;QAA9C,WAAM,GAAN,MAAM,CAAQ;QAAS,YAAO,GAAP,OAAO,CAAgB;QAFzD,cAAS,GAAgB,EAAE,CAAC;IAEiC,CAAC;IAEvE,yCAAQ,GAAR;IAEA,CAAC;IAED,qCAAI,GAAJ,UAAK,IAAY;QACb,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC3C,CAAC;IAEH,6BAAC;AAAD,CAAC,AAdD,IAcC;AAZU;IAAR,YAAK,EAAE;;yDAA6B;AAF1B,sBAAsB;IATlC,gBAAS,CAAC;QACT,QAAQ,EAAE,MAAM,CAAC,EAAE;QACnB,QAAQ,EAAE,gBAAgB;QAC1B,WAAW,EAAE,+BAA+B;QAC5C,uEAAuE;QACvE,oEAAoE;QACpE,+EAA+E;QAC/E,eAAe,EAAE,8BAAuB,CAAC,MAAM;KAChD,CAAC;qCAK4B,eAAM,EAAkB,gCAAc;GAJvD,sBAAsB,CAclC;AAdY,wDAAsB"} -------------------------------------------------------------------------------- /src/app/customers/customers-grid.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core'; 2 | 3 | import { ICustomer } from '../shared/interfaces'; 4 | import { Sorter } from '../core/sorter'; 5 | import { TrackByService } from '../core/trackby.service'; 6 | 7 | @Component({ 8 | selector: 'customers-grid', 9 | templateUrl: 'customers-grid.component.html', 10 | //When using OnPush detectors, then the framework will check an OnPush 11 | //component when any of its input properties changes, when it fires 12 | //an event, or when an observable fires an event ~ Victor Savkin (Angular Team) 13 | changeDetection: ChangeDetectionStrategy.OnPush 14 | }) 15 | export class CustomersGridComponent implements OnInit { 16 | 17 | @Input() customers: ICustomer[] = []; 18 | 19 | constructor(private sorter: Sorter, public trackby: TrackByService) { } 20 | 21 | ngOnInit() { 22 | 23 | } 24 | 25 | sort(prop: string) { 26 | this.sorter.sort(this.customers, prop); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/app/customers/customers.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

5 | 6 | {{ title }} 7 |

8 |
9 |
10 |
11 |
12 | 15 |
16 |
17 | Add New Customer 18 |
19 |
20 | 21 | 22 | 23 | 26 | 27 |
28 |
29 | -------------------------------------------------------------------------------- /src/app/customers/customers.component.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | var core_1 = require("@angular/core"); 13 | var router_1 = require("@angular/router"); 14 | var data_filter_service_1 = require("../core/data-filter.service"); 15 | var data_service_1 = require("../core/data.service"); 16 | var CustomersComponent = (function () { 17 | function CustomersComponent(router, dataService, dataFilter) { 18 | this.router = router; 19 | this.dataService = dataService; 20 | this.dataFilter = dataFilter; 21 | this.customers = []; 22 | this.filteredCustomers = []; 23 | this.totalRecords = 0; 24 | this.pageSize = 10; 25 | } 26 | CustomersComponent.prototype.ngOnInit = function () { 27 | this.title = 'Customers'; 28 | this.getCustomersPage(1); 29 | }; 30 | CustomersComponent.prototype.filterChanged = function (filterText) { 31 | if (filterText && this.customers) { 32 | var props = ['firstName', 'lastName', 'address', 'city', 'state.name', 'orderTotal']; 33 | this.filteredCustomers = this.dataFilter.filter(this.customers, props, filterText); 34 | } 35 | else { 36 | this.filteredCustomers = this.customers; 37 | } 38 | }; 39 | CustomersComponent.prototype.pageChanged = function (page) { 40 | this.getCustomersPage(page); 41 | }; 42 | CustomersComponent.prototype.getCustomersPage = function (page) { 43 | var _this = this; 44 | this.dataService.getCustomersPage((page - 1) * this.pageSize, this.pageSize) 45 | .subscribe(function (response) { 46 | _this.customers = _this.filteredCustomers = response.results; 47 | _this.totalRecords = response.totalRecords; 48 | }, function (err) { return console.log(err); }, function () { return console.log('getCustomersPage() retrieved customers'); }); 49 | }; 50 | return CustomersComponent; 51 | }()); 52 | CustomersComponent = __decorate([ 53 | core_1.Component({ 54 | moduleId: module.id, 55 | selector: 'customers', 56 | templateUrl: 'customers.component.html' 57 | }), 58 | __metadata("design:paramtypes", [router_1.Router, 59 | data_service_1.DataService, 60 | data_filter_service_1.DataFilterService]) 61 | ], CustomersComponent); 62 | exports.CustomersComponent = CustomersComponent; 63 | //# sourceMappingURL=customers.component.js.map -------------------------------------------------------------------------------- /src/app/customers/customers.component.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"customers.component.js","sourceRoot":"","sources":["customers.component.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,sCAAkD;AAClD,0CAAyC;AAEzC,mEAAgE;AAChE,qDAAmD;AAQnD,IAAa,kBAAkB;IAS7B,4BAAoB,MAAc,EACd,WAAwB,EACxB,UAA6B;QAF7B,WAAM,GAAN,MAAM,CAAQ;QACd,gBAAW,GAAX,WAAW,CAAa;QACxB,eAAU,GAAV,UAAU,CAAmB;QARjD,cAAS,GAAgB,EAAE,CAAC;QAC5B,sBAAiB,GAAgB,EAAE,CAAC;QAEpC,iBAAY,GAAW,CAAC,CAAC;QACzB,aAAQ,GAAW,EAAE,CAAC;IAI+B,CAAC;IAEtD,qCAAQ,GAAR;QACE,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC;QACzB,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAC3B,CAAC;IAED,0CAAa,GAAb,UAAc,UAAkB;QAC9B,EAAE,CAAC,CAAC,UAAU,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;YAC/B,IAAI,KAAK,GAAG,CAAC,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;YACrF,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;QACvF,CAAC;QACD,IAAI,CAAC,CAAC;YACJ,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,SAAS,CAAC;QAC1C,CAAC;IACH,CAAC;IAED,wCAAW,GAAX,UAAY,IAAY;QACtB,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED,6CAAgB,GAAhB,UAAiB,IAAY;QAA7B,iBAQC;QAPC,IAAI,CAAC,WAAW,CAAC,gBAAgB,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC;aACvE,SAAS,CAAC,UAAC,QAAoC;YAC9C,KAAI,CAAC,SAAS,GAAG,KAAI,CAAC,iBAAiB,GAAG,QAAQ,CAAC,OAAO,CAAC;YAC3D,KAAI,CAAC,YAAY,GAAG,QAAQ,CAAC,YAAY,CAAC;QAC5C,CAAC,EACD,UAAC,GAAQ,IAAK,OAAA,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAhB,CAAgB,EAC9B,cAAM,OAAA,OAAO,CAAC,GAAG,CAAC,wCAAwC,CAAC,EAArD,CAAqD,CAAC,CAAC;IACnE,CAAC;IAEH,yBAAC;AAAD,CAAC,AA1CD,IA0CC;AA1CY,kBAAkB;IAL9B,gBAAS,CAAC;QACT,QAAQ,EAAE,MAAM,CAAC,EAAE;QACnB,QAAQ,EAAE,WAAW;QACrB,WAAW,EAAE,0BAA0B;KACxC,CAAC;qCAU4B,eAAM;QACD,0BAAW;QACZ,uCAAiB;GAXtC,kBAAkB,CA0C9B;AA1CY,gDAAkB"} -------------------------------------------------------------------------------- /src/app/customers/customers.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | 4 | import { DataFilterService } from '../core/data-filter.service'; 5 | import { DataService } from '../core/data.service'; 6 | import { ICustomer, IOrder, IPagedResults } from '../shared/interfaces'; 7 | 8 | @Component({ 9 | selector: 'customers', 10 | templateUrl: 'customers.component.html' 11 | }) 12 | export class CustomersComponent implements OnInit { 13 | 14 | title: string; 15 | customers: ICustomer[] = []; 16 | filteredCustomers: ICustomer[] = []; 17 | 18 | totalRecords: number = 0; 19 | pageSize: number = 10; 20 | 21 | constructor(private router: Router, 22 | private dataService: DataService, 23 | private dataFilter: DataFilterService) { } 24 | 25 | ngOnInit() { 26 | this.title = 'Customers'; 27 | this.getCustomersPage(1); 28 | } 29 | 30 | filterChanged(filterText: string) { 31 | if (filterText && this.customers) { 32 | let props = ['firstName', 'lastName', 'address', 'city', 'state.name', 'orderTotal']; 33 | this.filteredCustomers = this.dataFilter.filter(this.customers, props, filterText); 34 | } 35 | else { 36 | this.filteredCustomers = this.customers; 37 | } 38 | } 39 | 40 | pageChanged(page: number) { 41 | this.getCustomersPage(page); 42 | } 43 | 44 | getCustomersPage(page: number) { 45 | this.dataService.getCustomersPage((page - 1) * this.pageSize, this.pageSize) 46 | .subscribe((response: IPagedResults) => { 47 | this.customers = this.filteredCustomers = response.results; 48 | this.totalRecords = response.totalRecords; 49 | }, 50 | (err: any) => console.log(err), 51 | () => console.log('getCustomersPage() retrieved customers')); 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /src/app/shared/ensureModuleLoadedOnceGuard.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var EnsureModuleLoadedOnceGuard = (function () { 4 | function EnsureModuleLoadedOnceGuard(targetModule) { 5 | if (targetModule) { 6 | throw new Error(targetModule.constructor.name + " has already been loaded. Import this module in the AppModule only."); 7 | } 8 | } 9 | return EnsureModuleLoadedOnceGuard; 10 | }()); 11 | exports.EnsureModuleLoadedOnceGuard = EnsureModuleLoadedOnceGuard; 12 | //# sourceMappingURL=ensureModuleLoadedOnceGuard.js.map -------------------------------------------------------------------------------- /src/app/shared/ensureModuleLoadedOnceGuard.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"ensureModuleLoadedOnceGuard.js","sourceRoot":"","sources":["ensureModuleLoadedOnceGuard.ts"],"names":[],"mappings":";;AAAA;IAEE,qCAAY,YAAiB;QAC3B,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC;YACjB,MAAM,IAAI,KAAK,CAAI,YAAY,CAAC,WAAW,CAAC,IAAI,wEAAqE,CAAC,CAAC;QACzH,CAAC;IACH,CAAC;IAEH,kCAAC;AAAD,CAAC,AARD,IAQC;AARY,kEAA2B"} -------------------------------------------------------------------------------- /src/app/shared/ensureModuleLoadedOnceGuard.ts: -------------------------------------------------------------------------------- 1 | export class EnsureModuleLoadedOnceGuard { 2 | 3 | constructor(targetModule: any) { 4 | if (targetModule) { 5 | throw new Error(`${targetModule.constructor.name} has already been loaded. Import this module in the AppModule only.`); 6 | } 7 | } 8 | 9 | } -------------------------------------------------------------------------------- /src/app/shared/filter-textbox/filter-textbox.component.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | var core_1 = require("@angular/core"); 13 | var FilterTextboxComponent = (function () { 14 | function FilterTextboxComponent() { 15 | this.model = { filter: null }; 16 | this.changed = new core_1.EventEmitter(); 17 | } 18 | FilterTextboxComponent.prototype.filterChanged = function (event) { 19 | event.preventDefault(); 20 | this.changed.emit(this.model.filter); //Raise changed event 21 | }; 22 | return FilterTextboxComponent; 23 | }()); 24 | __decorate([ 25 | core_1.Output(), 26 | __metadata("design:type", core_1.EventEmitter) 27 | ], FilterTextboxComponent.prototype, "changed", void 0); 28 | FilterTextboxComponent = __decorate([ 29 | core_1.Component({ 30 | selector: 'filter-textbox', 31 | template: "\n
\n Filter:\n \n
\n " 32 | }) 33 | ], FilterTextboxComponent); 34 | exports.FilterTextboxComponent = FilterTextboxComponent; 35 | //# sourceMappingURL=filter-textbox.component.js.map -------------------------------------------------------------------------------- /src/app/shared/filter-textbox/filter-textbox.component.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"filter-textbox.component.js","sourceRoot":"","sources":["filter-textbox.component.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,sCAAgE;AAahE,IAAa,sBAAsB;IAXnC;QAcI,UAAK,GAAuB,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QAG7C,YAAO,GAAyB,IAAI,mBAAY,EAAU,CAAC;IAM/D,CAAC;IAJG,8CAAa,GAAb,UAAc,KAAU;QACtB,KAAK,CAAC,cAAc,EAAE,CAAC;QACvB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,qBAAqB;IAC7D,CAAC;IACL,6BAAC;AAAD,CAAC,AAZD,IAYC;AANG;IADC,aAAM,EAAE;8BACA,mBAAY;uDAAsC;AANlD,sBAAsB;IAXlC,gBAAS,CAAC;QACT,QAAQ,EAAE,gBAAgB;QAC1B,QAAQ,EAAE,sMAOT;KACF,CAAC;GACW,sBAAsB,CAYlC;AAZY,wDAAsB"} -------------------------------------------------------------------------------- /src/app/shared/filter-textbox/filter-textbox.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Output, EventEmitter } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'filter-textbox', 5 | template: ` 6 |
7 | Filter: 8 | 11 |
12 | ` 13 | }) 14 | export class FilterTextboxComponent { 15 | 16 | 17 | model: { filter: string } = { filter: null }; 18 | 19 | @Output() 20 | changed: EventEmitter = new EventEmitter(); 21 | 22 | filterChanged(event: any) { 23 | event.preventDefault(); 24 | this.changed.emit(this.model.filter); //Raise changed event 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/app/shared/interfaces.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | //# sourceMappingURL=interfaces.js.map -------------------------------------------------------------------------------- /src/app/shared/interfaces.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"interfaces.js","sourceRoot":"","sources":["interfaces.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /src/app/shared/interfaces.ts: -------------------------------------------------------------------------------- 1 | import { ModuleWithProviders } from '@angular/core'; 2 | 3 | export interface ICustomer { 4 | id?: string; 5 | firstName: string; 6 | lastName: string; 7 | email: string; 8 | address: string; 9 | city: string; 10 | state?: IState; 11 | stateId?: number; 12 | zip: number; 13 | gender: string; 14 | orderCount?: number; 15 | orders?: IOrder[]; 16 | orderTotal?: number; 17 | } 18 | 19 | export interface IState { 20 | id?: string; 21 | abbreviation: string; 22 | name: string; 23 | } 24 | 25 | export interface IOrder { 26 | product: string; 27 | price: number; 28 | quantity: number; 29 | orderTotal?: number; 30 | } 31 | 32 | export interface IRouting { 33 | routes: ModuleWithProviders, 34 | components: any[] 35 | } 36 | 37 | export interface IPagedResults { 38 | totalRecords: number; 39 | results: T; 40 | } 41 | 42 | export interface ICustomerResponse { 43 | status: boolean; 44 | customer: ICustomer; 45 | } -------------------------------------------------------------------------------- /src/app/shared/pagination/pagination.component.css: -------------------------------------------------------------------------------- 1 | .pagination>.active>a, 2 | .pagination>.active>a:focus, 3 | .pagination>.active>a:hover, 4 | .pagination>.active>span, 5 | .pagination>.active>span:focus, 6 | .pagination>.active>span:hover { 7 | background-color: #027FF4; 8 | border-color: #027FF4; 9 | } 10 | 11 | .pagination a { 12 | cursor: pointer; 13 | } -------------------------------------------------------------------------------- /src/app/shared/pagination/pagination.component.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/shared/pagination/pagination.component.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | var core_1 = require("@angular/core"); 13 | var PaginationComponent = (function () { 14 | function PaginationComponent() { 15 | this.pages = []; 16 | this.currentPage = 1; 17 | this.isVisible = false; 18 | this.previousEnabled = false; 19 | this.nextEnabled = true; 20 | this.pageChanged = new core_1.EventEmitter(); 21 | } 22 | Object.defineProperty(PaginationComponent.prototype, "pageSize", { 23 | get: function () { 24 | return this.pagerPageSize; 25 | }, 26 | set: function (size) { 27 | this.pagerPageSize = size; 28 | this.update(); 29 | }, 30 | enumerable: true, 31 | configurable: true 32 | }); 33 | Object.defineProperty(PaginationComponent.prototype, "totalItems", { 34 | get: function () { 35 | return this.pagerTotalItems; 36 | }, 37 | set: function (itemCount) { 38 | this.pagerTotalItems = itemCount; 39 | this.update(); 40 | }, 41 | enumerable: true, 42 | configurable: true 43 | }); 44 | PaginationComponent.prototype.ngOnInit = function () { 45 | }; 46 | PaginationComponent.prototype.update = function () { 47 | if (this.pagerTotalItems && this.pagerPageSize) { 48 | this.totalPages = Math.ceil(this.pagerTotalItems / this.pageSize); 49 | this.isVisible = true; 50 | if (this.totalItems >= this.pageSize) { 51 | for (var i = 1; i < this.totalPages + 1; i++) { 52 | this.pages.push(i); 53 | } 54 | } 55 | return; 56 | } 57 | this.isVisible = false; 58 | }; 59 | PaginationComponent.prototype.previousNext = function (direction, event) { 60 | var page = this.currentPage; 61 | if (direction == -1) { 62 | if (page > 1) 63 | page--; 64 | } 65 | else { 66 | if (page < this.totalPages) 67 | page++; 68 | } 69 | this.changePage(page, event); 70 | }; 71 | PaginationComponent.prototype.changePage = function (page, event) { 72 | if (event) { 73 | event.preventDefault(); 74 | } 75 | if (this.currentPage === page) 76 | return; 77 | this.currentPage = page; 78 | this.previousEnabled = this.currentPage > 1; 79 | this.nextEnabled = this.currentPage < this.totalPages; 80 | this.pageChanged.emit(page); 81 | }; 82 | return PaginationComponent; 83 | }()); 84 | __decorate([ 85 | core_1.Input(), 86 | __metadata("design:type", Number), 87 | __metadata("design:paramtypes", [Number]) 88 | ], PaginationComponent.prototype, "pageSize", null); 89 | __decorate([ 90 | core_1.Input(), 91 | __metadata("design:type", Number), 92 | __metadata("design:paramtypes", [Number]) 93 | ], PaginationComponent.prototype, "totalItems", null); 94 | __decorate([ 95 | core_1.Output(), 96 | __metadata("design:type", core_1.EventEmitter) 97 | ], PaginationComponent.prototype, "pageChanged", void 0); 98 | PaginationComponent = __decorate([ 99 | core_1.Component({ 100 | moduleId: module.id, 101 | selector: 'pagination', 102 | templateUrl: 'pagination.component.html', 103 | styleUrls: ['pagination.component.css'] 104 | }), 105 | __metadata("design:paramtypes", []) 106 | ], PaginationComponent); 107 | exports.PaginationComponent = PaginationComponent; 108 | //# sourceMappingURL=pagination.component.js.map -------------------------------------------------------------------------------- /src/app/shared/pagination/pagination.component.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"pagination.component.js","sourceRoot":"","sources":["pagination.component.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /src/app/shared/pagination/pagination.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'pagination', 5 | templateUrl: './pagination.component.html', 6 | styleUrls: [ './pagination.component.css' ] 7 | }) 8 | 9 | export class PaginationComponent implements OnInit { 10 | 11 | private pagerTotalItems: number; 12 | private pagerPageSize: number; 13 | 14 | totalPages: number; 15 | pages: number[] = []; 16 | currentPage: number = 1; 17 | isVisible: boolean = false; 18 | previousEnabled: boolean = false; 19 | nextEnabled: boolean = true; 20 | 21 | @Input() get pageSize():number { 22 | return this.pagerPageSize; 23 | } 24 | 25 | set pageSize(size:number) { 26 | this.pagerPageSize = size; 27 | this.update(); 28 | } 29 | 30 | @Input() get totalItems():number { 31 | return this.pagerTotalItems; 32 | } 33 | 34 | set totalItems(itemCount:number) { 35 | this.pagerTotalItems = itemCount; 36 | this.update(); 37 | } 38 | 39 | @Output() pageChanged: EventEmitter = new EventEmitter(); 40 | 41 | constructor() { } 42 | 43 | ngOnInit() { 44 | 45 | } 46 | 47 | update() { 48 | if (this.pagerTotalItems && this.pagerPageSize) { 49 | this.totalPages = Math.ceil(this.pagerTotalItems/this.pageSize); 50 | this.isVisible = true; 51 | if (this.totalItems >= this.pageSize) { 52 | for (let i = 1;i < this.totalPages + 1;i++) { 53 | this.pages.push(i); 54 | } 55 | } 56 | return; 57 | } 58 | 59 | this.isVisible = false; 60 | } 61 | 62 | previousNext(direction: number, event?: MouseEvent) { 63 | let page: number = this.currentPage; 64 | if (direction == -1) { 65 | if (page > 1) page--; 66 | } else { 67 | if (page < this.totalPages) page++; 68 | } 69 | this.changePage(page, event); 70 | } 71 | 72 | changePage(page: number, event?: MouseEvent) { 73 | if (event) { 74 | event.preventDefault(); 75 | } 76 | if (this.currentPage === page) return; 77 | this.currentPage = page; 78 | this.previousEnabled = this.currentPage > 1; 79 | this.nextEnabled = this.currentPage < this.totalPages; 80 | this.pageChanged.emit(page); 81 | } 82 | 83 | } -------------------------------------------------------------------------------- /src/app/shared/pipes/capitalize.pipe.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | Object.defineProperty(exports, "__esModule", { value: true }); 9 | var core_1 = require("@angular/core"); 10 | var CapitalizePipe = (function () { 11 | function CapitalizePipe() { 12 | } 13 | CapitalizePipe.prototype.transform = function (value) { 14 | if (value) { 15 | return value.charAt(0).toUpperCase() + value.slice(1); 16 | } 17 | return value; 18 | }; 19 | return CapitalizePipe; 20 | }()); 21 | CapitalizePipe = __decorate([ 22 | core_1.Pipe({ name: 'capitalize' }) 23 | ], CapitalizePipe); 24 | exports.CapitalizePipe = CapitalizePipe; 25 | //# sourceMappingURL=capitalize.pipe.js.map -------------------------------------------------------------------------------- /src/app/shared/pipes/capitalize.pipe.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"capitalize.pipe.js","sourceRoot":"","sources":["capitalize.pipe.ts"],"names":[],"mappings":";;;;;;;;AAAA,sCAAoD;AAGpD,IAAa,cAAc;IAA3B;IASA,CAAC;IAPC,kCAAS,GAAT,UAAU,KAAU;QACnB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;YACR,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC1D,CAAC;QACD,MAAM,CAAC,KAAK,CAAC;IACd,CAAC;IAEH,qBAAC;AAAD,CAAC,AATD,IASC;AATY,cAAc;IAD1B,WAAI,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;GAChB,cAAc,CAS1B;AATY,wCAAc"} -------------------------------------------------------------------------------- /src/app/shared/pipes/capitalize.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ name: 'capitalize' }) 4 | export class CapitalizePipe implements PipeTransform { 5 | 6 | transform(value: any) { 7 | if (value) { 8 | return value.charAt(0).toUpperCase() + value.slice(1); 9 | } 10 | return value; 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /src/app/shared/pipes/trim.pipe.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | Object.defineProperty(exports, "__esModule", { value: true }); 9 | var core_1 = require("@angular/core"); 10 | var TrimPipe = (function () { 11 | function TrimPipe() { 12 | } 13 | TrimPipe.prototype.transform = function (value) { 14 | if (!value) { 15 | return ''; 16 | } 17 | return value.trim(); 18 | }; 19 | return TrimPipe; 20 | }()); 21 | TrimPipe = __decorate([ 22 | core_1.Pipe({ name: 'trim' }) 23 | ], TrimPipe); 24 | exports.TrimPipe = TrimPipe; 25 | //# sourceMappingURL=trim.pipe.js.map -------------------------------------------------------------------------------- /src/app/shared/pipes/trim.pipe.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"trim.pipe.js","sourceRoot":"","sources":["trim.pipe.ts"],"names":[],"mappings":";;;;;;;;AAAA,sCAAkD;AAGlD,IAAa,QAAQ;IAArB;IAOA,CAAC;IANC,4BAAS,GAAT,UAAU,KAAU;QAClB,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;YACX,MAAM,CAAC,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IACtB,CAAC;IACH,eAAC;AAAD,CAAC,AAPD,IAOC;AAPY,QAAQ;IADpB,WAAI,CAAC,EAAC,IAAI,EAAE,MAAM,EAAC,CAAC;GACR,QAAQ,CAOpB;AAPY,4BAAQ"} -------------------------------------------------------------------------------- /src/app/shared/pipes/trim.pipe.ts: -------------------------------------------------------------------------------- 1 | import {Pipe, PipeTransform} from '@angular/core'; 2 | 3 | @Pipe({name: 'trim'}) 4 | export class TrimPipe implements PipeTransform { 5 | transform(value: any) { 6 | if (!value) { 7 | return ''; 8 | } 9 | return value.trim(); 10 | } 11 | } -------------------------------------------------------------------------------- /src/app/shared/property-resolver.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var propertyResolver = (function () { 4 | function propertyResolver() { 5 | } 6 | propertyResolver.resolve = function (path, obj) { 7 | return path.split('.').reduce(function (prev, curr) { 8 | return (prev ? prev[curr] : undefined); 9 | }, obj || self); 10 | }; 11 | return propertyResolver; 12 | }()); 13 | exports.propertyResolver = propertyResolver; 14 | //# sourceMappingURL=property-resolver.js.map -------------------------------------------------------------------------------- /src/app/shared/property-resolver.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"property-resolver.js","sourceRoot":"","sources":["property-resolver.ts"],"names":[],"mappings":";;AAAA;IAAA;IAMA,CAAC;IALW,wBAAO,GAAd,UAAe,IAAY,EAAE,GAAQ;QACpC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,UAAC,IAAI,EAAE,IAAI;YACrC,MAAM,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAA;QAC1C,CAAC,EAAE,GAAG,IAAI,IAAI,CAAC,CAAA;IACjB,CAAC;IACL,uBAAC;AAAD,CAAC,AAND,IAMC;AANY,4CAAgB"} -------------------------------------------------------------------------------- /src/app/shared/property-resolver.ts: -------------------------------------------------------------------------------- 1 | export class propertyResolver { 2 | static resolve(path: string, obj: any) { 3 | return path.split('.').reduce((prev, curr) => { 4 | return (prev ? prev[curr] : undefined) 5 | }, obj || self) 6 | } 7 | } -------------------------------------------------------------------------------- /src/app/shared/shared.module.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | Object.defineProperty(exports, "__esModule", { value: true }); 9 | var core_1 = require("@angular/core"); 10 | var common_1 = require("@angular/common"); 11 | var forms_1 = require("@angular/forms"); 12 | var pagination_component_1 = require("./pagination/pagination.component"); 13 | var capitalize_pipe_1 = require("./pipes/capitalize.pipe"); 14 | var trim_pipe_1 = require("./pipes/trim.pipe"); 15 | var filter_textbox_component_1 = require("./filter-textbox/filter-textbox.component"); 16 | var SharedModule = (function () { 17 | function SharedModule() { 18 | } 19 | return SharedModule; 20 | }()); 21 | SharedModule = __decorate([ 22 | core_1.NgModule({ 23 | imports: [common_1.CommonModule, forms_1.FormsModule, forms_1.ReactiveFormsModule], 24 | declarations: [capitalize_pipe_1.CapitalizePipe, trim_pipe_1.TrimPipe, filter_textbox_component_1.FilterTextboxComponent, pagination_component_1.PaginationComponent], 25 | exports: [common_1.CommonModule, forms_1.FormsModule, forms_1.ReactiveFormsModule, capitalize_pipe_1.CapitalizePipe, trim_pipe_1.TrimPipe, filter_textbox_component_1.FilterTextboxComponent, pagination_component_1.PaginationComponent] 26 | }) 27 | ], SharedModule); 28 | exports.SharedModule = SharedModule; 29 | //# sourceMappingURL=shared.module.js.map -------------------------------------------------------------------------------- /src/app/shared/shared.module.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"shared.module.js","sourceRoot":"","sources":["shared.module.ts"],"names":[],"mappings":";;;;;;;;AAAA,sCAAyC;AACzC,0CAA+C;AAC/C,wCAAkE;AAElE,0EAAwE;AACxE,2DAAyD;AACzD,+CAA6C;AAC7C,sFAAmF;AAOnF,IAAa,YAAY;IAAzB;IAA4B,CAAC;IAAD,mBAAC;AAAD,CAAC,AAA7B,IAA6B;AAAhB,YAAY;IALxB,eAAQ,CAAC;QACR,OAAO,EAAE,CAAE,qBAAY,EAAE,mBAAW,EAAE,2BAAmB,CAAE;QAC3D,YAAY,EAAE,CAAC,gCAAc,EAAE,oBAAQ,EAAE,iDAAsB,EAAE,0CAAmB,CAAE;QACtF,OAAO,EAAE,CAAE,qBAAY,EAAE,mBAAW,EAAE,2BAAmB,EAAE,gCAAc,EAAE,oBAAQ,EAAE,iDAAsB,EAAE,0CAAmB,CAAE;KACnI,CAAC;GACW,YAAY,CAAI;AAAhB,oCAAY"} -------------------------------------------------------------------------------- /src/app/shared/shared.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | 5 | import { PaginationComponent } from './pagination/pagination.component'; 6 | import { CapitalizePipe } from './pipes/capitalize.pipe'; 7 | import { TrimPipe } from './pipes/trim.pipe'; 8 | import { FilterTextboxComponent } from './filter-textbox/filter-textbox.component'; 9 | 10 | @NgModule({ 11 | imports: [ CommonModule, FormsModule, ReactiveFormsModule ], 12 | declarations: [CapitalizePipe, TrimPipe, FilterTextboxComponent, PaginationComponent ], 13 | exports: [ CommonModule, FormsModule, ReactiveFormsModule, CapitalizePipe, TrimPipe, FilterTextboxComponent, PaginationComponent ] 14 | }) 15 | export class SharedModule { } 16 | -------------------------------------------------------------------------------- /src/app/shared/validation.service.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var ValidationService = (function () { 4 | function ValidationService() { 5 | } 6 | ValidationService.getValidatorErrorMessage = function (code) { 7 | var config = { 8 | 'required': 'Required', 9 | 'invalidCreditCard': 'Is invalid credit card number', 10 | 'invalidEmailAddress': 'Invalid email address', 11 | 'invalidPassword': 'Invalid password. Password must be at least 6 characters long, and contain a number.' 12 | }; 13 | return config[code]; 14 | }; 15 | ValidationService.creditCardValidator = function (control) { 16 | // Visa, MasterCard, American Express, Diners Club, Discover, JCB 17 | if (control.value.match(/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/)) { 18 | return null; 19 | } 20 | else { 21 | return { 'invalidCreditCard': true }; 22 | } 23 | }; 24 | ValidationService.emailValidator = function (control) { 25 | // RFC 2822 compliant regex 26 | if (control.value.match(/[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/)) { 27 | return null; 28 | } 29 | else { 30 | return { 'invalidEmailAddress': true }; 31 | } 32 | }; 33 | ValidationService.passwordValidator = function (control) { 34 | // {6,100} - Assert password is between 6 and 100 characters 35 | // (?=.*[0-9]) - Assert a string has at least one number 36 | if (control.value.match(/^(?=.*[0-9])[a-zA-Z0-9!@#$%^&*]{6,100}$/)) { 37 | return null; 38 | } 39 | else { 40 | return { 'invalidPassword': true }; 41 | } 42 | }; 43 | return ValidationService; 44 | }()); 45 | exports.ValidationService = ValidationService; 46 | //# sourceMappingURL=validation.service.js.map -------------------------------------------------------------------------------- /src/app/shared/validation.service.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"validation.service.js","sourceRoot":"","sources":["validation.service.ts"],"names":[],"mappings":";;AAGA;IAAA;IAuCA,CAAC;IArCU,0CAAwB,GAA/B,UAAgC,IAAY;QACxC,IAAI,MAAM,GAAG;YACT,UAAU,EAAE,UAAU;YACtB,mBAAmB,EAAE,+BAA+B;YACpD,qBAAqB,EAAE,uBAAuB;YAC9C,iBAAiB,EAAE,sFAAsF;SAC5G,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IAEM,qCAAmB,GAA1B,UAA2B,OAAwB;QAC/C,iEAAiE;QACjE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,uJAAuJ,CAAC,CAAC,CAAC,CAAC;YAC/K,MAAM,CAAC,IAAI,CAAC;QAChB,CAAC;QAAC,IAAI,CAAC,CAAC;YACJ,MAAM,CAAC,EAAE,mBAAmB,EAAE,IAAI,EAAE,CAAC;QACzC,CAAC;IACL,CAAC;IAEM,gCAAc,GAArB,UAAsB,OAAwB;QAC1C,2BAA2B;QAC3B,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,uIAAuI,CAAC,CAAC,CAAC,CAAC;YAC/J,MAAM,CAAC,IAAI,CAAC;QAChB,CAAC;QAAC,IAAI,CAAC,CAAC;YACJ,MAAM,CAAC,EAAE,qBAAqB,EAAE,IAAI,EAAE,CAAC;QAC3C,CAAC;IACL,CAAC;IAEM,mCAAiB,GAAxB,UAAyB,OAAwB;QAC7C,sEAAsE;QACtE,8DAA8D;QAC9D,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC,CAAC,CAAC;YACjE,MAAM,CAAC,IAAI,CAAC;QAChB,CAAC;QAAC,IAAI,CAAC,CAAC;YACJ,MAAM,CAAC,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC;QACvC,CAAC;IACL,CAAC;IACL,wBAAC;AAAD,CAAC,AAvCD,IAuCC;AAvCY,8CAAiB"} -------------------------------------------------------------------------------- /src/app/shared/validation.service.ts: -------------------------------------------------------------------------------- 1 | //Original version created by Cory Rylan: https://coryrylan.com/blog/angular-2-form-builder-and-validation-management 2 | import { AbstractControl } from '@angular/forms'; 3 | 4 | export class ValidationService { 5 | 6 | static getValidatorErrorMessage(code: string) { 7 | let config = { 8 | 'required': 'Required', 9 | 'invalidCreditCard': 'Is invalid credit card number', 10 | 'invalidEmailAddress': 'Invalid email address', 11 | 'invalidPassword': 'Invalid password. Password must be at least 6 characters long, and contain a number.' 12 | }; 13 | return config[code]; 14 | } 15 | 16 | static creditCardValidator(control: AbstractControl) { 17 | // Visa, MasterCard, American Express, Diners Club, Discover, JCB 18 | if (control.value.match(/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/)) { 19 | return null; 20 | } else { 21 | return { 'invalidCreditCard': true }; 22 | } 23 | } 24 | 25 | static emailValidator(control: AbstractControl) { 26 | // RFC 2822 compliant regex 27 | if (control.value.match(/[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/)) { 28 | return null; 29 | } else { 30 | return { 'invalidEmailAddress': true }; 31 | } 32 | } 33 | 34 | static passwordValidator(control: AbstractControl) { 35 | // {6,100} - Assert password is between 6 and 100 characters 36 | // (?=.*[0-9]) - Assert a string has at least one number 37 | if (control.value.match(/^(?=.*[0-9])[a-zA-Z0-9!@#$%^&*]{6,100}$/)) { 38 | return null; 39 | } else { 40 | return { 'invalidPassword': true }; 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanWahlin/Angular-Docker-Microservices/b5daf9dfb2f3028e7cf95afec84d1b6103e4d550/src/assets/images/favicon.ico -------------------------------------------------------------------------------- /src/assets/images/female.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanWahlin/Angular-Docker-Microservices/b5daf9dfb2f3028e7cf95afec84d1b6103e4d550/src/assets/images/female.png -------------------------------------------------------------------------------- /src/assets/images/male.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanWahlin/Angular-Docker-Microservices/b5daf9dfb2f3028e7cf95afec84d1b6103e4d550/src/assets/images/male.png -------------------------------------------------------------------------------- /src/assets/images/people.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanWahlin/Angular-Docker-Microservices/b5daf9dfb2f3028e7cf95afec84d1b6103e4d550/src/assets/images/people.png -------------------------------------------------------------------------------- /src/assets/styles/styles.css: -------------------------------------------------------------------------------- 1 | html { 2 | overflow-y: scroll; 3 | overflow-x: hidden; 4 | } 5 | 6 | body { 7 | font-family: 'Open Sans' 8 | } 9 | 10 | main { 11 | position: relative; 12 | padding-top: 60px; 13 | } 14 | 15 | /* Ensure display:flex and others don't override a [hidden] */ 16 | [hidden] { display: none !important; } 17 | 18 | .white { 19 | color: white; 20 | } 21 | 22 | .white:hover{ 23 | color: white; 24 | } 25 | 26 | th { 27 | cursor: pointer; 28 | } 29 | 30 | .navbar-header { 31 | width: 300px; 32 | } 33 | 34 | .nav.navbar-padding { 35 | margin-left:25px; 36 | margin-top: 10px; 37 | } 38 | 39 | .app-title { 40 | line-height:50px; 41 | font-size:20px; 42 | color: white; 43 | } 44 | 45 | .navbar .nav > li.toolbar-item > a { 46 | color: #9E9E9E; 47 | font-weight:bold; 48 | -webkit-text-shadow: none; 49 | text-shadow: none; 50 | } 51 | 52 | .navbar .nav > .toolbar-item > a.active { 53 | color: #000; 54 | } 55 | 56 | .toolbar-item a { 57 | cursor: pointer; 58 | } 59 | 60 | .view { 61 | 62 | } 63 | 64 | .indent { 65 | margin-left:5px; 66 | } 67 | 68 | .card-container { 69 | width:85%; 70 | } 71 | 72 | .card { 73 | background-color:#fff; 74 | border: 1px solid #d4d4d4; 75 | height:100px; 76 | margin-bottom: 20px; 77 | position: relative; 78 | } 79 | 80 | .card-header { 81 | background-color:#027FF4; 82 | font-size:14pt; 83 | color:white; 84 | padding:5px; 85 | width:100%; 86 | } 87 | 88 | .card-close { 89 | color: white; 90 | font-weight:bold; 91 | margin-right:5px; 92 | } 93 | 94 | .card-body { 95 | padding-left: 5px; 96 | } 97 | 98 | .card-body-left { 99 | margin-top: -5px; 100 | } 101 | 102 | .card-body-right { 103 | margin-left: 20px; 104 | margin-top: 2px; 105 | } 106 | 107 | .card-body-content { 108 | width: 100px; 109 | } 110 | 111 | .card-image { 112 | height:50px;width:50px;margin-top:10px; 113 | } 114 | 115 | .grid-container div { 116 | padding-left: 0px; 117 | } 118 | 119 | .grid-container td { 120 | vertical-align: middle; 121 | } 122 | 123 | .navbar-brand { 124 | float:none; 125 | } 126 | 127 | a.navbar-brand { 128 | color: #fff; 129 | } 130 | 131 | .navbar-inner { 132 | padding-left: 0px; 133 | -webkit-border-radius: 0px; 134 | border-radius: 0px; 135 | -webkit-box-shadow: none; 136 | -moz-box-shadow: none; 137 | box-shadow: none; 138 | background-color: #027FF4; 139 | background-image: none; 140 | } 141 | 142 | .navbar-inner.toolbar { 143 | background-color: #fafafa; 144 | } 145 | 146 | footer { 147 | margin-top: 10px; 148 | } 149 | 150 | .navbar-inner.footer { 151 | background-color: #fafafa; 152 | -webkit-box-shadow: none; 153 | -moz-box-shadow: none; 154 | box-shadow: none; 155 | height:50px; 156 | } 157 | 158 | .navbar .nav > .active > a, .navbar .nav > .active > a:hover, .navbar .nav > .active > a:focus { 159 | background-color: #efefef; 160 | -webkit-box-shadow: none; 161 | box-shadow: none; 162 | color: #808080; 163 | } 164 | 165 | .navbar .nav li.toolbaritem a:hover, .navbar .nav li a:hover { 166 | color: #E03930; 167 | } 168 | 169 | .navbar .nav > li { 170 | cursor:pointer; 171 | } 172 | 173 | .navbar .nav > li > a { 174 | color: white; 175 | font-weight:bold; 176 | -webkit-text-shadow: none; 177 | text-shadow: none; 178 | height:30px; 179 | padding-top: 6px; 180 | padding-bottom: 0px; 181 | } 182 | 183 | .navbar .nav > li.toolbaritem > a { 184 | color: black; 185 | font-weight:bold; 186 | -webkit-text-shadow: none; 187 | text-shadow: none; 188 | } 189 | 190 | 191 | .navbar-fixed-top .navbar-inner, 192 | .navbar-static-top .navbar-inner { 193 | -webkit-box-shadow: 0 1px 00px rgba(0, 0, 0, 0); 194 | -moz-box-shadow: 0 1px 00px rgba(0, 0, 0, 0); 195 | box-shadow: 0 1px 00px rgba(0, 0, 0, 0); 196 | } 197 | 198 | .nav.navBarPadding { 199 | margin-left:25px; 200 | margin-top: 10px; 201 | } 202 | 203 | .navbarText { 204 | font-weight:bold; 205 | } 206 | 207 | .navbar .brand { 208 | margin-top: 2px; 209 | color: #fff; 210 | -webkit-text-shadow: none; 211 | text-shadow: none; 212 | } 213 | 214 | .navbar-toggle { 215 | border: 1px solid white; 216 | } 217 | 218 | .navbar-toggle .icon-bar { 219 | background-color: white; 220 | } 221 | 222 | footer { 223 | margin-top: 15px; 224 | } 225 | 226 | .navbar-inner.footer { 227 | background-color: #fafafa; 228 | -webkit-box-shadow: none; 229 | -moz-box-shadow: none; 230 | box-shadow: none; 231 | height:50px; 232 | } 233 | 234 | filter-textbox { 235 | margin-top: 5px; 236 | } 237 | 238 | form { 239 | width:50%; 240 | } 241 | 242 | .editForm .ng-invalid { 243 | border-left: 5px solid #a94442; 244 | } 245 | 246 | .editForm .ng-valid { 247 | border-left: 5px solid #42A948; 248 | } 249 | 250 | -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanWahlin/Angular-Docker-Microservices/b5daf9dfb2f3028e7cf95afec84d1b6103e4d550/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Angular-Docker Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 29 | 30 |
31 | 32 | Loading... 33 | 34 |

35 |
36 | 37 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/set'; 35 | 36 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 37 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 38 | 39 | /** IE10 and IE11 requires the following to support `@angular/animation`. */ 40 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 41 | 42 | 43 | /** Evergreen browsers require these. **/ 44 | import 'core-js/es6/reflect'; 45 | import 'core-js/es7/reflect'; 46 | 47 | 48 | /** ALL Firefox browsers require the following to support `@angular/animation`. **/ 49 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 50 | 51 | 52 | 53 | /*************************************************************************************************** 54 | * Zone JS is required by Angular itself. 55 | */ 56 | import 'zone.js/dist/zone'; // Included with Angular CLI. 57 | 58 | 59 | 60 | /*************************************************************************************************** 61 | * APPLICATION IMPORTS 62 | */ 63 | 64 | /** 65 | * Date, currency, decimal and percent pipes. 66 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 67 | */ 68 | // import 'intl'; // Run `npm install --save intl`. 69 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/long-stack-trace-zone'; 4 | import 'zone.js/dist/proxy.js'; 5 | import 'zone.js/dist/sync-test'; 6 | import 'zone.js/dist/jasmine-patch'; 7 | import 'zone.js/dist/async-test'; 8 | import 'zone.js/dist/fake-async-test'; 9 | import { getTestBed } from '@angular/core/testing'; 10 | import { 11 | BrowserDynamicTestingModule, 12 | platformBrowserDynamicTesting 13 | } from '@angular/platform-browser-dynamic/testing'; 14 | 15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. 16 | declare var __karma__: any; 17 | declare var require: any; 18 | 19 | // Prevent Karma from running prematurely. 20 | __karma__.loaded = function () {}; 21 | 22 | // First, initialize the Angular testing environment. 23 | getTestBed().initTestEnvironment( 24 | BrowserDynamicTestingModule, 25 | platformBrowserDynamicTesting() 26 | ); 27 | // Then we find all the tests. 28 | const context = require.context('./', true, /\.spec\.ts$/); 29 | // And load the modules. 30 | context.keys().map(context); 31 | // Finally, start Karma to run the tests. 32 | __karma__.start(); 33 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "es2015", 6 | "baseUrl": "", 7 | "types": [] 8 | }, 9 | "exclude": [ 10 | "test.ts", 11 | "**/*.spec.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "baseUrl": "", 8 | "types": [ 9 | "jasmine", 10 | "node" 11 | ] 12 | }, 13 | "files": [ 14 | "test.ts", 15 | "polyfills.ts" 16 | ], 17 | "include": [ 18 | "**/*.spec.ts", 19 | "**/*.d.ts" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /src/typings.d.ts: -------------------------------------------------------------------------------- 1 | /* SystemJS module definition */ 2 | declare var module: NodeModule; 3 | interface NodeModule { 4 | id: string; 5 | } 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "outDir": "./dist/out-tsc", 5 | "baseUrl": "src", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "moduleResolution": "node", 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "target": "es5", 12 | "typeRoots": [ 13 | "node_modules/@types" 14 | ], 15 | "lib": [ 16 | "es2016", 17 | "dom" 18 | ] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "callable-types": true, 7 | "class-name": true, 8 | "comment-format": [ 9 | true, 10 | "check-space" 11 | ], 12 | "curly": true, 13 | "eofline": true, 14 | "forin": true, 15 | "import-blacklist": [true], 16 | "import-spacing": true, 17 | "indent": [ 18 | true, 19 | "spaces" 20 | ], 21 | "interface-over-type-literal": true, 22 | "label-position": true, 23 | "max-line-length": [ 24 | true, 25 | 140 26 | ], 27 | "member-access": false, 28 | "member-ordering": [ 29 | true, 30 | "static-before-instance", 31 | "variables-before-functions" 32 | ], 33 | "no-arg": true, 34 | "no-bitwise": true, 35 | "no-console": [ 36 | true, 37 | "debug", 38 | "info", 39 | "time", 40 | "timeEnd", 41 | "trace" 42 | ], 43 | "no-construct": true, 44 | "no-debugger": true, 45 | "no-duplicate-variable": true, 46 | "no-empty": false, 47 | "no-empty-interface": true, 48 | "no-eval": true, 49 | "no-inferrable-types": [true, "ignore-params"], 50 | "no-shadowed-variable": true, 51 | "no-string-literal": false, 52 | "no-string-throw": true, 53 | "no-switch-case-fall-through": true, 54 | "no-trailing-whitespace": true, 55 | "no-unused-expression": true, 56 | "no-use-before-declare": true, 57 | "no-var-keyword": true, 58 | "object-literal-sort-keys": false, 59 | "one-line": [ 60 | true, 61 | "check-open-brace", 62 | "check-catch", 63 | "check-else", 64 | "check-whitespace" 65 | ], 66 | "prefer-const": true, 67 | "quotemark": [ 68 | true, 69 | "single" 70 | ], 71 | "radix": true, 72 | "semicolon": [ 73 | "always" 74 | ], 75 | "triple-equals": [ 76 | true, 77 | "allow-null-check" 78 | ], 79 | "typedef-whitespace": [ 80 | true, 81 | { 82 | "call-signature": "nospace", 83 | "index-signature": "nospace", 84 | "parameter": "nospace", 85 | "property-declaration": "nospace", 86 | "variable-declaration": "nospace" 87 | } 88 | ], 89 | "typeof-compare": true, 90 | "unified-signatures": true, 91 | "variable-name": false, 92 | "whitespace": [ 93 | true, 94 | "check-branch", 95 | "check-decl", 96 | "check-operator", 97 | "check-separator", 98 | "check-type" 99 | ], 100 | 101 | "directive-selector": [true, "attribute", "app", "camelCase"], 102 | "component-selector": [true, "element", "app", "kebab-case"], 103 | "use-input-property-decorator": true, 104 | "use-output-property-decorator": true, 105 | "use-host-property-decorator": true, 106 | "no-input-rename": true, 107 | "no-output-rename": true, 108 | "use-life-cycle-interface": true, 109 | "use-pipe-transform-interface": true, 110 | "component-class-suffix": true, 111 | "directive-class-suffix": true, 112 | "no-access-missing-member": true, 113 | "templates-use-public": true, 114 | "invoke-injectable": true 115 | } 116 | } 117 | --------------------------------------------------------------------------------