├── .editorconfig ├── .gitignore ├── README.md ├── _config.yml ├── angular.json ├── e2e ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts └── tsconfig.e2e.json ├── package-lock.json ├── package.json ├── src ├── app │ ├── app-routing.module.ts │ ├── app.component.css │ ├── app.component.html │ ├── app.component.ts │ ├── app.module.ts │ ├── core │ │ ├── core.module.ts │ │ ├── exception.service.ts │ │ ├── footer │ │ │ ├── footer.component.css │ │ │ ├── footer.component.html │ │ │ └── footer.component.ts │ │ ├── logger.service.ts │ │ └── navbar │ │ │ ├── navbar.component.css │ │ │ ├── navbar.component.html │ │ │ └── navbar.component.ts │ ├── dashboard │ │ ├── dashboard.component.css │ │ ├── dashboard.component.html │ │ ├── dashboard.component.spec.ts │ │ └── dashboard.component.ts │ ├── shared │ │ ├── models │ │ │ └── tasks.ts │ │ ├── pipes │ │ │ └── file-size.pipe.ts │ │ ├── services │ │ │ └── resource │ │ │ │ └── resource.service.ts │ │ └── shared.module.ts │ └── task │ │ ├── schedule │ │ ├── schedule.component.css │ │ ├── schedule.component.html │ │ └── schedule.component.ts │ │ ├── shared │ │ ├── task.json │ │ └── task.service.ts │ │ ├── task-routing.module.ts │ │ ├── task.module.ts │ │ ├── tasks │ │ ├── tasks.component.css │ │ ├── tasks.component.html │ │ └── tasks.component.ts │ │ └── threshold │ │ ├── threshold.component.css │ │ ├── threshold.component.html │ │ └── threshold.component.ts ├── browserslist ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── karma.conf.js ├── main.ts ├── polyfills.ts ├── styles.css ├── test.ts ├── tsconfig.app.json ├── tsconfig.spec.json └── tslint.json ├── tsconfig.json └── tslint.json /.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 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | testem.log 34 | /typings 35 | 36 | # e2e 37 | /e2e/*.js 38 | /e2e/*.map 39 | 40 | # System Files 41 | .DS_Store 42 | Thumbs.db 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular-6-Architecture 2 | Robust architecture of an Angular 6 app 3 | 4 | > An Angular project 5 | 6 | ## Pre-requisites 7 | 8 | 1. Node version should be greater than 8.9 9 | 2. Install Angular CLI from https://angular.io/guide/quickstart 10 | 11 | ## Build Setup 12 | 13 | ``` bash 14 | # install dependencies 15 | npm install 16 | 17 | # serve at localhost:4200 18 | ng serve 19 | 20 | # serve at localhost:4200 and open it in browser 21 | ng serve --open 22 | ``` 23 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular-devkit/core/src/workspace/workspace-schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "angular.io-example": { 7 | "root": "", 8 | "projectType": "application", 9 | "prefix": "app", 10 | "architect": { 11 | "build": { 12 | "builder": "@angular-devkit/build-angular:browser", 13 | "options": { 14 | "outputPath": "dist/angular.io-example", 15 | "index": "src/index.html", 16 | "main": "src/main.ts", 17 | "polyfills": "src/polyfills.ts", 18 | "tsConfig": "src/tsconfig.app.json", 19 | "assets": [ 20 | { 21 | "glob": "favicon.ico", 22 | "input": "src", 23 | "output": "/" 24 | }, 25 | { 26 | "glob": "**/*", 27 | "input": "src/assets", 28 | "output": "/assets" 29 | } 30 | ], 31 | "styles": [ 32 | { 33 | "input": "node_modules/@angular/material/prebuilt-themes/indigo-pink.css" 34 | }, 35 | { 36 | "input": "src/styles.css" 37 | }, 38 | { 39 | "input": "./node_modules/bootstrap/dist/css/bootstrap.min.css" 40 | } 41 | ], 42 | "scripts": [] 43 | }, 44 | "configurations": { 45 | "production": { 46 | "fileReplacements": [ 47 | { 48 | "replace": "src/environments/environment.ts", 49 | "with": "src/environments/environment.prod.ts" 50 | } 51 | ], 52 | "optimization": true, 53 | "outputHashing": "all", 54 | "sourceMap": false, 55 | "extractCss": true, 56 | "namedChunks": false, 57 | "aot": true, 58 | "extractLicenses": true, 59 | "vendorChunk": false, 60 | "buildOptimizer": true 61 | } 62 | } 63 | }, 64 | "serve": { 65 | "builder": "@angular-devkit/build-angular:dev-server", 66 | "options": { 67 | "browserTarget": "angular.io-example:build" 68 | }, 69 | "configurations": { 70 | "production": { 71 | "browserTarget": "angular.io-example:build:production" 72 | } 73 | } 74 | }, 75 | "extract-i18n": { 76 | "builder": "@angular-devkit/build-angular:extract-i18n", 77 | "options": { 78 | "browserTarget": "angular.io-example:build" 79 | } 80 | }, 81 | "test": { 82 | "builder": "@angular-devkit/build-angular:karma", 83 | "options": { 84 | "main": "src/test.ts", 85 | "polyfills": "src/polyfills.ts", 86 | "tsConfig": "src/tsconfig.spec.json", 87 | "karmaConfig": "src/karma.conf.js", 88 | "styles": [ 89 | { 90 | "input": "node_modules/@angular/material/prebuilt-themes/indigo-pink.css" 91 | }, 92 | { 93 | "input": "styles.css" 94 | } 95 | ], 96 | "scripts": [], 97 | "assets": [ 98 | { 99 | "glob": "favicon.ico", 100 | "input": "src/", 101 | "output": "/" 102 | }, 103 | { 104 | "glob": "**/*", 105 | "input": "src/assets", 106 | "output": "/assets" 107 | } 108 | ] 109 | } 110 | }, 111 | "lint": { 112 | "builder": "@angular-devkit/build-angular:tslint", 113 | "options": { 114 | "tsConfig": [ 115 | "src/tsconfig.app.json", 116 | "src/tsconfig.spec.json" 117 | ], 118 | "exclude": [ 119 | "**/node_modules/**" 120 | ] 121 | } 122 | } 123 | } 124 | }, 125 | "angular.io-example-e2e": { 126 | "root": "e2e/", 127 | "projectType": "application", 128 | "architect": { 129 | "e2e": { 130 | "builder": "@angular-devkit/build-angular:protractor", 131 | "options": { 132 | "protractorConfig": "e2e/protractor.conf.js", 133 | "devServerTarget": "angular.io-example:serve" 134 | } 135 | }, 136 | "lint": { 137 | "builder": "@angular-devkit/build-angular:tslint", 138 | "options": { 139 | "tsConfig": "e2e/tsconfig.e2e.json", 140 | "exclude": [ 141 | "**/node_modules/**" 142 | ] 143 | } 144 | } 145 | } 146 | } 147 | } 148 | } -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; // necessary for es6 output in node 2 | 3 | import { browser, element, by, ElementFinder, ElementArrayFinder } from 'protractor'; 4 | import { promise } from 'selenium-webdriver'; 5 | 6 | const expectedH1 = 'Tour of Heroes'; 7 | const expectedTitle = `${expectedH1}`; 8 | const targetHero = { id: 15, name: 'Magneta' }; 9 | const targetHeroDashboardIndex = 3; 10 | const nameSuffix = 'X'; 11 | const newHeroName = targetHero.name + nameSuffix; 12 | 13 | class Hero { 14 | id: number; 15 | name: string; 16 | 17 | // Factory methods 18 | 19 | // Hero from string formatted as ' '. 20 | static fromString(s: string): Hero { 21 | return { 22 | id: +s.substr(0, s.indexOf(' ')), 23 | name: s.substr(s.indexOf(' ') + 1), 24 | }; 25 | } 26 | 27 | // Hero from hero list
  • element. 28 | static async fromLi(li: ElementFinder): Promise { 29 | let stringsFromA = await li.all(by.css('a')).getText(); 30 | let strings = stringsFromA[0].split(' '); 31 | return { id: +strings[0], name: strings[1] }; 32 | } 33 | 34 | // Hero id and name from the given detail element. 35 | static async fromDetail(detail: ElementFinder): Promise { 36 | // Get hero id from the first
    37 | let _id = await detail.all(by.css('div')).first().getText(); 38 | // Get name from the h2 39 | let _name = await detail.element(by.css('h2')).getText(); 40 | return { 41 | id: +_id.substr(_id.indexOf(' ') + 1), 42 | name: _name.substr(0, _name.lastIndexOf(' ')) 43 | }; 44 | } 45 | } 46 | 47 | describe('Tutorial part 6', () => { 48 | 49 | beforeAll(() => browser.get('')); 50 | 51 | function getPageElts() { 52 | let navElts = element.all(by.css('app-root nav a')); 53 | 54 | return { 55 | navElts: navElts, 56 | 57 | appDashboardHref: navElts.get(0), 58 | appDashboard: element(by.css('app-root app-dashboard')), 59 | topHeroes: element.all(by.css('app-root app-dashboard > div h4')), 60 | 61 | appHeroesHref: navElts.get(1), 62 | appHeroes: element(by.css('app-root app-heroes')), 63 | allHeroes: element.all(by.css('app-root app-heroes li')), 64 | selectedHeroSubview: element(by.css('app-root app-heroes > div:last-child')), 65 | 66 | heroDetail: element(by.css('app-root app-hero-detail > div')), 67 | 68 | searchBox: element(by.css('#search-box')), 69 | searchResults: element.all(by.css('.search-result li')) 70 | }; 71 | } 72 | 73 | describe('Initial page', () => { 74 | 75 | it(`has title '${expectedTitle}'`, () => { 76 | expect(browser.getTitle()).toEqual(expectedTitle); 77 | }); 78 | 79 | it(`has h1 '${expectedH1}'`, () => { 80 | expectHeading(1, expectedH1); 81 | }); 82 | 83 | const expectedViewNames = ['Dashboard', 'Heroes']; 84 | it(`has views ${expectedViewNames}`, () => { 85 | let viewNames = getPageElts().navElts.map((el: ElementFinder) => el.getText()); 86 | expect(viewNames).toEqual(expectedViewNames); 87 | }); 88 | 89 | it('has dashboard as the active view', () => { 90 | let page = getPageElts(); 91 | expect(page.appDashboard.isPresent()).toBeTruthy(); 92 | }); 93 | 94 | }); 95 | 96 | describe('Dashboard tests', () => { 97 | 98 | beforeAll(() => browser.get('')); 99 | 100 | it('has top heroes', () => { 101 | let page = getPageElts(); 102 | expect(page.topHeroes.count()).toEqual(4); 103 | }); 104 | 105 | it(`selects and routes to ${targetHero.name} details`, dashboardSelectTargetHero); 106 | 107 | it(`updates hero name (${newHeroName}) in details view`, updateHeroNameInDetailView); 108 | 109 | it(`cancels and shows ${targetHero.name} in Dashboard`, () => { 110 | element(by.buttonText('go back')).click(); 111 | browser.waitForAngular(); // seems necessary to gets tests to pass for toh-pt6 112 | 113 | let targetHeroElt = getPageElts().topHeroes.get(targetHeroDashboardIndex); 114 | expect(targetHeroElt.getText()).toEqual(targetHero.name); 115 | }); 116 | 117 | it(`selects and routes to ${targetHero.name} details`, dashboardSelectTargetHero); 118 | 119 | it(`updates hero name (${newHeroName}) in details view`, updateHeroNameInDetailView); 120 | 121 | it(`saves and shows ${newHeroName} in Dashboard`, () => { 122 | element(by.buttonText('save')).click(); 123 | browser.waitForAngular(); // seems necessary to gets tests to pass for toh-pt6 124 | 125 | let targetHeroElt = getPageElts().topHeroes.get(targetHeroDashboardIndex); 126 | expect(targetHeroElt.getText()).toEqual(newHeroName); 127 | }); 128 | 129 | }); 130 | 131 | describe('Heroes tests', () => { 132 | 133 | beforeAll(() => browser.get('')); 134 | 135 | it('can switch to Heroes view', () => { 136 | getPageElts().appHeroesHref.click(); 137 | let page = getPageElts(); 138 | expect(page.appHeroes.isPresent()).toBeTruthy(); 139 | expect(page.allHeroes.count()).toEqual(10, 'number of heroes'); 140 | }); 141 | 142 | it('can route to hero details', async () => { 143 | getHeroLiEltById(targetHero.id).click(); 144 | 145 | let page = getPageElts(); 146 | expect(page.heroDetail.isPresent()).toBeTruthy('shows hero detail'); 147 | let hero = await Hero.fromDetail(page.heroDetail); 148 | expect(hero.id).toEqual(targetHero.id); 149 | expect(hero.name).toEqual(targetHero.name.toUpperCase()); 150 | }); 151 | 152 | it(`updates hero name (${newHeroName}) in details view`, updateHeroNameInDetailView); 153 | 154 | it(`shows ${newHeroName} in Heroes list`, () => { 155 | element(by.buttonText('save')).click(); 156 | browser.waitForAngular(); 157 | let expectedText = `${targetHero.id} ${newHeroName}`; 158 | expect(getHeroAEltById(targetHero.id).getText()).toEqual(expectedText); 159 | }); 160 | 161 | it(`deletes ${newHeroName} from Heroes list`, async () => { 162 | const heroesBefore = await toHeroArray(getPageElts().allHeroes); 163 | const li = getHeroLiEltById(targetHero.id); 164 | li.element(by.buttonText('x')).click(); 165 | 166 | const page = getPageElts(); 167 | expect(page.appHeroes.isPresent()).toBeTruthy(); 168 | expect(page.allHeroes.count()).toEqual(9, 'number of heroes'); 169 | const heroesAfter = await toHeroArray(page.allHeroes); 170 | // console.log(await Hero.fromLi(page.allHeroes[0])); 171 | const expectedHeroes = heroesBefore.filter(h => h.name !== newHeroName); 172 | expect(heroesAfter).toEqual(expectedHeroes); 173 | // expect(page.selectedHeroSubview.isPresent()).toBeFalsy(); 174 | }); 175 | 176 | it(`adds back ${targetHero.name}`, async () => { 177 | const newHeroName = 'Alice'; 178 | const heroesBefore = await toHeroArray(getPageElts().allHeroes); 179 | const numHeroes = heroesBefore.length; 180 | 181 | element(by.css('input')).sendKeys(newHeroName); 182 | element(by.buttonText('add')).click(); 183 | 184 | let page = getPageElts(); 185 | let heroesAfter = await toHeroArray(page.allHeroes); 186 | expect(heroesAfter.length).toEqual(numHeroes + 1, 'number of heroes'); 187 | 188 | expect(heroesAfter.slice(0, numHeroes)).toEqual(heroesBefore, 'Old heroes are still there'); 189 | 190 | const maxId = heroesBefore[heroesBefore.length - 1].id; 191 | expect(heroesAfter[numHeroes]).toEqual({id: maxId + 1, name: newHeroName}); 192 | }); 193 | 194 | it('displays correctly styled buttons', async () => { 195 | element.all(by.buttonText('x')).then(buttons => { 196 | for (const button of buttons) { 197 | // Inherited styles from styles.css 198 | expect(button.getCssValue('font-family')).toBe('Arial'); 199 | expect(button.getCssValue('border')).toContain('none'); 200 | expect(button.getCssValue('padding')).toBe('5px 10px'); 201 | expect(button.getCssValue('border-radius')).toBe('4px'); 202 | // Styles defined in heroes.component.css 203 | expect(button.getCssValue('left')).toBe('194px'); 204 | expect(button.getCssValue('top')).toBe('-32px'); 205 | } 206 | }); 207 | 208 | const addButton = element(by.buttonText('add')); 209 | // Inherited styles from styles.css 210 | expect(addButton.getCssValue('font-family')).toBe('Arial'); 211 | expect(addButton.getCssValue('border')).toContain('none'); 212 | expect(addButton.getCssValue('padding')).toBe('5px 10px'); 213 | expect(addButton.getCssValue('border-radius')).toBe('4px'); 214 | }); 215 | 216 | }); 217 | 218 | describe('Progressive hero search', () => { 219 | 220 | beforeAll(() => browser.get('')); 221 | 222 | it(`searches for 'Ma'`, async () => { 223 | getPageElts().searchBox.sendKeys('Ma'); 224 | browser.sleep(1000); 225 | 226 | expect(getPageElts().searchResults.count()).toBe(4); 227 | }); 228 | 229 | it(`continues search with 'g'`, async () => { 230 | getPageElts().searchBox.sendKeys('g'); 231 | browser.sleep(1000); 232 | expect(getPageElts().searchResults.count()).toBe(2); 233 | }); 234 | 235 | it(`continues search with 'e' and gets ${targetHero.name}`, async () => { 236 | getPageElts().searchBox.sendKeys('n'); 237 | browser.sleep(1000); 238 | let page = getPageElts(); 239 | expect(page.searchResults.count()).toBe(1); 240 | let hero = page.searchResults.get(0); 241 | expect(hero.getText()).toEqual(targetHero.name); 242 | }); 243 | 244 | it(`navigates to ${targetHero.name} details view`, async () => { 245 | let hero = getPageElts().searchResults.get(0); 246 | expect(hero.getText()).toEqual(targetHero.name); 247 | hero.click(); 248 | 249 | let page = getPageElts(); 250 | expect(page.heroDetail.isPresent()).toBeTruthy('shows hero detail'); 251 | let hero2 = await Hero.fromDetail(page.heroDetail); 252 | expect(hero2.id).toEqual(targetHero.id); 253 | expect(hero2.name).toEqual(targetHero.name.toUpperCase()); 254 | }); 255 | }); 256 | 257 | async function dashboardSelectTargetHero() { 258 | let targetHeroElt = getPageElts().topHeroes.get(targetHeroDashboardIndex); 259 | expect(targetHeroElt.getText()).toEqual(targetHero.name); 260 | targetHeroElt.click(); 261 | browser.waitForAngular(); // seems necessary to gets tests to pass for toh-pt6 262 | 263 | let page = getPageElts(); 264 | expect(page.heroDetail.isPresent()).toBeTruthy('shows hero detail'); 265 | let hero = await Hero.fromDetail(page.heroDetail); 266 | expect(hero.id).toEqual(targetHero.id); 267 | expect(hero.name).toEqual(targetHero.name.toUpperCase()); 268 | } 269 | 270 | async function updateHeroNameInDetailView() { 271 | // Assumes that the current view is the hero details view. 272 | addToHeroName(nameSuffix); 273 | 274 | let page = getPageElts(); 275 | let hero = await Hero.fromDetail(page.heroDetail); 276 | expect(hero.id).toEqual(targetHero.id); 277 | expect(hero.name).toEqual(newHeroName.toUpperCase()); 278 | } 279 | 280 | }); 281 | 282 | function addToHeroName(text: string): promise.Promise { 283 | let input = element(by.css('input')); 284 | return input.sendKeys(text); 285 | } 286 | 287 | function expectHeading(hLevel: number, expectedText: string): void { 288 | let hTag = `h${hLevel}`; 289 | let hText = element(by.css(hTag)).getText(); 290 | expect(hText).toEqual(expectedText, hTag); 291 | }; 292 | 293 | function getHeroAEltById(id: number): ElementFinder { 294 | let spanForId = element(by.cssContainingText('li span.badge', id.toString())); 295 | return spanForId.element(by.xpath('..')); 296 | } 297 | 298 | function getHeroLiEltById(id: number): ElementFinder { 299 | let spanForId = element(by.cssContainingText('li span.badge', id.toString())); 300 | return spanForId.element(by.xpath('../..')); 301 | } 302 | 303 | async function toHeroArray(allHeroes: ElementArrayFinder): Promise { 304 | let promisedHeroes = await allHeroes.map(Hero.fromLi); 305 | // The cast is necessary to get around issuing with the signature of Promise.all() 306 | return > Promise.all(promisedHeroes); 307 | } 308 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": [ 9 | "jasmine", 10 | "jasminewd2", 11 | "node" 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-io-example", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "Example project from an angular.io guide.", 6 | "scripts": { 7 | "ng": "ng", 8 | "build": "ng build --prod", 9 | "start": "ng serve", 10 | "test": "ng test", 11 | "lint": "tslint ./src/**/*.ts -t verbose", 12 | "e2e": "ng e2e" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "MIT", 17 | "dependencies": { 18 | "@angular/animations": "^6.0.0", 19 | "@angular/cdk": "^6.0.2", 20 | "@angular/common": "^6.0.0", 21 | "@angular/compiler": "^6.0.0", 22 | "@angular/core": "^6.0.0", 23 | "@angular/forms": "^6.0.0", 24 | "@angular/http": "^6.0.0", 25 | "@angular/material": "^6.0.2", 26 | "@angular/platform-browser": "^6.0.0", 27 | "@angular/platform-browser-dynamic": "^6.0.0", 28 | "@angular/router": "^6.0.0", 29 | "@angular/upgrade": "^6.0.0", 30 | "angular-in-memory-web-api": "^0.6.0", 31 | "bootstrap": "^4.1.1", 32 | "core-js": "^2.5.4", 33 | "hammerjs": "^2.0.8", 34 | "ngx-bootstrap": "^2.0.5", 35 | "rxjs": "^6.0.0", 36 | "zone.js": "^0.8.24" 37 | }, 38 | "devDependencies": { 39 | "@angular-devkit/build-angular": "~0.6.0", 40 | "@angular/cli": "^6.0.0", 41 | "@angular/compiler-cli": "^6.0.0", 42 | "@angular/platform-server": "^6.0.0", 43 | "@types/jasmine": "~2.8.0", 44 | "@types/jasminewd2": "^2.0.3", 45 | "@types/node": "^6.0.45", 46 | "jasmine-core": "~2.99.1", 47 | "jasmine-spec-reporter": "~4.2.1", 48 | "karma": "~1.7.1", 49 | "karma-chrome-launcher": "~2.2.0", 50 | "karma-coverage-istanbul-reporter": "~1.4.2", 51 | "karma-jasmine": "~1.1.1", 52 | "karma-jasmine-html-reporter": "^0.2.2", 53 | "karma-phantomjs-launcher": "^1.0.2", 54 | "lodash": "^4.16.2", 55 | "phantomjs-prebuilt": "^2.1.7", 56 | "protractor": "~5.3.0", 57 | "ts-node": "^5.0.1", 58 | "tslint": "^5.9.1", 59 | "typescript": "2.7.2" 60 | }, 61 | "repository": {} 62 | } 63 | -------------------------------------------------------------------------------- /src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | 4 | import { DashboardComponent } from './dashboard/dashboard.component'; 5 | 6 | const routes: Routes = [ 7 | { path: '', redirectTo: '/dashboard', pathMatch: 'full' }, 8 | { path: 'dashboard', component: DashboardComponent }, 9 | { 10 | path: 'task', 11 | loadChildren: 'app/task/task.module#TaskModule' 12 | }, 13 | ]; 14 | 15 | @NgModule({ 16 | imports: [ RouterModule.forRoot(routes) ], 17 | exports: [ RouterModule ] 18 | }) 19 | export class AppRoutingModule {} 20 | -------------------------------------------------------------------------------- /src/app/app.component.css: -------------------------------------------------------------------------------- 1 | /* AppComponent's private CSS styles */ 2 | h1 { 3 | font-size: 1.2em; 4 | color: #999; 5 | margin-bottom: 0; 6 | } 7 | h2 { 8 | font-size: 2em; 9 | margin-top: 0; 10 | padding-top: 0; 11 | } 12 | nav a { 13 | padding: 5px 10px; 14 | text-decoration: none; 15 | margin-top: 10px; 16 | display: inline-block; 17 | background-color: #eee; 18 | border-radius: 4px; 19 | } 20 | nav a:visited, a:link { 21 | color: #607D8B; 22 | } 23 | nav a:hover { 24 | color: #039be5; 25 | background-color: #CFD8DC; 26 | } 27 | nav a.active { 28 | color: #039be5; 29 | } 30 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'] 7 | }) 8 | export class AppComponent { 9 | title = 'Angular Architecture Demo'; 10 | } 11 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | import { FormsModule } from '@angular/forms'; 4 | import { HttpClientModule } from '@angular/common/http'; 5 | 6 | import { CoreModule } from './core/core.module'; 7 | import { AppRoutingModule } from './app-routing.module'; 8 | 9 | import { AppComponent } from './app.component'; 10 | import { DashboardComponent } from './dashboard/dashboard.component'; 11 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 12 | import { MatGridListModule, MatCardModule, MatMenuModule, MatIconModule, MatButtonModule } from '@angular/material'; 13 | 14 | @NgModule({ 15 | imports: [ 16 | BrowserModule, 17 | FormsModule, 18 | AppRoutingModule, 19 | HttpClientModule, 20 | CoreModule, 21 | 22 | BrowserAnimationsModule, 23 | 24 | MatGridListModule, 25 | 26 | MatCardModule, 27 | 28 | MatMenuModule, 29 | 30 | MatIconModule, 31 | 32 | MatButtonModule 33 | ], 34 | declarations: [ 35 | AppComponent, 36 | DashboardComponent 37 | ], 38 | bootstrap: [ AppComponent ] 39 | }) 40 | export class AppModule { } 41 | -------------------------------------------------------------------------------- /src/app/core/core.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { FooterComponent } from '@app/core/footer/footer.component'; 5 | import { NavbarComponent } from '@app/core/navbar/navbar.component'; 6 | import { LayoutModule } from '@angular/cdk/layout'; 7 | import { MatToolbarModule, MatButtonModule, MatSidenavModule, MatIconModule, MatListModule } from '@angular/material'; 8 | 9 | @NgModule({ 10 | imports: [ 11 | CommonModule, 12 | LayoutModule, 13 | MatToolbarModule, 14 | MatButtonModule, 15 | MatSidenavModule, 16 | MatIconModule, 17 | MatListModule 18 | ], 19 | declarations: [FooterComponent, NavbarComponent], 20 | exports: [FooterComponent, NavbarComponent] 21 | }) 22 | export class CoreModule { } 23 | -------------------------------------------------------------------------------- /src/app/core/exception.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable({ 4 | providedIn: 'root' 5 | }) 6 | export class ExceptionService { 7 | 8 | constructor() { } 9 | } 10 | -------------------------------------------------------------------------------- /src/app/core/footer/footer.component.css: -------------------------------------------------------------------------------- 1 | .footer-main-box11 { 2 | border-top: 1px solid #d2d2d2; 3 | width: 100%; 4 | display: inline-block; 5 | text-align: center; 6 | } 7 | 8 | .footer { 9 | height: 54px; 10 | line-height: 54px; 11 | text-align: center; 12 | font-size: 15px; 13 | color: #000; 14 | margin-top: 1px; 15 | display: inherit; 16 | } 17 | -------------------------------------------------------------------------------- /src/app/core/footer/footer.component.html: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/app/core/footer/footer.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-footer', 5 | templateUrl: './footer.component.html', 6 | styleUrls: ['./footer.component.css'] 7 | }) 8 | export class FooterComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/core/logger.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable({ 4 | providedIn: 'root' 5 | }) 6 | export class LoggerService { 7 | 8 | constructor() { } 9 | 10 | log(msg: any) { 11 | console.log(new Date() + ": " 12 | + JSON.stringify(msg)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/app/core/navbar/navbar.component.css: -------------------------------------------------------------------------------- 1 | .sidenav-container { 2 | height: 100%; 3 | } 4 | 5 | .sidenav { 6 | width: 200px; 7 | box-shadow: 3px 0 6px rgba(0,0,0,.24); 8 | } 9 | 10 | .navbar-default { 11 | background-color: #3f51b5; 12 | border-color: #3f51b5; 13 | } 14 | .navbar-custom-color 15 | { 16 | background-color: #3f51b5 !important; 17 | border-color: #3f51b5 !important; 18 | } 19 | 20 | .navbar-custom-font-color 21 | { 22 | color: white !important; 23 | font-size: 18px !important; 24 | } 25 | -------------------------------------------------------------------------------- /src/app/core/navbar/navbar.component.html: -------------------------------------------------------------------------------- 1 | 27 | 56 | -------------------------------------------------------------------------------- /src/app/core/navbar/navbar.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout'; 3 | import { Observable } from 'rxjs'; 4 | 5 | @Component({ 6 | selector: 'app-navbar', 7 | templateUrl: './navbar.component.html', 8 | styleUrls: ['./navbar.component.css'] 9 | }) 10 | export class NavbarComponent { 11 | isHandset: Observable = this.breakpointObserver.observe(Breakpoints.Handset); 12 | constructor(private breakpointObserver: BreakpointObserver) {} 13 | } 14 | -------------------------------------------------------------------------------- /src/app/dashboard/dashboard.component.css: -------------------------------------------------------------------------------- 1 | .grid-container { 2 | margin: 20px; 3 | } 4 | 5 | .dashboard-card { 6 | position: absolute; 7 | top: 15px; 8 | left: 15px; 9 | right: 15px; 10 | bottom: 15px; 11 | } 12 | 13 | .more-button { 14 | position: absolute; 15 | top: 5px; 16 | right: 10px; 17 | } 18 | 19 | .dashboard-card-content { 20 | text-align: center; 21 | } -------------------------------------------------------------------------------- /src/app/dashboard/dashboard.component.html: -------------------------------------------------------------------------------- 1 |
    2 |

    Dashboard

    3 | 4 | 5 | 6 | 7 | 8 | {{card.title}} 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
    Card Content Here
    20 |
    21 |
    22 |
    23 |
    24 |
    25 | -------------------------------------------------------------------------------- /src/app/dashboard/dashboard.component.spec.ts: -------------------------------------------------------------------------------- 1 | 2 | import { fakeAsync, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | 4 | import { DashboardComponent } from './dashboard.component'; 5 | 6 | describe('DashboardComponent', () => { 7 | let component: DashboardComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(fakeAsync(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [ DashboardComponent ] 13 | }) 14 | .compileComponents(); 15 | 16 | fixture = TestBed.createComponent(DashboardComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | })); 20 | 21 | it('should compile', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/dashboard/dashboard.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'dashboard', 5 | templateUrl: './dashboard.component.html', 6 | styleUrls: ['./dashboard.component.css'] 7 | }) 8 | export class DashboardComponent { 9 | cards = [ 10 | { title: 'Card 1', cols: 2, rows: 1 }, 11 | { title: 'Card 2', cols: 1, rows: 1 }, 12 | { title: 'Card 3', cols: 1, rows: 2 }, 13 | { title: 'Card 4', cols: 1, rows: 1 } 14 | ]; 15 | } 16 | -------------------------------------------------------------------------------- /src/app/shared/models/tasks.ts: -------------------------------------------------------------------------------- 1 | export class Schedule { 2 | position: number; 3 | name: string; 4 | weight: number; 5 | symbol: string; 6 | } 7 | -------------------------------------------------------------------------------- /src/app/shared/pipes/file-size.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | // 4 | // {{ file.size | filesize }} 5 | 6 | @Pipe({ 7 | name: 'fileSize' 8 | }) 9 | 10 | export class FileSizePipe implements PipeTransform { 11 | 12 | transform(size: number): string { 13 | return (size / (1024 * 1024)).toFixed(2) + 'MB'; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/app/shared/services/resource/resource.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient, HttpHeaders } from '@angular/common/http'; 2 | import { Injectable } from '@angular/core'; 3 | import { Observable } from "rxjs"; 4 | import { map } from "rxjs/operators"; 5 | import { environment } from "../../../../environments/environment"; 6 | 7 | 8 | @Injectable({ 9 | providedIn: 'root' 10 | }) 11 | export class ResourceService { 12 | 13 | constructor(private http: HttpClient) {} 14 | 15 | getAll(resource: string, options?: any): Observable { 16 | return this.http.get(`${environment.api}${resource}?${options}`).pipe( 17 | map(res => { 18 | return res; 19 | }) 20 | ); 21 | } 22 | 23 | getById(resource: string, id: any) { 24 | return this.http.get(`${environment.api}${resource}/${id}`); 25 | } 26 | 27 | post(resource: string, data: any) { 28 | return this.http.post(`${environment.api}${resource}`, data); 29 | } 30 | 31 | save(resource: string, data: any) { 32 | if (!data._id) { 33 | return this.http.post(`${environment.api}${resource}`, data); 34 | }else { 35 | return this.http.patch(`${environment.api}${resource}/${data._id}`, data); 36 | } 37 | } 38 | 39 | delete(resource: string, id: any) { 40 | return this.http.delete(`${environment.api}${resource}/${id}`); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/app/shared/shared.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FileSizePipe } from '@app/shared/pipes/file-size.pipe'; 4 | 5 | @NgModule({ 6 | imports: [ 7 | CommonModule 8 | ], 9 | declarations: [FileSizePipe] 10 | }) 11 | export class SharedModule { } 12 | -------------------------------------------------------------------------------- /src/app/task/schedule/schedule.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spider-yamet/Angular-6-Architecture/f43cca7b47d5b80830f1d512220aff3143975d2b/src/app/task/schedule/schedule.component.css -------------------------------------------------------------------------------- /src/app/task/schedule/schedule.component.html: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
    No. {{element.position}} Name {{element.name}} Weight {{element.weight}} Symbol {{element.symbol}}
    35 |
    36 | -------------------------------------------------------------------------------- /src/app/task/schedule/schedule.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { TaskService } from '@app/task/shared/task.service'; 3 | import { Schedule } from '@app/shared/models/tasks'; 4 | import { LoggerService } from '@app/core/logger.service'; 5 | 6 | @Component({ 7 | selector: 'task/schedule', 8 | templateUrl: './schedule.component.html', 9 | styleUrls: ['./schedule.component.css'] 10 | }) 11 | export class ScheduleComponent implements OnInit { 12 | 13 | constructor(private taskService: TaskService, private logger: LoggerService) { } 14 | 15 | dataSource = [ 16 | {position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H'}, 17 | {position: 2, name: 'Helium', weight: 4.0026, symbol: 'He'}, 18 | {position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li'}, 19 | {position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be'}, 20 | {position: 5, name: 'Boron', weight: 10.811, symbol: 'B'}, 21 | {position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C'}, 22 | {position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N'}, 23 | {position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O'}, 24 | {position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F'}, 25 | {position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne'} 26 | ]; 27 | 28 | displayedColumns = ['position', 'name', 'weight', 'symbol']; 29 | 30 | ngOnInit() { 31 | this.logger.log('Feting Schedule Data'); 32 | this.getSchedule(); 33 | } 34 | 35 | getSchedule(): void { 36 | this.taskService.getTask() 37 | .subscribe(schedules => this.dataSource = schedules || this.dataSource); 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /src/app/task/shared/task.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "position": 1, 4 | "name": "Hydrogen", 5 | "weight": 33, 6 | "symbol": "H" 7 | }, 8 | { 9 | "position": 2, 10 | "name": "Helium", 11 | "weight": 43, 12 | "symbol": "He" 13 | }, 14 | { 15 | "position": 1, 16 | "name": "Lithium", 17 | "weight": 88, 18 | "symbol": "Li" 19 | } 20 | ] 21 | -------------------------------------------------------------------------------- /src/app/task/shared/task.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient, HttpHeaders } from '@angular/common/http'; 3 | import { Schedule } from '@app/shared/models/tasks'; 4 | 5 | @Injectable({ 6 | providedIn: 'root' 7 | }) 8 | export class TaskService { 9 | configUrl = 'http://localhost:3000/getTempSchedule'; 10 | 11 | constructor(private http: HttpClient) {} 12 | 13 | getTask() { 14 | return this.http.get(this.configUrl); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/app/task/task-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { ThresholdComponent } from './threshold/threshold.component'; 4 | import { TasksComponent } from './tasks/tasks.component'; 5 | import { ScheduleComponent } from './schedule/schedule.component'; 6 | 7 | const routes: Routes = [ 8 | { path: '', component: TasksComponent }, 9 | { path: 'schedule', component: ScheduleComponent }, 10 | { path: 'threshold', component: ThresholdComponent } 11 | ]; 12 | 13 | @NgModule({ 14 | imports: [RouterModule.forChild(routes)], 15 | exports: [RouterModule] 16 | }) 17 | export class TaskRoutingModule { } 18 | -------------------------------------------------------------------------------- /src/app/task/task.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { TaskRoutingModule } from './task-routing.module'; 5 | import { ThresholdComponent } from './threshold/threshold.component'; 6 | import { TasksComponent } from './tasks/tasks.component'; 7 | import { ScheduleComponent } from './schedule/schedule.component'; 8 | import { MatTableModule, MatPaginatorModule, MatSortModule } from '@angular/material'; 9 | 10 | @NgModule({ 11 | imports: [ 12 | CommonModule, 13 | TaskRoutingModule, 14 | MatTableModule, 15 | MatPaginatorModule, 16 | MatSortModule 17 | ], 18 | declarations: [ThresholdComponent, TasksComponent, ScheduleComponent] 19 | }) 20 | export class TaskModule { } 21 | -------------------------------------------------------------------------------- /src/app/task/tasks/tasks.component.css: -------------------------------------------------------------------------------- 1 | [class*='col-'] { 2 | float: left; 3 | padding-right: 20px; 4 | padding-bottom: 20px; 5 | } 6 | [class*='col-']:last-of-type { 7 | padding-right: 0; 8 | } 9 | a { 10 | text-decoration: none; 11 | } 12 | *, *:after, *:before { 13 | -webkit-box-sizing: border-box; 14 | -moz-box-sizing: border-box; 15 | box-sizing: border-box; 16 | } 17 | h3 { 18 | text-align: center; margin-bottom: 0; 19 | } 20 | h4 { 21 | position: relative; 22 | } 23 | .grid { 24 | margin: 0; 25 | } 26 | .col-1-4 { 27 | width: 25%; 28 | } 29 | .module { 30 | padding: 20px; 31 | text-align: center; 32 | color: #eee; 33 | max-height: 120px; 34 | min-width: 120px; 35 | background-color: #607D8B; 36 | border-radius: 2px; 37 | } 38 | .module:hover { 39 | background-color: #EEE; 40 | cursor: pointer; 41 | color: #607d8b; 42 | } 43 | .grid-pad { 44 | padding: 10px 0; 45 | } 46 | .grid-pad > [class*='col-']:last-of-type { 47 | padding-right: 20px; 48 | } 49 | @media (max-width: 600px) { 50 | .module { 51 | font-size: 10px; 52 | max-height: 75px; } 53 | } 54 | @media (max-width: 1024px) { 55 | .grid { 56 | margin: 0; 57 | } 58 | .module { 59 | min-width: 60px; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/app/task/tasks/tasks.component.html: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /src/app/task/tasks/tasks.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-tasks', 5 | templateUrl: './tasks.component.html', 6 | styleUrls: ['./tasks.component.css'] 7 | }) 8 | export class TasksComponent implements OnInit { 9 | tasks: any[] = [ 10 | { id: 1, name: 'Schedule', routerLink: '/task/schedule' }, 11 | { id: 2, name: 'Threshold', routerLink: '/task/threshold' }, 12 | ]; 13 | constructor() { } 14 | 15 | ngOnInit() { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/app/task/threshold/threshold.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spider-yamet/Angular-6-Architecture/f43cca7b47d5b80830f1d512220aff3143975d2b/src/app/task/threshold/threshold.component.css -------------------------------------------------------------------------------- /src/app/task/threshold/threshold.component.html: -------------------------------------------------------------------------------- 1 |

    2 | threshold works! 3 |

    4 | -------------------------------------------------------------------------------- /src/app/task/threshold/threshold.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-threshold', 5 | templateUrl: './threshold.component.html', 6 | styleUrls: ['./threshold.component.css'] 7 | }) 8 | export class ThresholdComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/browserslist: -------------------------------------------------------------------------------- 1 | > 0.5% 2 | last 2 versions 3 | Firefox ESR 4 | not dead 5 | IE 9-11 -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | api: 'http://localhost:3010/', 7 | production: false 8 | }; 9 | 10 | /* 11 | * In development mode, to ignore zone related error stack frames such as 12 | * `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can 13 | * import the following file, but please comment it out in production mode 14 | * because it will have performance impact when throw error 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- 1 |  h& ��(  @������������������71�U61��0-��2.�;������������������������������71�971��71��71��0-��0-��1-��3/�#������������������71�!71�71��71��71��71��0-��0-��0-��0-��1-��3/����������71�U71��OI��VQ��82��71��71��0-��0-��0-��UR��KH��0-��2.�=������71�71����������mh��71��71��0-��0-��[X����������0-��1.�{������71�71��C=����������71��71��0-��0-����������85��0-��0-��������71��71��71����������������������������������0-��0-��1-��������71��71��71��QL��������������������������?<��0-��0-��0-��������71��71��71��71����������;5��:7����������0-��0-��0-��0-�����71�#71��71��71��71��d_������}y����������IG��0-��0-��0-��0-��4/�71�M71��71��71��71��71������������������0-��0-��0-��0-��0-��3/�A71�k71��71��71��71��71��zv����������VS��0-��0-��0-��0-��0-��2.�e71�}71��71��71��71��71��93����������0-��0-��0-��0-��0-��0-��1.�{71�A71��71��71��71��71��71������db��0-��0-��0-��0-��0-��1-��0-�E������71�)71�71��71��71��=7��0-��0-��0-��0-��1-��3/�/���������������������71�A71�61��0-��0-��3.�9�����������������������������������������������( @ �������������������������������������������71�61��2.��3/�?������������������������������������������������������������������������������71�_71��71��71��0-��0-��2.��3/�������������������������������������������������������������������71�;71��71��71��71��71��0-��0-��0-��0-��3.ŭ4/� ������������������������������������������������������71�71��71��71��71��71��71��71��0-��0-��0-��0-��0-��1.��3/lj���������������������������������������������71� 71�71��71��71��71��71��71��71��71��0-��0-��0-��0-��0-��0-��0-��1.��3/�_������������������������������������71�71��71��71��71��71��71��71��71��71��71��0-��0-��0-��0-��0-��0-��0-��0-��0-��2.��3/�;������������������������71�Y71��71��71��71��71��71��71��71��71��71��71��71��0-��0-��0-��0-��0-��0-��0-��0-��0-��0-��0-��2.��3/�������������������71��71��71��XS��wr��vr��vr��<7��71��71��71��71��71��0-��0-��0-��0-��0-��0-��wu��~|��~|��NK��0-��0-��2.��������������������71��71��71��UP��������������{w��71��71��71��71��71��0-��0-��0-��0-��0-��YW��������������DB��0-��0-��1.�����������������71�71��71��71��71������������������71��71��71��71��71��0-��0-��0-��0-��0-������������������0-��0-��0-��1.�����������������71�O71��71��71��71��hc��������������SN��71��71��71��71��0-��0-��0-��0-��B?��������������PM��0-��0-��0-��0-��50�������������71�}71��71��71��71��71������������������71��71��71��71��0-��0-��0-��0-������������������0-��0-��0-��0-��0-��3/�3������������71�71��71��71��71��71��~{����������������������������������������������������������][��0-��0-��0-��0-��0-��3/�i������������71��71��71��71��71��71��;5����������������������������������������������������������0-��0-��0-��0-��0-��0-��3/Ǘ������������71��71��71��71��71��71��71������������������������������������������������������mk��0-��0-��0-��0-��0-��0-��2.Ž������������71��71��71��71��71��71��71��D>������������������ie��ie��db��db������������������1.��0-��0-��0-��0-��0-��0-��2.��������������71��71��71��71��71��71��71��71������������������71��71��0-��0-��������������|z��0-��0-��0-��0-��0-��0-��0-��2.��������������71��71��71��71��71��71��71��71��RL��������������JD��71��0-��XU��������������52��0-��0-��0-��0-��0-��0-��0-��1.�����������71�+71��71��71��71��71��71��71��71��71������������������71��0-������������������0-��0-��0-��0-��0-��0-��0-��0-��0-��50�������71�]71��71��71��71��71��71��71��71��71��ea��������������:4��QO��������������:7��0-��0-��0-��0-��0-��0-��0-��0-��0-��4/�5������71�71��71��71��71��71��71��71��71��71��71��������������wr������������������0-��0-��0-��0-��0-��0-��0-��0-��0-��0-��3/�k������71�71��71��71��71��71��71��71��71��71��71��zv��������������������������C@��0-��0-��0-��0-��0-��0-��0-��0-��0-��0-��3/Ǘ������71��71��71��71��71��71��71��71��71��71��71��:4��������������������������0-��0-��0-��0-��0-��0-��0-��0-��0-��0-��0-��2.Ž������71��71��71��71��71��71��71��71��71��71��71��71����������������������MK��0-��0-��0-��0-��0-��0-��0-��0-��0-��0-��0-��2.��������71��71��71��71��71��71��71��71��71��71��71��71��B<������������������0-��0-��0-��0-��0-��0-��0-��0-��0-��0-��0-��0-��2.��������71��71��71��71��71��71��71��71��71��71��71��71��71��������������[Y��0-��0-��0-��0-��0-��0-��0-��0-��0-��0-��0-��0-��1.��������71��71��71��71��71��71��71��71��71��71��71��71��71��OJ����������0-��0-��0-��0-��0-��0-��0-��0-��0-��0-��0-��0-��0-��0-��������71�71�y71��71��71��71��71��71��71��71��71��71��71��71������jg��0-��0-��0-��0-��0-��0-��0-��0-��0-��0-��0-��2.��3/Ɠ4/����������������71�71�71��71��71��71��71��71��71��71��71��PK��1.��0-��0-��0-��0-��0-��0-��0-��0-��2.��3/Ǜ3/����������������������������������71�)71�71��71��71��71��71��71��71��0-��0-��0-��0-��0-��0-��1.��3/ƥ4/�'���������������������������������������������������71�C71�71��71��71��71��0-��0-��0-��1.��3.ů3/�1���������������������������������������������������������������������71�a71��61��1.��2.ŷ3/�=��������������������������������������� -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Angular Architecture Demo 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, 'coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | angularCli: { 24 | environment: 'dev' 25 | }, 26 | reporters: ['progress', 'kjhtml'], 27 | port: 9876, 28 | colors: true, 29 | logLevel: config.LOG_INFO, 30 | autoWatch: true, 31 | browsers: ['Chrome'], 32 | singleRun: false 33 | }); 34 | }; -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | import 'hammerjs'; 4 | 5 | import { AppModule } from './app/app.module'; 6 | import { environment } from './environments/environment'; 7 | 8 | if (environment.production) { 9 | enableProdMode(); 10 | } 11 | 12 | platformBrowserDynamic().bootstrapModule(AppModule); 13 | -------------------------------------------------------------------------------- /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/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** IE10 and IE11 requires the following for the Reflect API. */ 41 | // import 'core-js/es6/reflect'; 42 | 43 | 44 | /** Evergreen browsers require these. **/ 45 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. 46 | import 'core-js/es7/reflect'; 47 | 48 | 49 | /** 50 | * Required to support Web Animations `@angular/platform-browser/animations`. 51 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation 52 | **/ 53 | // import 'web-animations-js'; 54 | 55 | /** 56 | * By default, zone.js will patch all possible macroTask and DomEvents 57 | * user can disable parts of macroTask/DomEvents patch by setting following flags 58 | */ 59 | 60 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 61 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 62 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 63 | 64 | /* 65 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 66 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 67 | */ 68 | // (window as any).__Zone_enable_cross_context_check = true; 69 | 70 | /*************************************************************************************************** 71 | * Zone JS is required by default for Angular itself. 72 | */ 73 | import 'zone.js/dist/zone'; // Included with Angular CLI. 74 | 75 | 76 | 77 | /*************************************************************************************************** 78 | * APPLICATION IMPORTS 79 | */ 80 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | /* Master Styles */ 2 | h1 { 3 | color: #369; 4 | font-family: Arial, Helvetica, sans-serif; 5 | font-size: 250%; 6 | } 7 | h2, h3 { 8 | color: #444; 9 | font-family: Arial, Helvetica, sans-serif; 10 | font-weight: lighter; 11 | } 12 | body { 13 | margin: 2em; 14 | } 15 | body, input[text], button { 16 | color: #888; 17 | font-family: Cambria, Georgia; 18 | } 19 | a { 20 | cursor: pointer; 21 | cursor: hand; 22 | } 23 | button { 24 | font-family: Arial; 25 | background-color: #eee; 26 | border: none; 27 | padding: 5px 10px; 28 | border-radius: 4px; 29 | cursor: pointer; 30 | cursor: hand; 31 | } 32 | button:hover { 33 | background-color: #cfd8dc; 34 | } 35 | button:disabled { 36 | background-color: #eee; 37 | color: #aaa; 38 | cursor: auto; 39 | } 40 | 41 | /* Navigation link styles */ 42 | nav a { 43 | padding: 5px 10px; 44 | text-decoration: none; 45 | margin-right: 10px; 46 | margin-top: 10px; 47 | display: inline-block; 48 | background-color: #eee; 49 | border-radius: 4px; 50 | } 51 | nav a:visited, a:link { 52 | color: #607D8B; 53 | } 54 | nav a:hover { 55 | color: #039be5; 56 | background-color: #CFD8DC; 57 | } 58 | nav a.active { 59 | color: #039be5; 60 | } 61 | 62 | /* everywhere else */ 63 | * { 64 | font-family: Arial, Helvetica, sans-serif; 65 | } 66 | 67 | body { margin: 0; } 68 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "baseUrl": "./", 6 | "module": "es2015", 7 | "types": [] 8 | }, 9 | "exclude": [ 10 | "test.ts", 11 | "**/*.spec.ts", 12 | "**/testing/*" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 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/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "outDir": "./dist/out-tsc", 5 | "sourceMap": true, 6 | "declaration": false, 7 | "moduleResolution": "node", 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "target": "es5", 11 | "skipLibCheck": true, 12 | "typeRoots": [ 13 | "node_modules/@types" 14 | ], 15 | "lib": [ 16 | "es2017", 17 | "dom" 18 | ], 19 | "baseUrl": "src", 20 | "paths": { 21 | "@app/*": ["app/*"], 22 | "@env/*": ["environments/*"] 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "arrow-return-shorthand": true, 7 | "callable-types": true, 8 | "class-name": true, 9 | "comment-format": [ 10 | true, 11 | "check-space" 12 | ], 13 | "curly": true, 14 | "deprecation": { 15 | "severity": "warn" 16 | }, 17 | "eofline": true, 18 | "forin": true, 19 | "import-blacklist": [ 20 | true, 21 | "rxjs", 22 | "rxjs/Rx" 23 | ], 24 | "import-spacing": true, 25 | "indent": [ 26 | true, 27 | "spaces" 28 | ], 29 | "interface-over-type-literal": true, 30 | "label-position": true, 31 | "max-line-length": [ 32 | true, 33 | 140 34 | ], 35 | "member-access": false, 36 | "member-ordering": [ 37 | true, 38 | { 39 | "order": [ 40 | "static-field", 41 | "instance-field", 42 | "static-method", 43 | "instance-method" 44 | ] 45 | } 46 | ], 47 | "no-arg": true, 48 | "no-bitwise": true, 49 | "no-console": [ 50 | true, 51 | "debug", 52 | "info", 53 | "time", 54 | "timeEnd", 55 | "trace" 56 | ], 57 | "no-construct": true, 58 | "no-debugger": true, 59 | "no-duplicate-super": true, 60 | "no-empty": false, 61 | "no-empty-interface": true, 62 | "no-eval": true, 63 | "no-inferrable-types": [ 64 | true, 65 | "ignore-params" 66 | ], 67 | "no-misused-new": true, 68 | "no-non-null-assertion": true, 69 | "no-shadowed-variable": true, 70 | "no-string-literal": false, 71 | "no-string-throw": true, 72 | "no-switch-case-fall-through": true, 73 | "no-trailing-whitespace": true, 74 | "no-unnecessary-initializer": true, 75 | "no-unused-expression": true, 76 | "no-use-before-declare": true, 77 | "no-var-keyword": true, 78 | "object-literal-sort-keys": false, 79 | "one-line": [ 80 | true, 81 | "check-open-brace", 82 | "check-catch", 83 | "check-else", 84 | "check-whitespace" 85 | ], 86 | "prefer-const": true, 87 | "quotemark": [ 88 | true, 89 | "single" 90 | ], 91 | "radix": true, 92 | "semicolon": [ 93 | true, 94 | "always" 95 | ], 96 | "triple-equals": [ 97 | true, 98 | "allow-null-check" 99 | ], 100 | "typedef-whitespace": [ 101 | true, 102 | { 103 | "call-signature": "nospace", 104 | "index-signature": "nospace", 105 | "parameter": "nospace", 106 | "property-declaration": "nospace", 107 | "variable-declaration": "nospace" 108 | } 109 | ], 110 | "typeof-compare": true, 111 | "unified-signatures": true, 112 | "variable-name": false, 113 | "whitespace": [ 114 | true, 115 | "check-branch", 116 | "check-decl", 117 | "check-operator", 118 | "check-separator", 119 | "check-type" 120 | ], 121 | "directive-selector": [ 122 | true, 123 | "attribute", 124 | "app", 125 | "camelCase" 126 | ], 127 | "component-selector": [ 128 | true, 129 | "element", 130 | "app", 131 | "kebab-case" 132 | ], 133 | "no-output-on-prefix": true, 134 | "use-input-property-decorator": true, 135 | "use-output-property-decorator": true, 136 | "use-host-property-decorator": true, 137 | "no-input-rename": true, 138 | "no-output-rename": true, 139 | "use-life-cycle-interface": true, 140 | "use-pipe-transform-interface": true, 141 | "component-class-suffix": true, 142 | "directive-class-suffix": true 143 | } 144 | } 145 | --------------------------------------------------------------------------------