├── .editorconfig ├── .gitignore ├── README.md ├── angular.json ├── browserslist ├── e2e ├── protractor.conf.js ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts └── tsconfig.e2e.json ├── package-lock.json ├── package.json ├── proxy.conf.json ├── src ├── .htaccess ├── app │ ├── app-routing.module.ts │ ├── app.component.css │ ├── app.component.html │ ├── app.component.spec.ts │ ├── app.component.ts │ ├── app.controller.ts │ ├── app.module.ts │ ├── core │ │ ├── components │ │ │ ├── footer │ │ │ │ ├── footer.component.html │ │ │ │ ├── footer.component.scss │ │ │ │ ├── footer.component.spec.ts │ │ │ │ └── footer.component.ts │ │ │ ├── header │ │ │ │ ├── header.component.html │ │ │ │ ├── header.component.scss │ │ │ │ ├── header.component.spec.ts │ │ │ │ └── header.component.ts │ │ │ └── home │ │ │ │ ├── home.component.html │ │ │ │ ├── home.component.scss │ │ │ │ ├── home.component.spec.ts │ │ │ │ └── home.component.ts │ │ ├── config │ │ │ ├── app-config.const.ts │ │ │ └── router-config.const.ts │ │ ├── core.module.spec.ts │ │ ├── core.module.ts │ │ ├── interceptors │ │ │ ├── api.interceptor.spec.ts │ │ │ └── api.interceptor.ts │ │ └── interfaces │ │ │ ├── app-config.interface.ts │ │ │ ├── app-environment.interface.ts │ │ │ ├── router-config.interface.ts │ │ │ └── translations.interface.ts │ ├── features │ │ └── user │ │ │ ├── user.component.html │ │ │ ├── user.component.scss │ │ │ ├── user.component.spec.ts │ │ │ ├── user.component.ts │ │ │ └── user.module.ts │ ├── lazy │ │ └── users │ │ │ ├── users-routing.module.ts │ │ │ ├── users.component.html │ │ │ ├── users.component.scss │ │ │ ├── users.component.spec.ts │ │ │ ├── users.component.ts │ │ │ ├── users.module.spec.ts │ │ │ └── users.module.ts │ └── shared │ │ ├── shared.module.spec.ts │ │ └── shared.module.ts ├── assets │ ├── .gitkeep │ └── images │ │ └── contents │ │ ├── angular-best-boilerplate-app.png │ │ ├── angular-best-boilerplate-components.png │ │ ├── angular-best-boilerplate-general.png │ │ ├── angular-best-boilerplate-state.png │ │ └── angular-best-boilerplate-workflow.png ├── environments │ ├── environment.dev.ts │ ├── environment.prod.ts │ ├── environment.stage.ts │ ├── environment.ts │ └── environment.uat.ts ├── favicon.ico ├── index.html ├── karma.conf.js ├── lib │ └── js │ │ └── modernizr-touch-events.js ├── main.ts ├── polyfills.ts ├── styles.scss ├── styles │ ├── _reset.scss │ ├── _settings.scss │ ├── general │ │ ├── _animations.scss │ │ ├── _backgrounds.scss │ │ ├── _buttons.scss │ │ ├── _decorations.scss │ │ ├── _forms.scss │ │ ├── _icons.scss │ │ ├── _images.scss │ │ ├── _layout.scss │ │ └── _typography.scss │ ├── main.scss │ ├── settings │ │ ├── _extendables.scss │ │ ├── _fonts.scss │ │ ├── _media.scss │ │ ├── _mixins.scss │ │ ├── _paths.scss │ │ └── _variables.scss │ ├── templates │ │ ├── _card.scss │ │ ├── _notifications.scss │ │ └── _page.scss │ └── vendors │ │ └── _angular-material.scss ├── test.ts ├── test │ ├── components │ │ ├── dummy-app.component.ts │ │ ├── dummy-home.component.ts │ │ ├── dummy-login.component.ts │ │ └── dummy-search.component.ts │ ├── mocks │ │ ├── routes.mock.ts │ │ └── users.mock.ts │ ├── spies │ │ └── example-service.spy.ts │ └── stubs │ │ └── example-service.stub.ts ├── translations │ ├── en_GB.const.ts │ └── pl_PL.const.ts ├── tsconfig.app.json ├── tsconfig.spec.json ├── tslint.json └── web.config ├── 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 | yarn-error.log 34 | testem.log 35 | /typings 36 | 37 | # System Files 38 | .DS_Store 39 | Thumbs.db 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular Best Boilerplate 2 | 3 | This project is created for Angular 8+. 4 | 5 | _...To not to reinvent the wheel again_. 6 | 7 | ## Table of Contents 8 | 1. [What this repo contains](#contents) 9 | 2. [Getting Started](#getting-started) 10 | 3. [CLI](#cli) 11 | 4. [General Concept](#general) 12 | 5. [Application Concept](#application) 13 | 6. [Some hints](#hints) 14 | 7. [Appendix](#appendix) 15 | 16 | ## TODO 17 | 18 | _- Simplify **Getting Started** - interactive dialog or @schematics_ 19 | 20 | _- TSLint settings_ 21 | 22 | _- UIkit, Style Guide or Storybook_ 23 | 24 | 25 | 26 | ## What this repo contains 27 | 28 | 1. Auto `ng build --prod` attached on Git/Husky hook "pre-push" to avoid some of build failures during a deployment of the app. 29 | 2. Common SCSS files in `src/styles`. 30 | 3. Extendable abstract AppController in `src/app/app.controller.ts`. 31 | 4. Proxying by `proxy.conf.json` on `npm start`. 32 | 5. Webpack Bundle Analyzer on `npm run analyzer`. 33 | 6. ApiInterceptor in `src/app/core/interceptors/api.interceptor.ts` to: 34 | - set base API url taken from `src/environments` directory 35 | - set recommended HTTP headers (security) 36 | 7. `src/lib/js/modernizr-touch-events.js` indicates if the browser supports the W3C Touch Events API. 37 | 8. Auto-refreshing the browser tab title on route change (see `src/app/app.component.ts`) 38 | 9. .htaccess (Apache) and web.config (IIS) configuring the server to return the application's host page (index.html) when asked for a file that it does not have. 39 | 40 | 41 | 42 | ## Getting Started 43 | 44 | 1. `git clone` this repo. 45 | 2. `npm install` 46 | 3. Remove the contents of `src/assets/images/contents`. 47 | 4. Put your translations in `src/translations` and set them in `src/app/app.controller.ts`. 48 | 5. Add your custom core configuration in `src/app/core/config/app-config.const.ts` (you will need it in the future). 49 | 6. Add your router config in `src/app/core/config/router-config.const.ts`. 50 | 7. Remove `proxy.conf.json` if you don't need proxying. Otherwise, put your target there. 51 | 8. `npm start` calls `ng serve --proxy-config=proxy.conf.json` from `package.json` (remove 6th line from `package.json` if you don't need proxying). 52 | 9. Set your project name in `package.json`: lines 2nd and 12th. 53 | 10. Set your API urls in `src/environments`. 54 | 11. Replace the default Angular favicon with yours. 55 | 12. Verify whether you need `web.config` (IIS) or `.htaccess` (Apache) in `src` and `angular.json` (`projects` > `architect` > `build` > `options` > `assets`). If you need `web.config`, verify security headers in the file. 56 | 13. You are ready to work. 57 | 58 | 59 | 60 | ## CLI 61 | 62 | ### Build 63 | 64 | `ng build --configuration=production` or `ng build --prod` 65 | 66 | `ng build --configuration=dev` 67 | 68 | `ng build --configuration=stage` 69 | 70 | `ng build --configuration=uat` 71 | 72 | ### Webpack Bundle Analyzer 73 | 74 | `npm run analyzer` 75 | 76 | 77 | 78 | 79 | ## General concept 80 | 81 | Always keep your project directories neat and clean so that your project does not become another legacy monster from which you will want to escape ASAP. 82 | 83 | _Notice._ This document uses the shortcut **ASG** (:smile:) - Angular Style Guide. 84 | 85 | The directories you always need: 86 | 1. App. 87 | 2. Environments. 88 | 3. Assets. 89 | 4. Test. 90 | 5. Lib. 91 | 6. Styles. 92 | 7. Translations. 93 | 94 | ![General Concept](https://raw.githubusercontent.com/bartuck/angular-best-boilerplate/master/src/assets/images/contents/angular-best-boilerplate-general.png) 95 | 96 | 97 | 98 | ## Application concept 99 | 100 | The directories you always need: 101 | 1. Core. 102 | 2. Shared. 103 | 3. Features. 104 | 4. Lazy (lazy loaded folders). 105 | 106 | ![Application Workflow](https://raw.githubusercontent.com/bartuck/angular-best-boilerplate/master/src/assets/images/contents/angular-best-boilerplate-workflow.png) 107 | 108 | The class that some of your components will need to extend: **AppController**. It stores: 109 | - public configuration and settings, 110 | - router data, 111 | - current translations. 112 | 113 | ![Application Concept](https://raw.githubusercontent.com/bartuck/angular-best-boilerplate/master/src/assets/images/contents/angular-best-boilerplate-app.png) 114 | 115 | ### Core 116 | 117 | "MUST-HAVE" of your application: 118 | - configurations and settings of your application, i.e. available languages and locales, routing data, web storage keys, date format, 119 | - global services, i.e. http, 120 | - a skeleton of the layout - core components like header and footer, 121 | - core modules, i.e. dialog. 122 | 123 | ### Shared 124 | 125 | From ASG (https://angular.io/guide/styleguide#shared-feature-module): 126 | 127 | **Do** create a feature module named SharedModule in a shared folder; for example, app/shared/shared.module.ts defines SharedModule. 128 | 129 | **Do** declare components, directives, and pipes in a shared module when those items will be re-used and referenced by the components declared in other feature modules. 130 | 131 | **Consider** using the name SharedModule when the contents of a shared module are referenced across the entire application. 132 | 133 | **Consider not providing services in shared modules.** Services are usually singletons that are provided once for the entire application or in a particular feature module. There are exceptions, however. For example, in the sample code that follows, notice that the SharedModule provides FilterTextService. This is acceptable here because the service is stateless;that is, the consumers of the service aren't impacted by new instances. 134 | 135 | **Do** import all modules required by the assets in the SharedModule; for example, CommonModule and FormsModule. 136 | 137 | (...) 138 | 139 | **Avoid** specifying app-wide singleton providers in a SharedModule. Intentional singletons are OK. Take care. 140 | 141 | **Why?** A lazy loaded feature module that imports that shared module will make its own copy of the service and likely have undesirable results. 142 | 143 | **Why?** You don't want each module to have its own separate instance of singleton services. Yet there is a real danger of that happening if the SharedModule provides a service. 144 | 145 | ### Features 146 | 147 | From ASG (https://angular.io/guide/styleguide#folders-by-feature-structure): 148 | 149 | (...) 150 | 151 | **Why?** Helps reduce the app from becoming cluttered through organizing the content and keeping them aligned with the LIFT guidelines. 152 | 153 | **Why?** When there are a lot of files, for example 10+, locating them is easier with a consistent folder structure and more difficult in a flat structure. 154 | 155 | ### Lazy 156 | 157 | From ASG (https://angular.io/guide/styleguide#lazy-loaded-folders): 158 | 159 | A distinct application feature or workflow may be lazy loaded or loaded on demand rather than when the application starts. 160 | 161 | **Do** put the contents of lazy loaded features in a lazy loaded folder. A typical lazy loaded folder contains a routing component, its child components, and their related assets and modules. 162 | 163 | **Why?** The folder makes it easy to identify and isolate the feature content. 164 | 165 | (...) 166 | 167 | **Avoid** allowing modules in sibling and parent folders to directly import a module in a lazy loaded feature. 168 | 169 | **Why?** Directly importing and using a module will load it immediately when the intention is to load it on demand. 170 | 171 | 172 | 173 | ## Some Hints 174 | 175 | **Avoid** having multiple config files per module (*.const.ts). 176 | 177 | **ASG: Why?** No one wants to search for a file through seven levels of folders. A flat structure is easy to scan. 178 | 179 | **ASG: Do** start small but keep in mind where the app is heading down the road. 180 | 181 | **ASG: Do** have a near term view of implementation and a long term vision. 182 | 183 | **Consider** using Server Side Rendering. 184 | 185 | **Consider** creating your features in a separated "environment" like Storybook as an element of your Design System. 186 | 187 | **Consider** putting info about your styles and components in a separated module (a'la old good UI-kit) to be DRY and KISS, and to help other developers understand application CSS styles set. 188 | 189 | **Consider** proxying to a backend server to be "locally" independent of your API (https://angular.io/guide/build#proxying-to-a-backend-server). 190 | 191 | **Consider** using services and rxjs subjects to interact between components instead of libraries like ngrx, ngxs or akita. 192 | 193 | **Why?** I know those libraries are trendy and fancy, however you may experience problems regarding state management only in the case of huge, complex or unusual applications. [Read more here](https://blog.angular-university.io/angular-2-redux-ngrx-rxjs/). 194 | 195 | ![State Management](https://raw.githubusercontent.com/bartuck/angular-best-boilerplate/master/src/assets/images/contents/angular-best-boilerplate-state.png) 196 | 197 | _I'm not the author of this picture._ 198 | 199 | **Do** create dumb components - no external dependencies, no side effects. [Read more here](https://medium.com/@jtomaszewski/how-to-write-good-composable-and-pure-components-in-angular-2-1756945c0f5b). 200 | 201 | ![Components](https://raw.githubusercontent.com/bartuck/angular-best-boilerplate/master/src/assets/images/contents/angular-best-boilerplate-components.png) 202 | 203 | _I'm not the author of this picture._ 204 | 205 | 206 | 207 | 208 | ## Appendix 209 | 210 | Remember about the performance: 211 | 1. Runtime optimizations. 212 | 2. Network performance. 213 | 214 | Please refer to https://github.com/mgechev/angular-performance-checklist 215 | 216 | Read https://christianlydemann.com/the-complete-guide-to-angular-load-time-optimization 217 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "angular-best-boilerplate": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": { 12 | "@schematics/angular:component": { 13 | "styleext": "scss" 14 | } 15 | }, 16 | "architect": { 17 | "build": { 18 | "builder": "@angular-devkit/build-angular:browser", 19 | "options": { 20 | "outputPath": "dist/angular-best-boilerplate", 21 | "index": "src/index.html", 22 | "main": "src/main.ts", 23 | "polyfills": "src/polyfills.ts", 24 | "tsConfig": "src/tsconfig.app.json", 25 | "assets": [ 26 | "src/favicon.ico", 27 | "src/assets", 28 | "src/web.config", 29 | "src/.htaccess" 30 | ], 31 | "styles": [ 32 | "src/styles.scss" 33 | ], 34 | "scripts": [ 35 | "src/lib/js/modernizr-touch-events.js" 36 | ] 37 | }, 38 | "configurations": { 39 | "production": { 40 | "fileReplacements": [ 41 | { 42 | "replace": "src/environments/environment.ts", 43 | "with": "src/environments/environment.prod.ts" 44 | } 45 | ], 46 | "optimization": true, 47 | "outputHashing": "all", 48 | "sourceMap": false, 49 | "extractCss": true, 50 | "namedChunks": false, 51 | "aot": true, 52 | "extractLicenses": true, 53 | "vendorChunk": false, 54 | "buildOptimizer": true 55 | }, 56 | "dev": { 57 | "fileReplacements": [ 58 | { 59 | "replace": "src/environments/environment.ts", 60 | "with": "src/environments/environment.dev.ts" 61 | } 62 | ], 63 | "optimization": true, 64 | "outputHashing": "all", 65 | "sourceMap": false, 66 | "extractCss": true, 67 | "namedChunks": false, 68 | "aot": true, 69 | "extractLicenses": true, 70 | "vendorChunk": false, 71 | "buildOptimizer": true 72 | }, 73 | "stage": { 74 | "fileReplacements": [ 75 | { 76 | "replace": "src/environments/environment.ts", 77 | "with": "src/environments/environment.stage.ts" 78 | } 79 | ], 80 | "optimization": true, 81 | "outputHashing": "all", 82 | "sourceMap": false, 83 | "extractCss": true, 84 | "namedChunks": false, 85 | "aot": true, 86 | "extractLicenses": true, 87 | "vendorChunk": false, 88 | "buildOptimizer": true 89 | }, 90 | "uat": { 91 | "fileReplacements": [ 92 | { 93 | "replace": "src/environments/environment.ts", 94 | "with": "src/environments/environment.uat.ts" 95 | } 96 | ], 97 | "optimization": true, 98 | "outputHashing": "all", 99 | "sourceMap": false, 100 | "extractCss": true, 101 | "namedChunks": false, 102 | "aot": true, 103 | "extractLicenses": true, 104 | "vendorChunk": false, 105 | "buildOptimizer": true 106 | } 107 | } 108 | }, 109 | "serve": { 110 | "builder": "@angular-devkit/build-angular:dev-server", 111 | "options": { 112 | "browserTarget": "angular-best-boilerplate:build" 113 | }, 114 | "configurations": { 115 | "production": { 116 | "browserTarget": "angular-best-boilerplate:build:production" 117 | } 118 | } 119 | }, 120 | "extract-i18n": { 121 | "builder": "@angular-devkit/build-angular:extract-i18n", 122 | "options": { 123 | "browserTarget": "angular-best-boilerplate:build" 124 | } 125 | }, 126 | "test": { 127 | "builder": "@angular-devkit/build-angular:karma", 128 | "options": { 129 | "main": "src/test.ts", 130 | "polyfills": "src/polyfills.ts", 131 | "tsConfig": "src/tsconfig.spec.json", 132 | "karmaConfig": "src/karma.conf.js", 133 | "styles": [ 134 | "src/styles.scss" 135 | ], 136 | "scripts": [], 137 | "assets": [ 138 | "src/favicon.ico", 139 | "src/assets" 140 | ] 141 | } 142 | }, 143 | "lint": { 144 | "builder": "@angular-devkit/build-angular:tslint", 145 | "options": { 146 | "tsConfig": [ 147 | "src/tsconfig.app.json", 148 | "src/tsconfig.spec.json" 149 | ], 150 | "exclude": [ 151 | "**/node_modules/**" 152 | ] 153 | } 154 | } 155 | } 156 | }, 157 | "angular-best-boilerplate-e2e": { 158 | "root": "e2e/", 159 | "projectType": "application", 160 | "architect": { 161 | "e2e": { 162 | "builder": "@angular-devkit/build-angular:protractor", 163 | "options": { 164 | "protractorConfig": "e2e/protractor.conf.js", 165 | "devServerTarget": "angular-best-boilerplate:serve" 166 | }, 167 | "configurations": { 168 | "production": { 169 | "devServerTarget": "angular-best-boilerplate:serve:production" 170 | } 171 | } 172 | }, 173 | "lint": { 174 | "builder": "@angular-devkit/build-angular:tslint", 175 | "options": { 176 | "tsConfig": "e2e/tsconfig.e2e.json", 177 | "exclude": [ 178 | "**/node_modules/**" 179 | ] 180 | } 181 | } 182 | } 183 | } 184 | }, 185 | "defaultProject": "angular-best-boilerplate" 186 | } 187 | -------------------------------------------------------------------------------- /browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # 5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed 6 | 7 | > 0.5% 8 | last 2 versions 9 | Firefox ESR 10 | not dead 11 | not IE 9-11 -------------------------------------------------------------------------------- /e2e/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 | './src/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: require('path').join(__dirname, './tsconfig.e2e.json') 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('workspace-project App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to angular-best-boilerplate!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /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/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-best-boilerplate", 3 | "version": "0.2.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve --open --proxy-config=proxy.conf.json", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e", 11 | "stats": "ng build --prod --stats-json", 12 | "analyze": "webpack-bundle-analyzer dist/angular-best-boilerplate/stats-es5.json", 13 | "analyzer": "npm run stats && npm run analyze" 14 | }, 15 | "private": true, 16 | "husky": { 17 | "hooks": { 18 | "pre-push": "ng build --prod" 19 | } 20 | }, 21 | "dependencies": { 22 | "@angular/animations": "^8.0.2", 23 | "@angular/common": "^8.0.2", 24 | "@angular/compiler": "^8.0.2", 25 | "@angular/core": "^8.0.2", 26 | "@angular/forms": "^8.0.2", 27 | "@angular/platform-browser": "^8.0.2", 28 | "@angular/platform-browser-dynamic": "^8.0.2", 29 | "@angular/router": "^8.0.2", 30 | "core-js": "^2.5.4", 31 | "rxjs": "~6.5.2", 32 | "tslib": "^1.9.0", 33 | "zone.js": "~0.9.1" 34 | }, 35 | "devDependencies": { 36 | "@angular-devkit/build-angular": "~0.800.0", 37 | "@angular/cli": "~8.0.3", 38 | "@angular/compiler-cli": "^8.0.2", 39 | "@angular/language-service": "^8.0.2", 40 | "@types/jasmine": "~2.8.8", 41 | "@types/jasminewd2": "~2.0.3", 42 | "@types/node": "~8.9.4", 43 | "codelyzer": "^5.0.1", 44 | "husky": "^2.4.1", 45 | "jasmine-core": "~2.99.1", 46 | "jasmine-spec-reporter": "~4.2.1", 47 | "karma": "^4.1.0", 48 | "karma-chrome-launcher": "~2.2.0", 49 | "karma-coverage-istanbul-reporter": "~2.0.1", 50 | "karma-jasmine": "~1.1.2", 51 | "karma-jasmine-html-reporter": "^0.2.2", 52 | "protractor": "~5.4.0", 53 | "ts-node": "~7.0.0", 54 | "tslint": "~5.11.0", 55 | "typescript": "~3.4.5", 56 | "webpack-bundle-analyzer": "^3.3.2" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /proxy.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "/api": { 3 | "target": "https://user:password@dev-api.example.pl", 4 | "secure": false, 5 | "logLevel": "debug", 6 | "changeOrigin": true, 7 | "pathRewrite": { 8 | "^/api": "" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine On 2 | # If an existing asset or directory is requested go to it as it is 3 | RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR] 4 | RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d 5 | RewriteRule ^ - [L] 6 | 7 | # If the requested resource doesn't exist, use index.html 8 | RewriteRule ^ /index.html 9 | -------------------------------------------------------------------------------- /src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | 4 | import {AppController} from './app.controller'; 5 | import { HomeComponent } from './core/components/home/home.component'; 6 | import { routerConfig } from './core/config/router-config.const'; 7 | 8 | const routes: Routes = [ 9 | { 10 | path: '', 11 | component: HomeComponent, 12 | data: { 13 | title: AppController.trans().home 14 | } 15 | }, 16 | { 17 | path: routerConfig.users.base, 18 | loadChildren: () => import('./lazy/users/users.module').then(mod => mod.UsersModule) 19 | } 20 | ]; 21 | 22 | @NgModule({ 23 | imports: [RouterModule.forRoot(routes)], 24 | exports: [RouterModule] 25 | }) 26 | export class AppRoutingModule { } 27 | -------------------------------------------------------------------------------- /src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartuck/angular-best-boilerplate/480a621ead3d62d30e0e12c8dfd9f1973dc935e0/src/app/app.component.css -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
6 | 7 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | import { Title } from '@angular/platform-browser'; 4 | import { ActivatedRoute, Router } from '@angular/router'; 5 | import { RouterTestingModule } from '@angular/router/testing'; 6 | 7 | import { DummyHomeComponent } from '../test/components/dummy-home.component'; 8 | import { DummyLoginComponent } from '../test/components/dummy-login.component'; 9 | import { DummySearchComponent } from '../test/components/dummy-search.component'; 10 | import { testingRoutes } from '../test/mocks/routes.mock'; 11 | import { AppComponent } from './app.component'; 12 | import { appConfig } from './core/config/app-config.const'; 13 | 14 | describe('AppComponent', () => { 15 | let fixture: ComponentFixture; 16 | let component: AppComponent; 17 | let titleService: Title; 18 | let router: Router; 19 | let route: ActivatedRoute; 20 | 21 | beforeEach(() => { 22 | TestBed.configureTestingModule({ 23 | imports: [RouterTestingModule.withRoutes(testingRoutes)], 24 | declarations: [ 25 | AppComponent, 26 | DummyHomeComponent, 27 | DummySearchComponent, 28 | DummyLoginComponent 29 | ], 30 | providers: [Title], 31 | schemas: [CUSTOM_ELEMENTS_SCHEMA] 32 | }).compileComponents(); 33 | 34 | fixture = TestBed.createComponent(AppComponent); 35 | component = fixture.componentInstance; 36 | titleService = TestBed.get(Title); 37 | router = TestBed.get(Router); 38 | route = TestBed.get(ActivatedRoute); 39 | 40 | fixture.detectChanges(); 41 | }); 42 | 43 | it('should create the app', async(() => { 44 | expect(component).toBeTruthy(); 45 | })); 46 | 47 | describe('#ngOnInit', () => { 48 | beforeEach(() => { 49 | component.ngOnInit(); 50 | }); 51 | 52 | it('should set the browser tab title', () => { 53 | router.navigate(['/search']).then(() => { 54 | const currTitle = titleService.getTitle(); 55 | const expectedTitle = `Search${appConfig.browserTabTitleDelimiter}${appConfig.appTitle}`; 56 | 57 | expect(currTitle).toEqual(expectedTitle); 58 | }); 59 | }); 60 | 61 | it('should set the browser tab title as the application title', () => { 62 | router.navigate(['/']).then(() => { 63 | const currTitle = titleService.getTitle(); 64 | const expectedTitle = `${appConfig.appTitle}`; 65 | 66 | expect(currTitle).toEqual(expectedTitle); 67 | }); 68 | }); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Title } from '@angular/platform-browser'; 3 | import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; 4 | import { filter, map, mergeMap } from 'rxjs/operators'; 5 | 6 | import { AppController } from './app.controller'; 7 | 8 | @Component({ 9 | selector: 'app-root', 10 | templateUrl: './app.component.html', 11 | styleUrls: ['./app.component.css'] 12 | }) 13 | export class AppComponent extends AppController implements OnInit { 14 | 15 | constructor(private router: Router, 16 | private route: ActivatedRoute, 17 | private titleService: Title) { 18 | super(); 19 | } 20 | 21 | ngOnInit(): void { 22 | this.setBrowserTabTitle(); 23 | } 24 | 25 | private setBrowserTabTitle(): void { 26 | this.router.events.pipe( 27 | filter(event => event instanceof NavigationEnd), 28 | map(() => this.route), 29 | map(route => this.getRouteFirstChild(route)), 30 | filter(route => route.outlet === 'primary'), 31 | mergeMap(route => route.data), 32 | ).subscribe(event => this.titleService.setTitle(this.buildTitle(event['title']))); 33 | } 34 | 35 | private getRouteFirstChild(route: ActivatedRoute): ActivatedRoute { 36 | while (route.firstChild) { 37 | route = route.firstChild; 38 | } 39 | 40 | return route; 41 | } 42 | 43 | private buildTitle(pageTitle: string): string { 44 | if (pageTitle && pageTitle !== this.trans.home) { 45 | return [pageTitle, this.config.appTitle].join(this.config.browserTabTitleDelimiter); 46 | } 47 | 48 | return this.config.appTitle; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/app/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { en_GB } from '../translations/en_GB.const'; 2 | import { appConfig } from './core/config/app-config.const'; 3 | import { routerConfig } from './core/config/router-config.const'; 4 | import { AppConfig } from './core/interfaces/app-config.interface'; 5 | import { RouterConfig } from './core/interfaces/router-config.interface'; 6 | import { Translations } from './core/interfaces/translations.interface'; 7 | 8 | export abstract class AppController { 9 | 10 | /** 11 | * Application configuration for all environments 12 | */ 13 | public config: AppConfig = appConfig; 14 | 15 | /** 16 | * Routing data 17 | */ 18 | public routing: RouterConfig = routerConfig; 19 | 20 | /** 21 | * Current translations 22 | */ 23 | public trans: Translations = en_GB; 24 | 25 | /** 26 | * Current translations available anywhere 27 | */ 28 | static trans(): Translations { 29 | return en_GB; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | 4 | import { AppRoutingModule } from './app-routing.module'; 5 | import { AppComponent } from './app.component'; 6 | import { CoreModule } from './core/core.module'; 7 | 8 | @NgModule({ 9 | declarations: [ 10 | AppComponent 11 | ], 12 | imports: [ 13 | BrowserModule, 14 | AppRoutingModule, 15 | CoreModule 16 | ], 17 | providers: [], 18 | bootstrap: [AppComponent] 19 | }) 20 | export class AppModule { } 21 | -------------------------------------------------------------------------------- /src/app/core/components/footer/footer.component.html: -------------------------------------------------------------------------------- 1 |
2 |

Footer

3 | 4 | © 5 | Bartuck Avatar 9 | Thank you for downloading angular-best-boilerplate! 10 | 11 |
12 | -------------------------------------------------------------------------------- /src/app/core/components/footer/footer.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartuck/angular-best-boilerplate/480a621ead3d62d30e0e12c8dfd9f1973dc935e0/src/app/core/components/footer/footer.component.scss -------------------------------------------------------------------------------- /src/app/core/components/footer/footer.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { FooterComponent } from './footer.component'; 4 | 5 | describe('FooterComponent', () => { 6 | let component: FooterComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ FooterComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(FooterComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/core/components/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.scss'] 7 | }) 8 | export class FooterComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/core/components/header/header.component.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | {{trans.language}}: {{config.defaultLanguage}} 4 |

5 | Angular Logo 7 |

Header

8 |
25 |
26 | -------------------------------------------------------------------------------- /src/app/core/components/header/header.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartuck/angular-best-boilerplate/480a621ead3d62d30e0e12c8dfd9f1973dc935e0/src/app/core/components/header/header.component.scss -------------------------------------------------------------------------------- /src/app/core/components/header/header.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { RouterTestingModule } from '@angular/router/testing'; 4 | import { HeaderComponent } from './header.component'; 5 | 6 | describe('HeaderComponent', () => { 7 | let component: HeaderComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | imports: [RouterTestingModule], 13 | declarations: [HeaderComponent] 14 | }) 15 | .compileComponents(); 16 | })); 17 | 18 | beforeEach(() => { 19 | fixture = TestBed.createComponent(HeaderComponent); 20 | component = fixture.componentInstance; 21 | fixture.detectChanges(); 22 | }); 23 | 24 | it('should create', () => { 25 | expect(component).toBeTruthy(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/app/core/components/header/header.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | import { AppController } from '../../../app.controller'; 4 | 5 | @Component({ 6 | selector: 'app-header', 7 | templateUrl: './header.component.html', 8 | styleUrls: ['./header.component.scss'] 9 | }) 10 | export class HeaderComponent extends AppController implements OnInit { 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/core/components/home/home.component.html: -------------------------------------------------------------------------------- 1 |
2 | Contents... 3 |
4 | -------------------------------------------------------------------------------- /src/app/core/components/home/home.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartuck/angular-best-boilerplate/480a621ead3d62d30e0e12c8dfd9f1973dc935e0/src/app/core/components/home/home.component.scss -------------------------------------------------------------------------------- /src/app/core/components/home/home.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { HomeComponent } from './home.component'; 4 | 5 | describe('HomeComponent', () => { 6 | let component: HomeComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ HomeComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(HomeComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/core/components/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-home', 5 | templateUrl: './home.component.html', 6 | styleUrls: ['./home.component.scss'] 7 | }) 8 | export class HomeComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/core/config/app-config.const.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Global Configuration, i.e. languages, locale, date formats etc. 3 | * 4 | * Shared settings for all environments: dev, prod, stage etc. 5 | */ 6 | import { AppConfig } from '../interfaces/app-config.interface'; 7 | 8 | export const appConfig: AppConfig = { 9 | appTitle: 'Angular Best Boilerplate', 10 | browserTabTitleDelimiter: ' | ', 11 | defaultLanguage: 'en_GB', 12 | availableLanguages: ['en_GB', 'pl_PL'] 13 | }; 14 | -------------------------------------------------------------------------------- /src/app/core/config/router-config.const.ts: -------------------------------------------------------------------------------- 1 | import { RouterConfig } from '../interfaces/router-config.interface'; 2 | 3 | export const routerConfig: RouterConfig = { 4 | users: { 5 | base: 'users' 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /src/app/core/core.module.spec.ts: -------------------------------------------------------------------------------- 1 | import { CoreModule } from './core.module'; 2 | 3 | describe('CoreModule', () => { 4 | let coreModule: CoreModule; 5 | 6 | beforeEach(() => { 7 | coreModule = new CoreModule(); 8 | }); 9 | 10 | it('should create an instance', () => { 11 | expect(coreModule).toBeTruthy(); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /src/app/core/core.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { RouterModule } from '@angular/router'; 4 | 5 | import { FooterComponent } from './components/footer/footer.component'; 6 | import { HeaderComponent } from './components/header/header.component'; 7 | import { HomeComponent } from './components/home/home.component'; 8 | 9 | @NgModule({ 10 | imports: [ 11 | CommonModule, 12 | RouterModule 13 | ], 14 | declarations: [ 15 | HeaderComponent, 16 | FooterComponent, 17 | HomeComponent 18 | ], 19 | exports: [ 20 | HeaderComponent, 21 | FooterComponent 22 | ] 23 | }) 24 | export class CoreModule { 25 | } 26 | -------------------------------------------------------------------------------- /src/app/core/interceptors/api.interceptor.spec.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient, HTTP_INTERCEPTORS } from '@angular/common/http'; 2 | import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; 3 | import { TestBed } from '@angular/core/testing'; 4 | 5 | import { environment } from '../../../environments/environment'; 6 | import { ApiInterceptor } from './api.interceptor'; 7 | 8 | describe('ApiInterceptor', () => { 9 | let httpClient: HttpClient; 10 | let httpMock: HttpTestingController; 11 | 12 | beforeEach(() => { 13 | TestBed.configureTestingModule({ 14 | imports: [HttpClientTestingModule], 15 | providers: [ 16 | { provide: HTTP_INTERCEPTORS, useClass: ApiInterceptor, multi: true } 17 | ] 18 | }); 19 | httpClient = TestBed.get(HttpClient); 20 | httpMock = TestBed.get(HttpTestingController); 21 | }); 22 | 23 | afterEach(() => httpMock.verify()); 24 | 25 | it('should add correct Content-Security-Policy', () => { 26 | const header = 'Content-Security-Policy'; 27 | const expectedHeader = `frame-ancestors ${environment.security.allowedOrigins}`; 28 | 29 | httpClient.get('/').subscribe(() => {}); 30 | httpMock 31 | .expectOne(req => (req.headers.has(header) && req.headers.get(header) === expectedHeader)) 32 | .flush({}); 33 | }); 34 | 35 | it('should add correct X-Frame-Options', () => { 36 | const header = 'X-Frame-Options'; 37 | const expectedHeader = `ALLOW-FROM ${environment.security.allowedOrigins}`; 38 | 39 | httpClient.get('/').subscribe(() => {}); 40 | httpMock 41 | .expectOne(req => (req.headers.has(header) && req.headers.get(header) === expectedHeader)) 42 | .flush({}); 43 | }); 44 | 45 | it('should add correct X-XSS-Protection', () => { 46 | const header = 'X-XSS-Protection'; 47 | const expectedHeader = '1; mode=block'; 48 | 49 | httpClient.get('/').subscribe(() => {}); 50 | httpMock 51 | .expectOne(req => (req.headers.has(header) && req.headers.get(header) === expectedHeader)) 52 | .flush({}); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /src/app/core/interceptors/api.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; 2 | import { Injectable } from '@angular/core'; 3 | import { Observable } from 'rxjs'; 4 | 5 | import { environment } from '../../../environments/environment'; 6 | 7 | @Injectable() 8 | export class ApiInterceptor implements HttpInterceptor { 9 | 10 | private baseUrl = environment.baseApiUrl; 11 | 12 | intercept(req: HttpRequest, next: HttpHandler): Observable> { 13 | const apiUrl = `${this.baseUrl}/${req.url}`; 14 | const parsedApiUrl = apiUrl.replace('///', '/').replace('//', '/'); 15 | 16 | req = req.clone({ 17 | url: parsedApiUrl, 18 | setHeaders: { 19 | 'Content-Security-Policy': `frame-ancestors ${environment.security.allowedOrigins}`, 20 | 'X-Frame-Options': `ALLOW-FROM ${environment.security.allowedOrigins}`, 21 | 'X-XSS-Protection': '1; mode=block' 22 | } 23 | }); 24 | 25 | return next.handle(req); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/app/core/interfaces/app-config.interface.ts: -------------------------------------------------------------------------------- 1 | export interface AppConfig { 2 | appTitle: string; 3 | browserTabTitleDelimiter: string; 4 | defaultLanguage: string; 5 | availableLanguages: string[]; 6 | } 7 | -------------------------------------------------------------------------------- /src/app/core/interfaces/app-environment.interface.ts: -------------------------------------------------------------------------------- 1 | export interface AppEnvironment { 2 | production: boolean; 3 | baseApiUrl: string; 4 | security: { 5 | allowedOrigins: string; 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /src/app/core/interfaces/router-config.interface.ts: -------------------------------------------------------------------------------- 1 | import { Params } from '@angular/router'; 2 | 3 | export interface RouterConfig { 4 | users: Params | any; 5 | } 6 | 7 | -------------------------------------------------------------------------------- /src/app/core/interfaces/translations.interface.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Global Translations Modal in one place 3 | * 4 | * Keys should be as snake_case to distinguish them from classes properties in components and their views 5 | */ 6 | export interface Translations { 7 | hello_world: string; 8 | users: string; 9 | home: string; 10 | language: string; 11 | } 12 | -------------------------------------------------------------------------------- /src/app/features/user/user.component.html: -------------------------------------------------------------------------------- 1 |

2 | user works! 3 |

4 | -------------------------------------------------------------------------------- /src/app/features/user/user.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartuck/angular-best-boilerplate/480a621ead3d62d30e0e12c8dfd9f1973dc935e0/src/app/features/user/user.component.scss -------------------------------------------------------------------------------- /src/app/features/user/user.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { UserComponent } from './user.component'; 4 | 5 | describe('UserComponent', () => { 6 | let component: UserComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ UserComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(UserComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/features/user/user.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-user', 5 | templateUrl: './user.component.html', 6 | styleUrls: ['./user.component.scss'] 7 | }) 8 | export class UserComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/features/user/user.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | import { UserComponent } from './user.component'; 4 | 5 | @NgModule({ 6 | imports: [ 7 | CommonModule 8 | ], 9 | declarations: [UserComponent], 10 | exports: [UserComponent] 11 | }) 12 | export class UserModule { } 13 | -------------------------------------------------------------------------------- /src/app/lazy/users/users-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | 4 | import {AppController} from '../../app.controller'; 5 | import { UsersComponent } from './users.component'; 6 | 7 | const routes: Routes = [ 8 | { 9 | path: '', 10 | component: UsersComponent, 11 | data: { 12 | title: AppController.trans().users 13 | } 14 | } 15 | ]; 16 | 17 | @NgModule({ 18 | imports: [RouterModule.forChild(routes)], 19 | exports: [RouterModule] 20 | }) 21 | export class UsersRoutingModule { } 22 | -------------------------------------------------------------------------------- /src/app/lazy/users/users.component.html: -------------------------------------------------------------------------------- 1 |

2 | users works! 3 |

4 | 5 | -------------------------------------------------------------------------------- /src/app/lazy/users/users.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartuck/angular-best-boilerplate/480a621ead3d62d30e0e12c8dfd9f1973dc935e0/src/app/lazy/users/users.component.scss -------------------------------------------------------------------------------- /src/app/lazy/users/users.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | 4 | import { UsersComponent } from './users.component'; 5 | 6 | describe('UsersComponent', () => { 7 | let component: UsersComponent; 8 | let fixture: ComponentFixture; 9 | 10 | beforeEach(async(() => { 11 | TestBed.configureTestingModule({ 12 | declarations: [UsersComponent], 13 | schemas: [CUSTOM_ELEMENTS_SCHEMA] 14 | }) 15 | .compileComponents(); 16 | })); 17 | 18 | beforeEach(() => { 19 | fixture = TestBed.createComponent(UsersComponent); 20 | component = fixture.componentInstance; 21 | fixture.detectChanges(); 22 | }); 23 | 24 | it('should create', () => { 25 | expect(component).toBeTruthy(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/app/lazy/users/users.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-users', 5 | templateUrl: './users.component.html', 6 | styleUrls: ['./users.component.scss'] 7 | }) 8 | export class UsersComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/lazy/users/users.module.spec.ts: -------------------------------------------------------------------------------- 1 | import { UsersModule } from './users.module'; 2 | 3 | describe('UsersModule', () => { 4 | let usersModule: UsersModule; 5 | 6 | beforeEach(() => { 7 | usersModule = new UsersModule(); 8 | }); 9 | 10 | it('should create an instance', () => { 11 | expect(usersModule).toBeTruthy(); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /src/app/lazy/users/users.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { UserModule } from '../../features/user/user.module'; 5 | import { UsersRoutingModule } from './users-routing.module'; 6 | import { UsersComponent } from './users.component'; 7 | 8 | @NgModule({ 9 | imports: [ 10 | CommonModule, 11 | UserModule, 12 | UsersRoutingModule 13 | ], 14 | declarations: [UsersComponent] 15 | }) 16 | export class UsersModule { } 17 | 18 | -------------------------------------------------------------------------------- /src/app/shared/shared.module.spec.ts: -------------------------------------------------------------------------------- 1 | import { SharedModule } from './shared.module'; 2 | 3 | describe('SharedModule', () => { 4 | let sharedModule: SharedModule; 5 | 6 | beforeEach(() => { 7 | sharedModule = new SharedModule(); 8 | }); 9 | 10 | it('should create an instance', () => { 11 | expect(sharedModule).toBeTruthy(); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /src/app/shared/shared.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | @NgModule({ 5 | imports: [ 6 | CommonModule 7 | ], 8 | declarations: [] 9 | }) 10 | export class SharedModule { } 11 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartuck/angular-best-boilerplate/480a621ead3d62d30e0e12c8dfd9f1973dc935e0/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/assets/images/contents/angular-best-boilerplate-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartuck/angular-best-boilerplate/480a621ead3d62d30e0e12c8dfd9f1973dc935e0/src/assets/images/contents/angular-best-boilerplate-app.png -------------------------------------------------------------------------------- /src/assets/images/contents/angular-best-boilerplate-components.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartuck/angular-best-boilerplate/480a621ead3d62d30e0e12c8dfd9f1973dc935e0/src/assets/images/contents/angular-best-boilerplate-components.png -------------------------------------------------------------------------------- /src/assets/images/contents/angular-best-boilerplate-general.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartuck/angular-best-boilerplate/480a621ead3d62d30e0e12c8dfd9f1973dc935e0/src/assets/images/contents/angular-best-boilerplate-general.png -------------------------------------------------------------------------------- /src/assets/images/contents/angular-best-boilerplate-state.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartuck/angular-best-boilerplate/480a621ead3d62d30e0e12c8dfd9f1973dc935e0/src/assets/images/contents/angular-best-boilerplate-state.png -------------------------------------------------------------------------------- /src/assets/images/contents/angular-best-boilerplate-workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartuck/angular-best-boilerplate/480a621ead3d62d30e0e12c8dfd9f1973dc935e0/src/assets/images/contents/angular-best-boilerplate-workflow.png -------------------------------------------------------------------------------- /src/environments/environment.dev.ts: -------------------------------------------------------------------------------- 1 | import { AppEnvironment } from '../app/core/interfaces/app-environment.interface'; 2 | 3 | export const environment: AppEnvironment = { 4 | production: true, 5 | baseApiUrl: 'https://dev.example.com/api/v1', 6 | security: { 7 | allowedOrigins: 'https://dev.example.com' 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | import { AppEnvironment } from '../app/core/interfaces/app-environment.interface'; 2 | 3 | export const environment: AppEnvironment = { 4 | production: true, 5 | baseApiUrl: 'https://example.com/api/v1', 6 | security: { 7 | allowedOrigins: 'https://example.com' 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /src/environments/environment.stage.ts: -------------------------------------------------------------------------------- 1 | import { AppEnvironment } from '../app/core/interfaces/app-environment.interface'; 2 | 3 | export const environment: AppEnvironment = { 4 | production: true, 5 | baseApiUrl: 'https://stage.example.com/api/v1', 6 | security: { 7 | allowedOrigins: 'https://stage.example.com' 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /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 | import { AppEnvironment } from '../app/core/interfaces/app-environment.interface'; 6 | 7 | export const environment: AppEnvironment = { 8 | production: false, 9 | baseApiUrl: 'https://dev.example.com/api/v1', 10 | // baseAPI: PROXY_PREFIX_API_URL // using by http proxy, 11 | security: { 12 | allowedOrigins: 'https://dev.example.com', 13 | // allowedOrigins: 'https://localhost:4200' 14 | } 15 | }; 16 | 17 | /* 18 | * For easier debugging in development mode, you can import the following file 19 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 20 | * 21 | * This import should be commented out in production mode because it will have a negative impact 22 | * on performance if an error is thrown. 23 | */ 24 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 25 | -------------------------------------------------------------------------------- /src/environments/environment.uat.ts: -------------------------------------------------------------------------------- 1 | import { AppEnvironment } from '../app/core/interfaces/app-environment.interface'; 2 | 3 | export const environment: AppEnvironment = { 4 | production: true, 5 | baseApiUrl: 'https://uat.example.com/api/v1', 6 | security: { 7 | allowedOrigins: 'https://uat.example.com' 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartuck/angular-best-boilerplate/480a621ead3d62d30e0e12c8dfd9f1973dc935e0/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AngularBestBoilerplate 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /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 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; -------------------------------------------------------------------------------- /src/lib/js/modernizr-touch-events.js: -------------------------------------------------------------------------------- 1 | /*! modernizr 3.6.0 (Custom Build) | MIT * 2 | * https://modernizr.com/download/?-touchevents-setclasses !*/ 3 | !function(e,n,t){function o(e){var n=u.className,t=Modernizr._config.classPrefix||"";if(p&&(n=n.baseVal),Modernizr._config.enableJSClass){var o=new RegExp("(^|\\s)"+t+"no-js(\\s|$)");n=n.replace(o,"$1"+t+"js$2")}Modernizr._config.enableClasses&&(n+=" "+t+e.join(" "+t),p?u.className.baseVal=n:u.className=n)}function s(e,n){return typeof e===n}function a(){var e,n,t,o,a,i,r;for(var l in c)if(c.hasOwnProperty(l)){if(e=[],n=c[l],n.name&&(e.push(n.name.toLowerCase()),n.options&&n.options.aliases&&n.options.aliases.length))for(t=0;t console.error(err)); 13 | 14 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | /** 5 | * Web Animations `@angular/platform-browser/animations` 6 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 7 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 8 | **/ 9 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 10 | 11 | /** 12 | * By default, zone.js will patch all possible macroTask and DomEvents 13 | * user can disable parts of macroTask/DomEvents patch by setting following flags 14 | */ 15 | 16 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 17 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 18 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 19 | 20 | /* 21 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 22 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 23 | */ 24 | // (window as any).__Zone_enable_cross_context_check = true; 25 | 26 | /*************************************************************************************************** 27 | * Zone JS is required by default for Angular itself. 28 | */ 29 | import 'zone.js/dist/zone'; // Included with Angular CLI. 30 | 31 | 32 | 33 | /*************************************************************************************************** 34 | * APPLICATION IMPORTS 35 | */ 36 | -------------------------------------------------------------------------------- /src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | @import "styles/main"; 3 | -------------------------------------------------------------------------------- /src/styles/_reset.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Paste here your styles reset 3 | */ 4 | -------------------------------------------------------------------------------- /src/styles/_settings.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Shared SCSS settings for the global styles and components styles 3 | */ 4 | @import "settings/variables"; 5 | @import "settings/media"; 6 | @import "settings/extendables"; 7 | @import "settings/mixins"; 8 | @import "settings/paths"; 9 | -------------------------------------------------------------------------------- /src/styles/general/_animations.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Global Animations 3 | */ 4 | -------------------------------------------------------------------------------- /src/styles/general/_backgrounds.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Global Backgrounds 3 | */ 4 | -------------------------------------------------------------------------------- /src/styles/general/_buttons.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Global Buttons 3 | */ 4 | -------------------------------------------------------------------------------- /src/styles/general/_decorations.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Global Custom Decorations 3 | */ 4 | .border { 5 | 6 | &--default { 7 | border: 1px solid gray; 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/styles/general/_forms.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Global Forms 3 | */ 4 | -------------------------------------------------------------------------------- /src/styles/general/_icons.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Global Icons 3 | */ 4 | -------------------------------------------------------------------------------- /src/styles/general/_images.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Global Images 3 | */ 4 | -------------------------------------------------------------------------------- /src/styles/general/_layout.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Global Layout and Grid System 3 | */ 4 | 5 | .float-left { 6 | float: left; 7 | } 8 | 9 | .float-right { 10 | float: right; 11 | } 12 | 13 | .clearfix { 14 | 15 | &:after { 16 | content: ''; 17 | clear: both; 18 | display: block; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/styles/general/_typography.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Global Typography 3 | */ 4 | -------------------------------------------------------------------------------- /src/styles/main.scss: -------------------------------------------------------------------------------- 1 | // Settings 2 | @import "reset"; 3 | @import "settings"; 4 | 5 | // General 6 | @import "general/animations"; 7 | @import "general/backgrounds"; 8 | @import "general/buttons"; 9 | @import "general/decorations"; 10 | @import "general/forms"; 11 | @import "general/icons"; 12 | @import "general/images"; 13 | @import "general/layout"; 14 | @import "general/typography"; 15 | 16 | // Templates 17 | @import "templates/card"; 18 | @import "templates/notifications"; 19 | @import "templates/page"; 20 | 21 | // Vendors 22 | //@import "vendors/*.scss"; 23 | -------------------------------------------------------------------------------- /src/styles/settings/_extendables.scss: -------------------------------------------------------------------------------- 1 | // %your-abstract-scss-class to use by @extend 2 | -------------------------------------------------------------------------------- /src/styles/settings/_fonts.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Global Fonts Declarations 3 | */ 4 | -------------------------------------------------------------------------------- /src/styles/settings/_media.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * MEDIA & BREAKPOINTS & RELEVANT SIZES - Use the Mobile First approach! 3 | * Resolution -|- Usage - https://www.rapidtables.com/web/dev/screen-resolution-statistics.html 4 | * - 1366x768 - 19.1% 5 | * - 1920x1080 - 9.4% 6 | * - 1280x800 - 8.5% 7 | * - 320x568 - 6.4% 8 | * - 1440x900 - 5.7% 9 | * - others 10 | */ 11 | $media-xxs: 320px; // iPhone 5 12 | $media-xxs-standard: 360px;// SmartPhone 13 | $media-xs: 576px; // Bootstrap v4 14 | $media-sm: 768px; // Bootstrap v4 15 | $media-lg: 992px; // Bootstrap v4 16 | $media-xl: 1200px; // Bootstrap v4 17 | $media-xl-screen: 1920px; 18 | $media-notebook: 1280px; 19 | $media-laptop: 1366px; 20 | $media-macbook: 1440px; 21 | $media-monitor: 1600px; 22 | -------------------------------------------------------------------------------- /src/styles/settings/_mixins.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Global Mixins, i.e. box-shadow 3 | */ 4 | -------------------------------------------------------------------------------- /src/styles/settings/_paths.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS urls related to the assets directory 3 | */ 4 | // $path-logo-svg: url('/assets/images/logo.svg') 5 | // $path-logo-png: url('/assets/images/logo.png') 6 | // $path- ...your paths 7 | -------------------------------------------------------------------------------- /src/styles/settings/_variables.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * COLORS 3 | */ 4 | $color-default-font: black; 5 | $color-red-error: red; 6 | $color-red-bg: rgba(red, .5); 7 | // ...your custom colors 8 | 9 | /** 10 | * SPACING: MARGINS & PADDING 11 | */ 12 | $space-xs: 3px; 13 | $space-sm: 5px; 14 | $space-md: 10px; 15 | $space-lg: 15px; 16 | $space-xl: 15px; 17 | 18 | /** 19 | * FONTS 20 | */ 21 | $font-size-xs: .5rem; 22 | $font-size-sm: .6rem; 23 | $font-size-md: .75rem; 24 | $font-size-default: 1rem; 25 | $font-size-lg: 1.2rem; 26 | $font-size-xl: 1.4rem; 27 | 28 | /** 29 | * Z-INDEXES & LAYERS 30 | */ 31 | $z-index-bottom: -1; 32 | $z-index-default: 0; 33 | $z-index-top: 1; 34 | $z-index-dropdown: 10; 35 | $z-index-menu: 1000; 36 | $z-index-overlay: 1100; 37 | $z-index-dialog: 1200; 38 | $z-index-notification: 1300; 39 | 40 | /** 41 | * TRANSITIONS & EFFECTS 42 | */ 43 | $transition-effect-default: .3s ease; 44 | $transition-bezier: .3s cubic-bezier(0, 0, 0.2, 1); 45 | -------------------------------------------------------------------------------- /src/styles/templates/_card.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Card to use everywhere 3 | */ 4 | -------------------------------------------------------------------------------- /src/styles/templates/_notifications.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Global notifications to use everywhere 3 | */ 4 | -------------------------------------------------------------------------------- /src/styles/templates/_page.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Page template to use everywhere 3 | */ 4 | -------------------------------------------------------------------------------- /src/styles/vendors/_angular-material.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartuck/angular-best-boilerplate/480a621ead3d62d30e0e12c8dfd9f1973dc935e0/src/styles/vendors/_angular-material.scss -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import { getTestBed } from '@angular/core/testing'; 4 | import { 5 | platformBrowserDynamicTesting, 6 | BrowserDynamicTestingModule 7 | } from '@angular/platform-browser-dynamic/testing'; 8 | import 'zone.js/dist/zone-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/test/components/dummy-app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | template: `` 5 | }) 6 | export class DummyAppComponent { 7 | } 8 | -------------------------------------------------------------------------------- /src/test/components/dummy-home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | template: `Home` 5 | }) 6 | export class DummyHomeComponent { 7 | } 8 | -------------------------------------------------------------------------------- /src/test/components/dummy-login.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | template: `Login` 5 | }) 6 | export class DummyLoginComponent { 7 | } 8 | -------------------------------------------------------------------------------- /src/test/components/dummy-search.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | template: `Search` 5 | }) 6 | export class DummySearchComponent { 7 | } 8 | -------------------------------------------------------------------------------- /src/test/mocks/routes.mock.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | 3 | import { AppComponent } from '../../app/app.component'; 4 | import { DummyLoginComponent } from '../components/dummy-login.component'; 5 | import { DummySearchComponent } from '../components/dummy-search.component'; 6 | 7 | export const testingRoutes: Routes = [ 8 | { path: '', component: AppComponent, data: { title: 'Home'} }, 9 | { path: 'search', component: DummySearchComponent, data: { title: 'Search'} }, 10 | { path: 'log-in', component: DummyLoginComponent, data: { title: 'Login'} } 11 | ]; 12 | -------------------------------------------------------------------------------- /src/test/mocks/users.mock.ts: -------------------------------------------------------------------------------- 1 | export const USERS_MOCK = [ 2 | { 3 | name: 'User Name 1', 4 | email: 'user1@example.com' 5 | }, 6 | { 7 | name: 'User Name 2', 8 | email: 'user2@example.com' 9 | }, 10 | ]; 11 | -------------------------------------------------------------------------------- /src/test/spies/example-service.spy.ts: -------------------------------------------------------------------------------- 1 | export const EXAMPLE_SERVICE_SPY = jasmine.createSpyObj('ExampleService', ['get']); 2 | -------------------------------------------------------------------------------- /src/test/stubs/example-service.stub.ts: -------------------------------------------------------------------------------- 1 | export class ExampleServiceStub { 2 | 3 | private name: string = 'ExampleServiceStub'; 4 | 5 | get(): string { 6 | return `Hello, ${this.name}`; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/translations/en_GB.const.ts: -------------------------------------------------------------------------------- 1 | import { Translations } from '../app/core/interfaces/translations.interface'; 2 | 3 | export const en_GB: Translations = { 4 | hello_world: 'Hello World', 5 | users: 'Users', 6 | home: 'Home', 7 | language: 'Language' 8 | }; 9 | -------------------------------------------------------------------------------- /src/translations/pl_PL.const.ts: -------------------------------------------------------------------------------- 1 | import { Translations } from '../app/core/interfaces/translations.interface'; 2 | 3 | export const pl_PL: Translations = { 4 | hello_world: 'Witaj Świecie!', 5 | users: 'Użytkownicy', 6 | home: 'Home', 7 | language: 'Język' 8 | }; 9 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "types": [] 6 | }, 7 | "exclude": [ 8 | "test.ts", 9 | "**/*.spec.ts", 10 | "**/*.mock.ts", 11 | "**/*.spy.ts", 12 | "**/*.stub.ts", 13 | "test/**" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "test.ts", 12 | "polyfills.ts" 13 | ], 14 | "include": [ 15 | "**/*.spec.ts", 16 | "**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "downlevelIteration": true, 6 | "importHelpers": true, 7 | "outDir": "./dist/out-tsc", 8 | "sourceMap": true, 9 | "declaration": false, 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "emitDecoratorMetadata": true, 13 | "experimentalDecorators": true, 14 | "target": "es2015", 15 | "typeRoots": [ 16 | "node_modules/@types" 17 | ], 18 | "lib": [ 19 | "es2017", 20 | "dom" 21 | ] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /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/Rx" 22 | ], 23 | "import-spacing": true, 24 | "indent": [ 25 | true, 26 | "spaces" 27 | ], 28 | "interface-over-type-literal": true, 29 | "label-position": true, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-arg": true, 47 | "no-bitwise": true, 48 | "no-console": [ 49 | true, 50 | "debug", 51 | "info", 52 | "time", 53 | "timeEnd", 54 | "trace" 55 | ], 56 | "no-construct": true, 57 | "no-debugger": true, 58 | "no-duplicate-super": true, 59 | "no-empty": false, 60 | "no-empty-interface": true, 61 | "no-eval": true, 62 | "no-inferrable-types": [ 63 | true, 64 | "ignore-params" 65 | ], 66 | "no-misused-new": true, 67 | "no-non-null-assertion": true, 68 | "no-redundant-jsdoc": 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 | "ordered-imports": [ 87 | true, 88 | { 89 | "import-sources-order": "lowercase-last", 90 | "named-imports-order": "lowercase-first" 91 | } 92 | ], 93 | "prefer-const": true, 94 | "quotemark": [ 95 | true, 96 | "single" 97 | ], 98 | "radix": true, 99 | "semicolon": [ 100 | true, 101 | "always" 102 | ], 103 | "triple-equals": [ 104 | true, 105 | "allow-null-check" 106 | ], 107 | "typedef-whitespace": [ 108 | true, 109 | { 110 | "call-signature": "nospace", 111 | "index-signature": "nospace", 112 | "parameter": "nospace", 113 | "property-declaration": "nospace", 114 | "variable-declaration": "nospace" 115 | } 116 | ], 117 | "unified-signatures": true, 118 | "variable-name": false, 119 | "whitespace": [ 120 | true, 121 | "check-branch", 122 | "check-decl", 123 | "check-operator", 124 | "check-separator", 125 | "check-type" 126 | ], 127 | "no-output-on-prefix": true, 128 | "no-inputs-metadata-property": true, 129 | "no-outputs-metadata-property": true, 130 | "no-host-metadata-property": true, 131 | "no-input-rename": true, 132 | "no-output-rename": true, 133 | "use-lifecycle-interface": true, 134 | "use-pipe-transform-interface": true, 135 | "component-class-suffix": true, 136 | "directive-class-suffix": true 137 | } 138 | } 139 | --------------------------------------------------------------------------------