├── .editorconfig
├── .gitignore
├── README.md
├── angular.json
├── karma.conf.js
├── package-lock.json
├── package.json
├── protractor.conf.js
├── src
├── app
│ ├── app-injector.service.spec.ts
│ ├── app-injector.service.ts
│ ├── app-routing.module.ts
│ ├── app.component.html
│ ├── app.component.scss
│ ├── app.component.ts
│ ├── app.config.ts
│ ├── app.module.ts
│ ├── demo-common
│ │ ├── demo-common.module.ts
│ │ ├── directives
│ │ │ ├── component-header
│ │ │ │ ├── component-header.component.html
│ │ │ │ ├── component-header.component.scss
│ │ │ │ ├── component-header.component.spec.ts
│ │ │ │ └── component-header.component.ts
│ │ │ ├── header-bar
│ │ │ │ ├── header-bar.component.html
│ │ │ │ ├── header-bar.component.scss
│ │ │ │ ├── header-bar.component.spec.ts
│ │ │ │ └── header-bar.component.ts
│ │ │ ├── top-nav
│ │ │ │ ├── top-nav.component.html
│ │ │ │ ├── top-nav.component.scss
│ │ │ │ ├── top-nav.component.spec.ts
│ │ │ │ └── top-nav.component.ts
│ │ │ └── transaction-confirm-dialog
│ │ │ │ ├── transaction-confirm-dialog.component.css
│ │ │ │ ├── transaction-confirm-dialog.component.html
│ │ │ │ ├── transaction-confirm-dialog.component.spec.ts
│ │ │ │ └── transaction-confirm-dialog.component.ts
│ │ ├── models
│ │ │ ├── account-search-result.models.ts
│ │ │ ├── app-config.model.ts
│ │ │ └── transaction.models.ts
│ │ ├── services
│ │ │ ├── demo-common-data.service.ts
│ │ │ ├── demo-resolver.service.ts
│ │ │ ├── search-data.service.ts
│ │ │ ├── search.service.spec.ts
│ │ │ ├── search.service.ts
│ │ │ ├── transaction-data.service.ts
│ │ │ ├── transaction-entity-data.service.ts
│ │ │ ├── transaction.service.spec.ts
│ │ │ ├── transaction.service.ts
│ │ │ ├── user-session.service.spec.ts
│ │ │ └── user-session.service.ts
│ │ └── testing
│ │ │ └── testing-helpers.ts
│ ├── demo
│ │ ├── accounts
│ │ │ ├── child-component1
│ │ │ │ ├── child-component1-resolver.service.ts
│ │ │ │ ├── child-component1.component.html
│ │ │ │ ├── child-component1.component.scss
│ │ │ │ ├── child-component1.component.spec.ts
│ │ │ │ └── child-component1.component.ts
│ │ │ ├── child-component2
│ │ │ │ ├── child-component2-resolver.service.ts
│ │ │ │ ├── child-component2.component.html
│ │ │ │ ├── child-component2.component.scss
│ │ │ │ ├── child-component2.component.spec.ts
│ │ │ │ └── child-component2.component.ts
│ │ │ ├── child-component3
│ │ │ │ ├── child-component3-resolver.service.ts
│ │ │ │ ├── child-component3.component.html
│ │ │ │ ├── child-component3.component.scss
│ │ │ │ ├── child-component3.component.spec.ts
│ │ │ │ └── child-component3.component.ts
│ │ │ └── shared
│ │ │ │ ├── components
│ │ │ │ ├── demo-transaction-component.spec.ts
│ │ │ │ ├── demo-transaction-component.ts
│ │ │ │ └── navigation-error
│ │ │ │ │ ├── navigation-error.component.html
│ │ │ │ │ ├── navigation-error.component.scss
│ │ │ │ │ ├── navigation-error.component.spec.ts
│ │ │ │ │ └── navigation-error.component.ts
│ │ │ │ ├── directives
│ │ │ │ ├── account-header
│ │ │ │ │ ├── account-header.component.html
│ │ │ │ │ ├── account-header.component.scss
│ │ │ │ │ ├── account-header.component.spec.ts
│ │ │ │ │ ├── account-header.component.ts
│ │ │ │ │ ├── account-header.service.spec.ts
│ │ │ │ │ └── account-header.service.ts
│ │ │ │ └── header-menu
│ │ │ │ │ ├── header-menu.component.html
│ │ │ │ │ ├── header-menu.component.scss
│ │ │ │ │ └── header-menu.component.ts
│ │ │ │ ├── models
│ │ │ │ ├── account.models.ts
│ │ │ │ ├── child1-entity.models.ts
│ │ │ │ ├── child2-entity.models.ts
│ │ │ │ └── child3-entity.models.ts
│ │ │ │ └── services
│ │ │ │ ├── account-data.service.ts
│ │ │ │ ├── child1-data.service.ts
│ │ │ │ ├── child2-data.service.ts
│ │ │ │ ├── child3-data.service.ts
│ │ │ │ ├── demo-account-resolver.service.ts
│ │ │ │ ├── menu.service.ts
│ │ │ │ └── update.service.ts
│ │ ├── demo-routing.module.ts
│ │ ├── demo.module.ts
│ │ └── search
│ │ │ └── search-results
│ │ │ ├── search-results-resolver.service.ts
│ │ │ ├── search-results.component.html
│ │ │ ├── search-results.component.scss
│ │ │ ├── search-results.component.spec.ts
│ │ │ └── search-results.component.ts
│ ├── framework
│ │ ├── components
│ │ │ ├── base-component.spec.ts
│ │ │ └── base-component.ts
│ │ ├── errorhandling
│ │ │ ├── error-handler.service.spec.ts
│ │ │ ├── error-handler.service.ts
│ │ │ ├── error-utilities.service.spec.ts
│ │ │ └── error-utilities.service.ts
│ │ ├── framework.module.ts
│ │ ├── logging
│ │ │ ├── logging.service.ts
│ │ │ └── severity-level.model.ts
│ │ ├── models
│ │ │ ├── authorization.types.ts
│ │ │ ├── form-controls.models.ts
│ │ │ └── metadata.models.ts
│ │ ├── services
│ │ │ ├── auth-guard.service.ts
│ │ │ ├── auth-interceptor.service.ts
│ │ │ ├── auth.service.spec.ts
│ │ │ ├── auth.service.ts
│ │ │ ├── authorization-data.service.ts
│ │ │ ├── authorization.service.spec.ts
│ │ │ ├── authorization.service.ts
│ │ │ ├── can-deactivate-guard.service.ts
│ │ │ ├── data.service.ts
│ │ │ ├── form-builder.service.spec.ts
│ │ │ ├── form-builder.service.ts
│ │ │ ├── global-events.service.ts
│ │ │ ├── metadata-data.service.ts
│ │ │ ├── metadata.service.ts
│ │ │ ├── system-message-data.service.ts
│ │ │ ├── system-message.service.spec.ts
│ │ │ ├── system-message.service.ts
│ │ │ ├── url-serializer.service.ts
│ │ │ ├── utilities.service.spec.ts
│ │ │ ├── utilities.service.ts
│ │ │ └── web-storage.service.ts
│ │ └── validation
│ │ │ ├── directives
│ │ │ ├── control-validation.directives.spec.ts
│ │ │ └── control-validators.directive.ts
│ │ │ ├── models
│ │ │ ├── server-error.models.ts
│ │ │ └── validation.models.ts
│ │ │ ├── services
│ │ │ ├── validation.service.spec.ts
│ │ │ └── validation.service.ts
│ │ │ └── validation.module.ts
│ ├── home
│ │ ├── home.component.html
│ │ ├── home.component.scss
│ │ ├── home.component.ts
│ │ ├── log-out
│ │ │ ├── log-out.component.html
│ │ │ ├── log-out.component.scss
│ │ │ ├── log-out.component.spec.ts
│ │ │ └── log-out.component.ts
│ │ ├── page-error
│ │ │ ├── page-error.component.html
│ │ │ ├── page-error.component.scss
│ │ │ ├── page-error.component.spec.ts
│ │ │ └── page-error.component.ts
│ │ └── page-not-found
│ │ │ ├── page-not-found.component.html
│ │ │ ├── page-not-found.component.scss
│ │ │ ├── page-not-found.component.spec.ts
│ │ │ └── page-not-found.component.ts
│ └── shared
│ │ ├── directives
│ │ ├── accordion
│ │ │ ├── accordion.component.css
│ │ │ ├── accordion.component.html
│ │ │ └── accordion.component.ts
│ │ ├── alert
│ │ │ ├── alert.component.css
│ │ │ ├── alert.component.html
│ │ │ └── alert.component.ts
│ │ ├── calendar
│ │ │ ├── calendar.component.css
│ │ │ ├── calendar.component.html
│ │ │ ├── calendar.component.spec.ts
│ │ │ └── calendar.component.ts
│ │ ├── confirm-dialog
│ │ │ ├── confirm-dialog.component.css
│ │ │ ├── confirm-dialog.component.html
│ │ │ ├── confirm-dialog.component.ts
│ │ │ └── parts-confirm-dialog.component.spec.ts
│ │ ├── data-table
│ │ │ ├── data-table-models.ts
│ │ │ ├── data-table.component.html
│ │ │ ├── data-table.component.scss
│ │ │ ├── data-table.component.spec.ts
│ │ │ └── data-table.component.ts
│ │ ├── dialog
│ │ │ ├── dialog.component.css
│ │ │ ├── dialog.component.html
│ │ │ └── dialog.component.ts
│ │ ├── disable-if-unauthorized
│ │ │ └── disable-if-unauthorized.directive.ts
│ │ ├── error-message
│ │ │ ├── error-message.component.html
│ │ │ ├── error-message.component.scss
│ │ │ ├── error-message.component.spec.ts
│ │ │ └── error-message.component.ts
│ │ ├── form-field
│ │ │ ├── form-field.component.html
│ │ │ ├── form-field.component.scss
│ │ │ ├── form-field.component.spec.ts
│ │ │ └── form-field.component.ts
│ │ ├── form-list
│ │ │ ├── form-list.component.html
│ │ │ ├── form-list.component.scss
│ │ │ └── form-list.component.ts
│ │ ├── hide-if-unauthorized
│ │ │ └── hide-if-unauthorized.directive.ts
│ │ ├── input
│ │ │ ├── input.component.html
│ │ │ ├── input.component.scss
│ │ │ ├── input.component.spec.ts
│ │ │ └── input.component.ts
│ │ └── menu
│ │ │ ├── menu.component.html
│ │ │ ├── menu.component.scss
│ │ │ ├── menu.component.spec.ts
│ │ │ └── menu.component.ts
│ │ ├── models
│ │ ├── app-monitor.ts
│ │ └── confirm-choices.enum.ts
│ │ ├── pipes
│ │ ├── date-to-string.pipe.spec.ts
│ │ ├── date-to-string.pipe.ts
│ │ ├── display-error.pipe.spec.ts
│ │ └── display-error.pipe.ts
│ │ └── shared.module.ts
├── assets
│ ├── config
│ │ ├── config.deploy.json
│ │ ├── config.dev.json
│ │ ├── config.local.json
│ │ └── config.prod.json
│ └── loading.gif
├── environments
│ ├── environment.deploy.ts
│ ├── environment.prod.ts
│ └── environment.ts
├── favicon.ico
├── index.html
├── main.ts
├── polyfills.ts
├── scss
│ ├── colors.scss
│ ├── dimensions.scss
│ └── mixins.scss
├── styles.css
├── test.ts
├── tsconfig.app.json
├── tsconfig.spec.json
├── typings.d.ts
├── variables.less
└── web.config
├── tsconfig.json
├── tslint.json
└── website.publishproj
/.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 | /dist-server
6 | /tmp
7 | /out-tsc
8 |
9 | # dependencies
10 | /node_modules
11 |
12 | # IDEs and editors
13 | /.idea
14 | .project
15 | .classpath
16 | .c9/
17 | *.launch
18 | .settings/
19 | *.sublime-workspace
20 |
21 | # IDE - VSCode
22 | .vscode/*
23 | !.vscode/settings.json
24 | !.vscode/tasks.json
25 | !.vscode/launch.json
26 | !.vscode/extensions.json
27 |
28 | # misc
29 | /.sass-cache
30 | /connect.lock
31 | /coverage
32 | /libpeerconnection.log
33 | npm-debug.log
34 | yarn-error.log
35 | testem.log
36 | /typings
37 |
38 | # e2e
39 | /e2e/*.js
40 | /e2e/*.map
41 |
42 | # System Files
43 | .DS_Store
44 | Thumbs.db
45 | TESTS-Chrome*.xml
46 | TESTS-HeadlessChrome*.xml
47 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NgPatternsDemo
2 |
3 | ## Development server
4 | Run npm start` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
5 |
6 | ## Build
7 |
8 | Run `npm run prod` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
9 |
10 | ## Running unit tests
11 |
12 | Run `npm run test` to execute the unit tests via [Karma](https://karma-runner.github.io).
13 |
14 | ## App Description
15 | Angular demo app with examples of form creation, validation, class inheritance, etc.
16 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/0.13/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular-devkit/build-angular/plugins/karma'),
14 | require('karma-spec-reporter'),
15 | //require('karma-phantomjs-launcher'),
16 | require('karma-junit-reporter')
17 | ],
18 | client: {
19 | clearContext: false // leave Jasmine Spec Runner output visible in browser
20 | },
21 | files: [
22 |
23 | ],
24 | preprocessors: {
25 |
26 | },
27 | mime: {
28 | 'text/x-typescript': ['ts', 'tsx']
29 | },
30 | coverageIstanbulReporter: {
31 | dir: require('path').join(__dirname, 'coverage'), reports: ['html', 'lcovonly'],
32 | fixWebpackSourcePaths: true
33 | },
34 | junitReported: {
35 | outputDir: '', // results will be saved as $outputDir/$browserName.xml
36 | outputFile: 'test.xml', // if included, results will be saved as $outputDir/$browserName/$outputFile
37 | },
38 | angularCli: {
39 | environment: 'dev'
40 | },
41 | // reporters: config.angularCli && config.angularCli.codeCoverage
42 | // ? ['spec', 'coverage-istanbul']
43 | // : ['spec'],
44 | reporters: config.angularCli && config.angularCli.codeCoverage
45 | ? ['progress', 'coverage-istanbul', 'junit']
46 | : ['progress', 'kjhtml', 'junit'],
47 | port: 9876,
48 | colors: true,
49 | logLevel: config.LOG_INFO,
50 | autoWatch: true,
51 | customLaunchers: {
52 | ChromeHeadless: {
53 | base: 'Chrome',
54 | flags: [
55 | '--headless',
56 | '--disable-gpu',
57 | '--no-sandbox',
58 | '--remote-debugging-port=9222',
59 | ]
60 | }
61 | },
62 | browsers: ['Chrome', 'ChromeHeadless'],
63 | singleRun: false,
64 | sourcemaps: false,
65 | browserNoActivityTimeout: 60000, //default 10000
66 | browserDisconnectTimeout: 10000, // default 2000
67 | browserDisconnectTolerance: 1, // default 0
68 | captureTimeout: 60000
69 | });
70 | };
71 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ng-patterns-demo",
3 | "version": "1.0.0",
4 | "license": "MIT",
5 | "scripts": {
6 | "ng": "ng",
7 | "start": "ng serve",
8 | "test": "ng test --browsers=Chrome",
9 | "test-cc": "ng test --browsers=Chrome --code-coverage",
10 | "test-headless": "ng test --browsers=ChromeHeadless --single-run",
11 | "test-coverage": "ng test --browsers=ChromeHeadless --single-run --code-coverage",
12 | "lint": "ng lint",
13 | "e2e": "ng e2e",
14 | "build": "ng build",
15 | "dev-iis": "ng build --configuration=dev --build-optimizer --aot --output-hashing all",
16 | "deploy": "ng build --configuration=deploy --build-optimizer --aot --output-hashing all",
17 | "prod": "ng build --configuration=prod --build-optimizer --aot --output-hashing all"
18 | },
19 | "private": true,
20 | "dependencies": {
21 | "@angular/animations": "~8.2.11",
22 | "@angular/cdk": "~8.2.3",
23 | "@angular/common": "~8.2.11",
24 | "@angular/compiler": "~8.2.11",
25 | "@angular/core": "~8.2.11",
26 | "@angular/forms": "~8.2.11",
27 | "@angular/material": "^8.2.3",
28 | "@angular/platform-browser": "~8.2.11",
29 | "@angular/platform-browser-dynamic": "~8.2.11",
30 | "@angular/router": "~8.2.11",
31 | "hammerjs": "^2.0.8",
32 | "rxjs": "~6.4.0",
33 | "tslib": "^1.10.0",
34 | "zone.js": "~0.9.1",
35 | "adal-angular4": "~4.0.12",
36 | "@microsoft/applicationinsights-web": "~2.4.4",
37 | "bootstrap": "~3.4.1",
38 | "core-js": "2.5.4",
39 | "font-awesome": "4.7.0",
40 | "less": "3.0.4",
41 | "lodash": "~4.17.15",
42 | "moment": "2.22.1",
43 | "primeicons": "1.0.0",
44 | "primeng": "~7.0.0"
45 | },
46 | "devDependencies": {
47 | "@angular-devkit/build-angular": "~0.803.14",
48 | "@angular/cli": "~8.3.14",
49 | "@angular/compiler-cli": "~8.2.11",
50 | "@angular/language-service": "~8.2.11",
51 | "@types/node": "~8.9.4",
52 | "@types/jasmine": "~3.3.8",
53 | "@types/jasminewd2": "~2.0.3",
54 | "@types/adal-angular": "~1.0.1",
55 | "@types/lodash": "~4.14.144",
56 | "codelyzer": "^5.0.0",
57 | "jasmine-core": "~3.4.0",
58 | "jasmine-spec-reporter": "~4.2.1",
59 | "karma": "~4.1.0",
60 | "karma-chrome-launcher": "~2.2.0",
61 | "karma-coverage-istanbul-reporter": "~2.0.1",
62 | "karma-jasmine": "~2.0.1",
63 | "karma-jasmine-html-reporter": "^1.4.0",
64 | "karma-junit-reporter": "~2.0.1",
65 | "karma-spec-reporter": "~0.0.32",
66 | "protractor": "~5.4.0",
67 | "ts-node": "~7.0.0",
68 | "tslint": "~5.15.0",
69 | "typescript": "~3.5.3"
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // Protractor configuration file, see link for more information
2 | // https://github.com/angular/protractor/blob/master/lib/config.ts
3 |
4 | /*global jasmine */
5 | const { SpecReporter } = require('jasmine-spec-reporter');
6 |
7 | exports.config = {
8 | allScriptsTimeout: 11000,
9 | specs: [
10 | './e2e/**/*.e2e-spec.ts'
11 | ],
12 | capabilities: {
13 | 'browserName': 'chrome'
14 | },
15 | directConnect: true,
16 | baseUrl: 'http://localhost:4200/',
17 | framework: 'jasmine',
18 | jasmineNodeOpts: {
19 | showColors: true,
20 | defaultTimeoutInterval: 30000,
21 | print: function() {}
22 | },
23 | beforeLaunch: function() {
24 | require('ts-node').register({
25 | project: 'e2e/tsconfig.e2e.json'
26 | });
27 | },
28 | onPrepare: function() {
29 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
30 | }
31 | };
32 |
--------------------------------------------------------------------------------
/src/app/app-injector.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { AppInjector } from './app-injector.service';
2 | import { inject, TestBed } from '@angular/core/testing';
3 |
4 | describe('AppInjector', () => {
5 | beforeEach(() => {
6 |
7 | TestBed.configureTestingModule({
8 | providers: [
9 | AppInjector
10 | ]
11 | });
12 | });
13 |
14 | it('should only create one instance', () => {
15 | const service1 = AppInjector.getInstance();
16 | const service2 = AppInjector.getInstance();
17 | expect(service1).toBe(service2);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/src/app/app-injector.service.ts:
--------------------------------------------------------------------------------
1 | import { Injector } from '@angular/core';
2 |
3 | export class AppInjector {
4 | private static instance: AppInjector;
5 | private injector: Injector;
6 |
7 | static getInstance() {
8 | if (!AppInjector.instance) {
9 | AppInjector.instance = new AppInjector();
10 | }
11 |
12 | return AppInjector.instance;
13 | }
14 |
15 | private constructor() {}
16 |
17 | setInjector(injector: Injector) {
18 | this.injector = injector;
19 | }
20 |
21 | getInjector(): Injector {
22 | return this.injector;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { RouterModule, Routes } from '@angular/router';
3 | import { AuthGuardService } from './framework/services/auth-guard.service';
4 | import { HomeComponent } from './home/home.component';
5 | import { PageNotFoundComponent } from './home/page-not-found/page-not-found.component';
6 | import { PageErrorComponent } from './home/page-error/page-error.component';
7 | import { LogOutComponent } from './home/log-out/log-out.component';
8 |
9 | const routes: Routes = [
10 | { path: 'home', component: HomeComponent, canActivate: [AuthGuardService] },
11 | { path: 'error', component: PageErrorComponent },
12 | { path: 'logout', component: LogOutComponent },
13 | { path: '', redirectTo: '/home', pathMatch: 'full' },
14 | { path: '**', component: PageNotFoundComponent }
15 | ];
16 |
17 | @NgModule({
18 | imports: [RouterModule.forRoot(routes)],
19 | exports: [RouterModule]
20 | })
21 | export class AppRoutingModule {}
22 |
--------------------------------------------------------------------------------
/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |

12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 | @import '../scss/colors';
2 | @import '../scss/mixins';
3 |
4 | .overlay.loading {
5 | position:fixed;
6 | z-index: 999;
7 | top:0;
8 | left:0;
9 | width: 100%;
10 | height: 100%;
11 | opacity: .7;
12 | background-color: $text-med;
13 | }
14 | .hide-overflow {
15 | width: 100%;
16 | overflow-x: hidden;
17 | min-height: 93vh;
18 | }
19 | .container-fluid {
20 | margin-top: 80px;
21 | padding-top: 20px;
22 | margin-bottom: 30px;
23 | }
24 | .loadingGif {
25 | position: fixed;
26 | z-index: 1000;
27 | overflow: show;
28 | margin: auto;
29 | top: 0;
30 | left: 0;
31 | bottom: 0;
32 | right: 0;
33 | height: 4em;
34 | width: 4em;
35 | }
36 | .timeoutWarning {
37 | padding-top: 50px;
38 | }
39 |
--------------------------------------------------------------------------------
/src/app/app.config.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { IAppConfig } from './demo-common/models/app-config.model';
3 | import { environment } from 'environments/environment';
4 | import { HttpClient } from '@angular/common/http';
5 | import { IAppMonitor } from './shared/models/app-monitor';
6 |
7 | @Injectable()
8 | export class AppConfig {
9 |
10 | static settings: IAppConfig;
11 | static appMonitor: IAppMonitor;
12 |
13 | constructor(private http: HttpClient) {
14 | }
15 |
16 | load() {
17 | const cacheBusterParam = (new Date()).getTime();
18 | const jsonFile = `assets/config/config.${environment.name}.json?nocache=${cacheBusterParam}`;
19 | return new Promise((resolve, reject) => {
20 | this.http.get(jsonFile).toPromise()
21 | .then((response: IAppConfig) => {
22 | AppConfig.settings = response;
23 | resolve();
24 | }).catch((response: any) => {
25 | reject(`Could not load file '${jsonFile}': ${JSON.stringify(response)}`);
26 | });
27 | });
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
2 | import { NgModule, APP_INITIALIZER } from '@angular/core';
3 | import { UrlSerializer } from '@angular/router';
4 | import { BrowserModule } from '@angular/platform-browser';
5 | import { HTTP_INTERCEPTORS } from '@angular/common/http';
6 | import { ApplicationInsights } from '@microsoft/applicationinsights-web';
7 | import { DemoCommonModule } from './demo-common/demo-common.module';
8 | import { FrameworkModule } from './framework/framework.module';
9 | import { AppRoutingModule } from './app-routing.module';
10 | import { DemoModule } from './demo/demo.module';
11 | import { LowerCaseUrlSerializer } from './framework/services/url-serializer.service';
12 | import { AppComponent } from './app.component';
13 | import { HomeComponent } from './home/home.component';
14 | import { PageNotFoundComponent } from './home/page-not-found/page-not-found.component';
15 | import { PageErrorComponent } from './home/page-error/page-error.component';
16 | import { LogOutComponent } from './home/log-out/log-out.component';
17 | import { AppConfig } from './app.config';
18 | import { AuthInterceptorService } from './framework/services/auth-interceptor.service';
19 |
20 | export function initializeApp(appConfig: AppConfig) {
21 | const promise = appConfig.load().then(() => {
22 | if (AppConfig.settings && AppConfig.settings.logging &&
23 | AppConfig.settings.logging.appInsights) {
24 | const appInsights = new ApplicationInsights({
25 | config: {
26 | instrumentationKey: AppConfig.settings.appInsights.instrumentationKey,
27 | enableAutoRouteTracking: true // option to log all route changes
28 | }
29 | });
30 | appInsights.loadAppInsights();
31 | appInsights.trackPageView();
32 | AppConfig.appMonitor = appInsights;
33 | }
34 | });
35 | return () => promise;
36 | }
37 |
38 | @NgModule({
39 | imports: [
40 | BrowserModule,
41 | FrameworkModule,
42 | BrowserAnimationsModule,
43 | DemoCommonModule,
44 | DemoModule,
45 | AppRoutingModule
46 | ],
47 | declarations: [
48 | AppComponent,
49 | HomeComponent,
50 | PageNotFoundComponent,
51 | PageErrorComponent,
52 | LogOutComponent
53 | ],
54 | providers: [
55 | AppConfig,
56 | { provide: APP_INITIALIZER, useFactory: initializeApp, deps: [AppConfig], multi: true },
57 | { provide: UrlSerializer, useClass: LowerCaseUrlSerializer },
58 | { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptorService, multi: true },
59 | ],
60 | bootstrap: [
61 | AppComponent
62 | ]
63 | })
64 | export class AppModule { }
65 |
--------------------------------------------------------------------------------
/src/app/demo-common/demo-common.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { RouterModule } from '@angular/router';
3 | import { NgModule } from '@angular/core';
4 | import { HeaderBarComponent } from './directives/header-bar/header-bar.component';
5 | import { TopNavComponent } from './directives/top-nav/top-nav.component';
6 | import { TransactionDataService } from './services/transaction-data.service';
7 | import { TransactionService } from './services/transaction.service';
8 | import { TransactionEntityDataService } from './services/transaction-entity-data.service';
9 | import { SearchDataService } from './services/search-data.service';
10 | import { SearchService } from './services/search.service';
11 | import { UserSessionService } from './services/user-session.service';
12 | import { ComponentHeaderComponent } from './directives/component-header/component-header.component';
13 | import { DemoCommonDataService } from './services/demo-common-data.service';
14 | import { TransactionConfirmDialogComponent } from './directives/transaction-confirm-dialog/transaction-confirm-dialog.component';
15 | import { SharedModule } from '../shared/shared.module';
16 |
17 | @NgModule({
18 | imports: [
19 | CommonModule,
20 | RouterModule,
21 | SharedModule
22 | ],
23 | declarations: [
24 | TopNavComponent,
25 | HeaderBarComponent,
26 | TransactionConfirmDialogComponent,
27 | ComponentHeaderComponent
28 | ],
29 | providers: [
30 | UserSessionService,
31 | DemoCommonDataService,
32 | TransactionEntityDataService,
33 | TransactionDataService,
34 | TransactionService,
35 | SearchDataService,
36 | SearchService
37 | ],
38 | exports: [
39 | TopNavComponent,
40 | HeaderBarComponent,
41 | TransactionConfirmDialogComponent,
42 | ComponentHeaderComponent
43 | ]
44 | })
45 | export class DemoCommonModule {
46 | }
47 |
--------------------------------------------------------------------------------
/src/app/demo-common/directives/component-header/component-header.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
8 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/app/demo-common/directives/component-header/component-header.component.scss:
--------------------------------------------------------------------------------
1 | @import '../../../../scss/colors';
2 | @import '../../../../scss/mixins';
3 |
4 | @include generic-button();
5 |
6 | .save-button {
7 | margin-left:4px;
8 | }
9 |
10 | .row {
11 | margin-bottom: 1em;
12 | margin-top: 3em;
13 | }
14 |
15 | .header {
16 | @include font-h();
17 | margin-top: -20px;
18 | }
19 |
20 | h2, .h2 {
21 | font-family: Tahoma, Helvetica, Arial, sans-serif;
22 | font-weight: 520;
23 | font-size: 21px;
24 | // margin-top: 0px;
25 | }
26 |
--------------------------------------------------------------------------------
/src/app/demo-common/directives/component-header/component-header.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentHeaderComponent } from './component-header.component';
2 | import { TestInjector } from '../../testing/testing-helpers';
3 |
4 | describe('ComponentHeaderComponent', () => {
5 | let component: ComponentHeaderComponent;
6 | beforeAll(() => {
7 | TestInjector.setInjector();
8 | });
9 |
10 | beforeEach(() => {
11 | component = new ComponentHeaderComponent();
12 | });
13 |
14 | it('should be created', () => {
15 | expect(component).toBeTruthy();
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/src/app/demo-common/directives/component-header/component-header.component.ts:
--------------------------------------------------------------------------------
1 | import { FormGroup, AbstractControl } from '@angular/forms';
2 | import { Component, Output, Input, EventEmitter } from '@angular/core';
3 | import { ActionCode } from '../../../framework/models/authorization.types';
4 |
5 | @Component({
6 | selector: 'la-component-header',
7 | templateUrl: './component-header.component.html',
8 | styleUrls: ['./component-header.component.scss']
9 | })
10 | export class ComponentHeaderComponent {
11 |
12 | @Input() title: string;
13 | @Input() isEditable = false;
14 | @Input() disabled: boolean;
15 | @Input() form: FormGroup;
16 | @Input() updatePermission: ActionCode = 'UPDATE';
17 | @Input() customInvalid = false;
18 | @Output() toggleEdit = new EventEmitter();
19 | @Output() onSave = new EventEmitter();
20 | isAccountReadOnly = false;
21 |
22 | isEditDisabled(): boolean {
23 | if (this.disabled) {
24 | return true;
25 | } else {
26 | return this.isAccountReadOnly;
27 | }
28 | }
29 |
30 | toggle() {
31 | this.toggleEdit.emit(!this.isEditable);
32 | }
33 |
34 | save() {
35 | this.onSave.emit();
36 | }
37 |
38 | formInvalid() {
39 | if (!this.form || !this.form.invalid) {
40 | return false;
41 | }
42 |
43 | let invalid = false; // default to valid
44 |
45 | Object.keys(this.form.controls).forEach(fieldName => {
46 | if (this.form.controls[fieldName] instanceof FormGroup) {
47 | Object.keys((this.form.controls[fieldName]).controls).forEach(fldName => {
48 | if (this.anyControlsInvalid((this.form.controls[fieldName]).controls[fldName])) {
49 | invalid = true;
50 | }
51 | });
52 | } else {
53 | if (this.anyControlsInvalid(this.form.controls[fieldName])) {
54 | invalid = true;
55 | }
56 | }
57 |
58 | });
59 | return invalid;
60 | }
61 |
62 | private anyControlsInvalid(control: AbstractControl) {
63 | let invalidFound = false;
64 | // check if the only errors also are listed as warnings
65 | if (control.errors) {
66 | Object.keys(control.errors).forEach(errorKey => {
67 | // If any errors on this field and none of them are warning or
68 | // one of the errors matches none of the warnings, then form is invalid
69 | if (!(control).warnings ||
70 | !((control).warnings.includes(errorKey))) {
71 | invalidFound = true;
72 | }
73 | });
74 | }
75 | return invalidFound;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/app/demo-common/directives/header-bar/header-bar.component.html:
--------------------------------------------------------------------------------
1 |
22 |
--------------------------------------------------------------------------------
/src/app/demo-common/directives/header-bar/header-bar.component.scss:
--------------------------------------------------------------------------------
1 | @import '../../../../scss/colors';
2 | @import '../../../../scss/mixins';
3 |
4 | .navbar {
5 | min-height: 60px !important;
6 | max-height: 60px;
7 | z-index: 1001;
8 | background-color: #004059;
9 | border: none;
10 |
11 | min-width: 768px;
12 | }
13 | .navbar-brand {
14 | margin-left: 0 !important;
15 | margin-right: 50px;
16 | padding: 0;
17 | line-height: 48px;
18 | }
19 | .navbar-brand img {
20 | height: 44px;
21 | display: inline;
22 | border-right: 1px solid rgb(44, 133, 169);
23 | padding-left: 10px;
24 | padding-right: 30px;
25 | margin-top: 5px;
26 | }
27 | .navbar-brand img + span {
28 | padding-left: 10px;
29 | }
30 | .fa, .glyphicon, li {
31 | color: #f5f5f5;
32 | font-size: 1em;
33 | cursor: pointer;
34 | }
35 | li button {
36 | padding: 15px;
37 | color: #000;
38 | }
39 |
40 | .nav > li > a {
41 | padding: 20px;
42 | }
43 | .nav > li > a:hover, .nav > li > a:active {
44 | color: #fff;
45 | }
46 | .headerSearch {
47 | padding-top: 10px;
48 | }
49 | .closeSearch {
50 | margin-top: 5px;
51 | }
52 | .closeSearch a {
53 | padding-bottom: 35px !important;
54 | }
55 |
56 | .font-light {
57 | font-weight: 100;
58 | }
59 |
60 | .page-title {
61 | @include font-page-title();
62 | position: relative;
63 | top: 7px;
64 | letter-spacing: .25px;
65 | white-space: nowrap;
66 | overflow: hidden;
67 | text-overflow: ellipsis;
68 | }
69 |
70 | .fa-user {
71 | margin-right: 4px;
72 | }
73 |
74 | body .ui-menu .ui-menu-list .ui-menuitem .ui-menuitem-link {
75 | @include link-logout();
76 | }
77 |
78 | header > nav > ul > li {
79 | position: fixed;
80 | top: 0;
81 | right: 0;
82 | }
83 |
84 | div.navbar-header > button {
85 | display: none !important;
86 | }
--------------------------------------------------------------------------------
/src/app/demo-common/directives/header-bar/header-bar.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { Router } from '@angular/router';
2 | import { AuthService } from '../../../framework/services/auth.service';
3 | import { HeaderBarComponent } from './header-bar.component';
4 | import { TestInjector } from '../../testing/testing-helpers';
5 | import { MenuComponent } from '../../../shared/directives/menu/menu.component';
6 |
7 | describe('HeaderBarComponent', () => {
8 | let component: HeaderBarComponent;
9 | beforeAll(() => {
10 | TestInjector.setInjector();
11 | });
12 |
13 | beforeEach(() => {
14 | component = new HeaderBarComponent(
15 | TestInjector.getService(AuthService),
16 | TestInjector.getService(Router));
17 | component.userMenu = new MenuComponent(null);
18 | });
19 |
20 | it('should create', () => {
21 | expect(component).toBeTruthy();
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/src/app/demo-common/directives/header-bar/header-bar.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, ViewChild } from '@angular/core';
2 | import { Router } from '@angular/router';
3 | import { MenuItem } from 'primeng/primeng';
4 | import { AuthService } from '../../../framework/services/auth.service';
5 | import { MenuComponent } from '../../../shared/directives/menu/menu.component';
6 |
7 | @Component({
8 | selector: 'la-header-bar',
9 | templateUrl: './header-bar.component.html',
10 | styleUrls: ['./header-bar.component.scss']
11 | })
12 | export class HeaderBarComponent {
13 | showSearch = false;
14 | userMenuItems: Array