├── .editorconfig ├── .gitignore ├── README.md ├── angular.json ├── browserslist ├── e2e ├── protractor.conf.js ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts └── tsconfig.json ├── karma.conf.js ├── package-lock.json ├── package.json ├── queens.png ├── src ├── app │ ├── app-routing.module.ts │ ├── app.component.css │ ├── app.component.html │ ├── app.component.spec.ts │ ├── app.component.ts │ ├── app.module.ts │ ├── lazy │ │ ├── info │ │ │ ├── info.component.css │ │ │ ├── info.component.html │ │ │ ├── info.component.spec.ts │ │ │ └── info.component.ts │ │ ├── lazy.component.css │ │ ├── lazy.component.html │ │ ├── lazy.component.ts │ │ └── lazy.module.ts │ ├── logic │ │ ├── credits.txt │ │ ├── n-queens.ts │ │ ├── n-queens.worker.ts │ │ └── queens-position.ts │ ├── n-queens │ │ ├── n-queens.component.css │ │ ├── n-queens.component.html │ │ ├── n-queens.component.spec.ts │ │ └── n-queens.component.ts │ └── test.worker.ts ├── assets │ └── .gitkeep ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── main.ts ├── polyfills.ts ├── styles.css └── test.ts ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.spec.json ├── tsconfig.worker.json └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://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 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events.json 15 | speed-measure-plugin.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.sass-cache 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | npm-debug.log 40 | yarn-error.log 41 | testem.log 42 | /typings 43 | 44 | # System Files 45 | .DS_Store 46 | Thumbs.db 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular 8 Demo 2 | 3 | Shows some new features in Angular 8. 4 | 5 | ## Queens Problem 6 | 7 | The implementation of the queens problem shows how we can execute intense calulations in the background using web worker. 8 | 9 | To test it, request a solution for the 12x12 queens problem using the main thread and also using web workers. You'll see that in the first case the UI freezes while in the second case one can interact with it during the calcuation. 10 | 11 | ![Queens Problem](queens.png) 12 | 13 | ## 4 Steps to your web worker in Angular 8 14 | 15 | - Generate a component: ``ng g c my-calc`` 16 | - In the same directory, generate a worker with the same name: ``ng g worker my-calc`` 17 | - This gives you a worker script and some code within the component to call the worker 18 | - Update the worker script to your needs 19 | - Update the code for calling the worker to your needs 20 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "demo-app": { 7 | "projectType": "application", 8 | "schematics": {}, 9 | "root": "", 10 | "sourceRoot": "src", 11 | "prefix": "app", 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/demo-app", 17 | "index": "src/index.html", 18 | "main": "src/main.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "tsconfig.app.json", 21 | "assets": [ 22 | "src/favicon.ico", 23 | "src/assets" 24 | ], 25 | "styles": [ 26 | "src/styles.css" 27 | ], 28 | "scripts": [], 29 | "webWorkerTsConfig": "tsconfig.worker.json" 30 | }, 31 | "configurations": { 32 | "production": { 33 | "fileReplacements": [ 34 | { 35 | "replace": "src/environments/environment.ts", 36 | "with": "src/environments/environment.prod.ts" 37 | } 38 | ], 39 | "optimization": true, 40 | "outputHashing": "all", 41 | "sourceMap": false, 42 | "extractCss": true, 43 | "namedChunks": false, 44 | "aot": true, 45 | "extractLicenses": true, 46 | "vendorChunk": false, 47 | "buildOptimizer": true, 48 | "budgets": [ 49 | { 50 | "type": "initial", 51 | "maximumWarning": "2mb", 52 | "maximumError": "5mb" 53 | } 54 | ] 55 | } 56 | } 57 | }, 58 | "serve": { 59 | "builder": "@angular-devkit/build-angular:dev-server", 60 | "options": { 61 | "browserTarget": "demo-app:build" 62 | }, 63 | "configurations": { 64 | "production": { 65 | "browserTarget": "demo-app:build:production" 66 | } 67 | } 68 | }, 69 | "extract-i18n": { 70 | "builder": "@angular-devkit/build-angular:extract-i18n", 71 | "options": { 72 | "browserTarget": "demo-app:build" 73 | } 74 | }, 75 | "test": { 76 | "builder": "@angular-devkit/build-angular:karma", 77 | "options": { 78 | "main": "src/test.ts", 79 | "polyfills": "src/polyfills.ts", 80 | "tsConfig": "tsconfig.spec.json", 81 | "karmaConfig": "karma.conf.js", 82 | "assets": [ 83 | "src/favicon.ico", 84 | "src/assets" 85 | ], 86 | "styles": [ 87 | "src/styles.css" 88 | ], 89 | "scripts": [] 90 | } 91 | }, 92 | "lint": { 93 | "builder": "@angular-devkit/build-angular:tslint", 94 | "options": { 95 | "tsConfig": [ 96 | "tsconfig.app.json", 97 | "tsconfig.spec.json", 98 | "e2e/tsconfig.json", 99 | "tsconfig.worker.json" 100 | ], 101 | "exclude": [ 102 | "**/node_modules/**" 103 | ] 104 | } 105 | }, 106 | "e2e": { 107 | "builder": "@angular-devkit/build-angular:protractor", 108 | "options": { 109 | "protractorConfig": "e2e/protractor.conf.js", 110 | "devServerTarget": "demo-app:serve" 111 | }, 112 | "configurations": { 113 | "production": { 114 | "devServerTarget": "demo-app:serve:production" 115 | } 116 | } 117 | } 118 | } 119 | }}, 120 | "defaultProject": "demo-app" 121 | } -------------------------------------------------------------------------------- /browserslist: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | # not IE 9-11 # For IE 9-11 support, remove 'not'. 13 | # last 1 chrome version -------------------------------------------------------------------------------- /e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: [ 13 | './src/**/*.e2e-spec.ts' 14 | ], 15 | capabilities: { 16 | 'browserName': 'chrome' 17 | }, 18 | directConnect: true, 19 | baseUrl: 'http://localhost:4200/', 20 | framework: 'jasmine', 21 | jasmineNodeOpts: { 22 | showColors: true, 23 | defaultTimeoutInterval: 30000, 24 | print: function() {} 25 | }, 26 | onPrepare() { 27 | require('ts-node').register({ 28 | project: require('path').join(__dirname, './tsconfig.json') 29 | }); 30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 31 | } 32 | }; -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('Welcome to demo-app!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('app-root h1')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /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/demo-app'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo-app", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "~8.0.0-rc.4", 15 | "@angular/common": "~8.0.0-rc.4", 16 | "@angular/compiler": "~8.0.0-rc.4", 17 | "@angular/core": "~8.0.0-rc.4", 18 | "@angular/forms": "~8.0.0-rc.4", 19 | "@angular/platform-browser": "~8.0.0-rc.4", 20 | "@angular/platform-browser-dynamic": "~8.0.0-rc.4", 21 | "@angular/router": "~8.0.0-rc.4", 22 | "rxjs": "~6.4.0", 23 | "tslib": "^1.9.0", 24 | "zone.js": "~0.9.1" 25 | }, 26 | "devDependencies": { 27 | "@angular-devkit/build-angular": "~0.800.0-rc.4", 28 | "@angular/cli": "~8.0.0-rc.4", 29 | "@angular/compiler-cli": "~8.0.0-rc.4", 30 | "@angular/language-service": "~8.0.0-rc.4", 31 | "@types/node": "~8.9.4", 32 | "@types/jasmine": "~3.3.8", 33 | "@types/jasminewd2": "~2.0.3", 34 | "codelyzer": "^5.0.0", 35 | "jasmine-core": "~3.4.0", 36 | "jasmine-spec-reporter": "~4.2.1", 37 | "karma": "~4.1.0", 38 | "karma-chrome-launcher": "~2.2.0", 39 | "karma-coverage-istanbul-reporter": "~2.0.1", 40 | "karma-jasmine": "~2.0.1", 41 | "karma-jasmine-html-reporter": "^1.4.0", 42 | "protractor": "~5.4.0", 43 | "ts-node": "~7.0.0", 44 | "tslint": "~5.15.0", 45 | "typescript": "~3.4.3" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /queens.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manfredsteyer/angular8/7268c0ea73a94a607c53ccaf0d5643ffc30e64be/queens.png -------------------------------------------------------------------------------- /src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { NQueensComponent } from './n-queens/n-queens.component'; 4 | 5 | const routes: Routes = [ 6 | { 7 | path: '', 8 | component: NQueensComponent, 9 | pathMatch: 'full' 10 | }, 11 | { 12 | path: 'lazy', 13 | loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule) 14 | } 15 | ]; 16 | 17 | @NgModule({ 18 | imports: [RouterModule.forRoot(routes)], 19 | exports: [RouterModule] 20 | }) 21 | export class AppRoutingModule { } 22 | -------------------------------------------------------------------------------- /src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manfredsteyer/angular8/7268c0ea73a94a607c53ccaf0d5643ffc30e64be/src/app/app.component.css -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | Queens | 5 | Lazy 6 |
7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async(() => { 7 | TestBed.configureTestingModule({ 8 | imports: [ 9 | RouterTestingModule 10 | ], 11 | declarations: [ 12 | AppComponent 13 | ], 14 | }).compileComponents(); 15 | })); 16 | 17 | it('should create the app', () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.debugElement.componentInstance; 20 | expect(app).toBeTruthy(); 21 | }); 22 | 23 | it(`should have as title 'demo-app'`, () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | const app = fixture.debugElement.componentInstance; 26 | expect(app.title).toEqual('demo-app'); 27 | }); 28 | 29 | it('should render title in a h1 tag', () => { 30 | const fixture = TestBed.createComponent(AppComponent); 31 | fixture.detectChanges(); 32 | const compiled = fixture.debugElement.nativeElement; 33 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to demo-app!'); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Location, PlatformLocation } from '@angular/common'; 3 | 4 | @Component({ 5 | selector: 'app-root', 6 | templateUrl: './app.component.html', 7 | styleUrls: ['./app.component.css'] 8 | }) 9 | export class AppComponent { 10 | title = 'demo-app'; 11 | 12 | constructor(loc: Location, pLoc: PlatformLocation) { 13 | loc.onUrlChange((url) => console.debug('url change', url)); 14 | console.debug('hostname: ', pLoc.hostname); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { AppRoutingModule } from './app-routing.module'; 5 | import { AppComponent } from './app.component'; 6 | import { NQueensComponent } from './n-queens/n-queens.component'; 7 | import { FormsModule } from '@angular/forms'; 8 | 9 | @NgModule({ 10 | declarations: [ 11 | AppComponent, 12 | NQueensComponent 13 | ], 14 | imports: [ 15 | BrowserModule, 16 | AppRoutingModule, 17 | FormsModule 18 | ], 19 | providers: [], 20 | bootstrap: [ 21 | AppComponent 22 | ] 23 | }) 24 | export class AppModule { } 25 | -------------------------------------------------------------------------------- /src/app/lazy/info/info.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manfredsteyer/angular8/7268c0ea73a94a607c53ccaf0d5643ffc30e64be/src/app/lazy/info/info.component.css -------------------------------------------------------------------------------- /src/app/lazy/info/info.component.html: -------------------------------------------------------------------------------- 1 |

2 | info works! 3 |

4 | -------------------------------------------------------------------------------- /src/app/lazy/info/info.component.spec.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-unused-variable */ 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | import { By } from '@angular/platform-browser'; 4 | import { DebugElement } from '@angular/core'; 5 | 6 | import { InfoComponent } from './info.component'; 7 | 8 | describe('InfoComponent', () => { 9 | let component: InfoComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async(() => { 13 | TestBed.configureTestingModule({ 14 | declarations: [ InfoComponent ] 15 | }) 16 | .compileComponents(); 17 | })); 18 | 19 | beforeEach(() => { 20 | fixture = TestBed.createComponent(InfoComponent); 21 | component = fixture.componentInstance; 22 | fixture.detectChanges(); 23 | }); 24 | 25 | it('should create', () => { 26 | expect(component).toBeTruthy(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /src/app/lazy/info/info.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-info', 5 | templateUrl: './info.component.html', 6 | styleUrls: ['./info.component.css'] 7 | }) 8 | export class InfoComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/lazy/lazy.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manfredsteyer/angular8/7268c0ea73a94a607c53ccaf0d5643ffc30e64be/src/app/lazy/lazy.component.css -------------------------------------------------------------------------------- /src/app/lazy/lazy.component.html: -------------------------------------------------------------------------------- 1 |

2 | Lazy route works! 3 |

4 |
5 |

6 | Please note that beginning with Angular 8 the string-based syntax for loadChildren is deprecated. 7 | Instead, you can use: 8 |

9 |
10 |     loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule)
11 |   
12 | 13 |
-------------------------------------------------------------------------------- /src/app/lazy/lazy.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, AfterContentInit, ElementRef, ViewChild, ContentChild, ViewChildren, AfterViewInit, AfterViewChecked } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-lazy', 5 | templateUrl: './lazy.component.html', 6 | styleUrls: ['./lazy.component.css'] 7 | }) 8 | export class LazyComponent implements OnInit, AfterViewInit, AfterViewChecked { 9 | 10 | constructor() { } 11 | 12 | @ViewChild('info', { static: false }) 13 | paragraph: ElementRef; 14 | 15 | show = true; 16 | 17 | ngOnInit() { 18 | setTimeout(() => { this.show = false; }, 3000); 19 | setTimeout(() => { this.show = true; }, 4000); 20 | console.debug('ngOnInit', this.paragraph); 21 | } 22 | 23 | ngAfterViewInit(): void { 24 | console.debug('ngAfterContentInit', this.paragraph); 25 | } 26 | 27 | ngAfterViewChecked(): void { 28 | console.debug('ngAfterViewChecked', this.paragraph); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/app/lazy/lazy.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { LazyComponent } from './lazy.component'; 4 | import { RouterModule } from '@angular/router'; 5 | import { InfoComponent } from './info/info.component'; 6 | 7 | @NgModule({ 8 | imports: [ 9 | CommonModule, 10 | RouterModule.forChild([ 11 | { 12 | path: '', 13 | component: LazyComponent 14 | } 15 | ]) 16 | ], 17 | declarations: [LazyComponent, InfoComponent] 18 | }) 19 | export class LazyModule { } 20 | -------------------------------------------------------------------------------- /src/app/logic/credits.txt: -------------------------------------------------------------------------------- 1 | # Credits 2 | 3 | This implementation was taken from [1] and slightly 4 | adopted to TypeScript. 5 | 6 | [1] https://github.com/trekhleb/javascript-algorithms 7 | -------------------------------------------------------------------------------- /src/app/logic/n-queens.ts: -------------------------------------------------------------------------------- 1 | import QueenPosition from './queens-position'; 2 | 3 | 4 | function isSafe(queensPositions, rowIndex, columnIndex) { 5 | // New position to which the Queen is going to be placed. 6 | const newQueenPosition = new QueenPosition(rowIndex, columnIndex); 7 | 8 | // Check if new queen position conflicts with any other queens. 9 | // tslint:disable-next-line: prefer-for-of 10 | for (let queenIndex = 0; queenIndex < queensPositions.length; queenIndex += 1) { 11 | const currentQueenPosition = queensPositions[queenIndex]; 12 | 13 | if ( 14 | // Check if queen has been already placed. 15 | currentQueenPosition 16 | && ( 17 | // Check if there are any queen on the same column. 18 | newQueenPosition.columnIndex === currentQueenPosition.columnIndex 19 | // Check if there are any queen on the same row. 20 | || newQueenPosition.rowIndex === currentQueenPosition.rowIndex 21 | // Check if there are any queen on the same left diagonal. 22 | || newQueenPosition.leftDiagonal === currentQueenPosition.leftDiagonal 23 | // Check if there are any queen on the same right diagonal. 24 | || newQueenPosition.rightDiagonal === currentQueenPosition.rightDiagonal 25 | ) 26 | ) { 27 | // Can't place queen into current position since there are other queens that 28 | // are threatening it. 29 | return false; 30 | } 31 | } 32 | 33 | // Looks like we're safe. 34 | return true; 35 | } 36 | 37 | function nQueensRecursive(solutions, previousQueensPositions, queensCount, rowIndex) { 38 | // Clone positions array. 39 | const queensPositions = [...previousQueensPositions].map((queenPosition) => { 40 | return !queenPosition ? queenPosition : new QueenPosition( 41 | queenPosition.rowIndex, 42 | queenPosition.columnIndex, 43 | ); 44 | }); 45 | 46 | if (rowIndex === queensCount) { 47 | // We've successfully reached the end of the board. 48 | // Store solution to the list of solutions. 49 | solutions.push(queensPositions); 50 | 51 | // Solution found. 52 | return true; 53 | } 54 | 55 | // Let's try to put queen at row rowIndex into its safe column position. 56 | for (let columnIndex = 0; columnIndex < queensCount; columnIndex += 1) { 57 | if (isSafe(queensPositions, rowIndex, columnIndex)) { 58 | // Place current queen to its current position. 59 | queensPositions[rowIndex] = new QueenPosition(rowIndex, columnIndex); 60 | 61 | // Try to place all other queens as well. 62 | nQueensRecursive(solutions, queensPositions, queensCount, rowIndex + 1); 63 | 64 | // BACKTRACKING. 65 | // Remove the queen from the row to avoid isSafe() returning false. 66 | queensPositions[rowIndex] = null; 67 | } 68 | } 69 | 70 | return false; 71 | } 72 | 73 | 74 | export default function nQueens(queensCount) { 75 | // Init NxN chessboard with zeros. 76 | // const chessboard = Array(queensCount).fill(null).map(() => Array(queensCount).fill(0)); 77 | 78 | // This array will hold positions or coordinates of each of 79 | // N queens in form of [rowIndex, columnIndex]. 80 | const queensPositions = Array(queensCount).fill(null); 81 | 82 | /** @var {QueenPosition[][]} solutions */ 83 | const solutions = []; 84 | 85 | // Solve problem recursively. 86 | nQueensRecursive(solutions, queensPositions, queensCount, 0); 87 | 88 | return solutions; 89 | } 90 | -------------------------------------------------------------------------------- /src/app/logic/n-queens.worker.ts: -------------------------------------------------------------------------------- 1 | import nQueens from './n-queens'; 2 | 3 | addEventListener('message', ({ data }) => { 4 | const result = nQueens(data.count); 5 | postMessage(result, undefined); 6 | }); 7 | -------------------------------------------------------------------------------- /src/app/logic/queens-position.ts: -------------------------------------------------------------------------------- 1 | export type Solution = QueenPosition[]; 2 | 3 | /** 4 | * Class that represents queen position on the chessboard. 5 | */ 6 | export default class QueenPosition { 7 | rowIndex: number; 8 | columnIndex: number; 9 | 10 | /** 11 | * @param {number} rowIndex 12 | * @param {number} columnIndex 13 | */ 14 | constructor(rowIndex, columnIndex) { 15 | this.rowIndex = rowIndex; 16 | this.columnIndex = columnIndex; 17 | } 18 | 19 | /** 20 | * @return {number} 21 | */ 22 | get leftDiagonal() { 23 | // Each position on the same left (\) diagonal has the same difference of 24 | // rowIndex and columnIndex. This fact may be used to quickly check if two 25 | // positions (queens) are on the same left diagonal. 26 | // @see https://youtu.be/xouin83ebxE?t=1m59s 27 | return this.rowIndex - this.columnIndex; 28 | } 29 | 30 | /** 31 | * @return {number} 32 | */ 33 | get rightDiagonal() { 34 | // Each position on the same right diagonal (/) has the same 35 | // sum of rowIndex and columnIndex. This fact may be used to quickly 36 | // check if two positions (queens) are on the same right diagonal. 37 | // @see https://youtu.be/xouin83ebxE?t=1m59s 38 | return this.rowIndex + this.columnIndex; 39 | } 40 | 41 | toString() { 42 | return `${this.rowIndex},${this.columnIndex}`; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/app/n-queens/n-queens.component.css: -------------------------------------------------------------------------------- 1 | 2 | table { 3 | display: table; 4 | padding: 0px; 5 | border-collapse: collapse; 6 | } 7 | 8 | td { 9 | height: 50px; 10 | width: 50px; 11 | background-color: burlywood; 12 | text-align: center; 13 | border: 0px; 14 | font-weight: bold; 15 | color: black; 16 | font-size:20px; 17 | } 18 | 19 | td.black { 20 | background-color: brown; 21 | } 22 | 23 | .control-bar { 24 | margin-top:20px; 25 | margin-bottom: 20px; 26 | } 27 | -------------------------------------------------------------------------------- /src/app/n-queens/n-queens.component.html: -------------------------------------------------------------------------------- 1 |

ngQueens

2 | 3 |

4 | Count: 5 | 6 | 7 |

8 | 9 |
10 | 11 |
12 | 13 | {{ currentSolutionIndex + 1 }} / 14 | {{ solutions.length }} 15 | 16 |
17 | 18 | 19 | 20 | 23 | 24 |
21 | 22 |
25 | 26 |
-------------------------------------------------------------------------------- /src/app/n-queens/n-queens.component.spec.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-unused-variable */ 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | import { By } from '@angular/platform-browser'; 4 | import { DebugElement } from '@angular/core'; 5 | 6 | import { NQueensComponent } from './n-queens.component'; 7 | 8 | describe('NQueensComponent', () => { 9 | let component: NQueensComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async(() => { 13 | TestBed.configureTestingModule({ 14 | declarations: [ NQueensComponent ] 15 | }) 16 | .compileComponents(); 17 | })); 18 | 19 | beforeEach(() => { 20 | fixture = TestBed.createComponent(NQueensComponent); 21 | component = fixture.componentInstance; 22 | fixture.detectChanges(); 23 | }); 24 | 25 | it('should create', () => { 26 | expect(component).toBeTruthy(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /src/app/n-queens/n-queens.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import nQueens from '../logic/n-queens'; 3 | import { Solution } from '../logic/queens-position'; 4 | 5 | @Component({ 6 | selector: 'app-n-queens', 7 | templateUrl: './n-queens.component.html', 8 | styleUrls: ['./n-queens.component.css'] 9 | }) 10 | export class NQueensComponent implements OnInit { 11 | 12 | constructor() { } 13 | 14 | count = '8'; 15 | solutions: Solution[] = []; 16 | 17 | cols: number[] = []; 18 | rows: number[] = []; 19 | 20 | currentSolutionIndex = 0; 21 | currentSolution: Solution; 22 | 23 | ngOnInit() { 24 | } 25 | 26 | calc() { 27 | const count = parseInt(this.count, 10); 28 | const result = nQueens(count); 29 | // tslint:disable-next-line: no-console 30 | console.debug('result', result); 31 | 32 | this.processResult(result); 33 | } 34 | 35 | calcWithWorker() { 36 | const count = parseInt(this.count, 10); 37 | 38 | const worker = new Worker('../logic/n-queens.worker', { 39 | type: 'module' 40 | }); 41 | 42 | worker.addEventListener('message', (event) => { 43 | // tslint:disable-next-line: no-console 44 | console.debug('worker result', event.data); 45 | 46 | this.processResult(event.data); 47 | }); 48 | 49 | worker.postMessage({count}); 50 | } 51 | 52 | next() { 53 | if (this.currentSolutionIndex < this.solutions.length - 1) { 54 | this.currentSolutionIndex++; 55 | this.update(); 56 | } 57 | } 58 | 59 | prev() { 60 | if (this.currentSolutionIndex > 0) { 61 | this.currentSolutionIndex--; 62 | this.update(); 63 | } 64 | } 65 | 66 | hasQueen(row: number, col: number) { 67 | // TODO: Refactor into pipe 68 | return !!this.currentSolution.find(q => q.rowIndex === row && q.columnIndex === col); 69 | } 70 | 71 | isBlack(row: number, col: number) { 72 | // TODO: Refactor into pipe 73 | const offset = row % 2; 74 | return (col + 1 + offset) % 2 === 0; 75 | } 76 | 77 | private update() { 78 | this.currentSolution = this.solutions[this.currentSolutionIndex]; 79 | } 80 | 81 | private range(from: number, to: number) { 82 | const result = []; 83 | for (let i = from; i <= to; i++) { 84 | result.push(i); 85 | } 86 | return result; 87 | } 88 | 89 | private processResult(solutions: Solution[]) { 90 | const count = parseInt(this.count, 10); 91 | this.cols = this.range(0, count - 1); 92 | this.rows = this.range(0, count - 1); 93 | this.solutions = solutions; 94 | this.currentSolutionIndex = 0; 95 | this.update(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/app/test.worker.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | addEventListener('message', ({ data }) => { 4 | const response = `worker response to ${data}`; 5 | postMessage(response); 6 | }); 7 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manfredsteyer/angular8/7268c0ea73a94a607c53ccaf0d5643ffc30e64be/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manfredsteyer/angular8/7268c0ea73a94a607c53ccaf0d5643ffc30e64be/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DemoApp 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags.ts'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | 61 | /*************************************************************************************************** 62 | * APPLICATION IMPORTS 63 | */ 64 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | button { 3 | border-radius: 5px; 4 | border: 1px solid black; 5 | background-color:lightsteelblue; 6 | padding: 5px; 7 | margin: 5px; 8 | cursor: pointer; 9 | } 10 | 11 | input { 12 | border-radius: 3px; 13 | border: 1px solid black; 14 | padding: 5px; 15 | margin: 5px; 16 | } 17 | 18 | body { 19 | font-family: Arial; 20 | } -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": [] 6 | }, 7 | "include": [ 8 | "src/**/*.ts" 9 | ], 10 | "exclude": [ 11 | "src/test.ts", 12 | "src/**/*.spec.ts", 13 | "src/**/*.worker.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "importHelpers": true, 13 | "target": "es2015", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /tsconfig.worker.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/worker", 5 | "lib": [ 6 | "es2018", 7 | "webworker" 8 | ], 9 | "types": [] 10 | }, 11 | "include": [ 12 | "src/**/*.worker.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "array-type": false, 5 | "arrow-parens": false, 6 | "deprecation": { 7 | "severity": "warn" 8 | }, 9 | "component-class-suffix": true, 10 | "contextual-lifecycle": true, 11 | "directive-class-suffix": true, 12 | "directive-selector": [ 13 | true, 14 | "attribute", 15 | "app", 16 | "camelCase" 17 | ], 18 | "component-selector": [ 19 | true, 20 | "element", 21 | "app", 22 | "kebab-case" 23 | ], 24 | "import-blacklist": [ 25 | true, 26 | "rxjs/Rx" 27 | ], 28 | "interface-name": false, 29 | "max-classes-per-file": false, 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-consecutive-blank-lines": false, 47 | "no-console": [ 48 | true, 49 | "debug", 50 | "info", 51 | "time", 52 | "timeEnd", 53 | "trace" 54 | ], 55 | "no-empty": false, 56 | "no-inferrable-types": [ 57 | true, 58 | "ignore-params" 59 | ], 60 | "no-non-null-assertion": true, 61 | "no-redundant-jsdoc": true, 62 | "no-switch-case-fall-through": true, 63 | "no-use-before-declare": true, 64 | "no-var-requires": false, 65 | "object-literal-key-quotes": [ 66 | true, 67 | "as-needed" 68 | ], 69 | "object-literal-sort-keys": false, 70 | "ordered-imports": false, 71 | "quotemark": [ 72 | true, 73 | "single" 74 | ], 75 | "trailing-comma": false, 76 | "no-conflicting-lifecycle": true, 77 | "no-host-metadata-property": true, 78 | "no-input-rename": true, 79 | "no-inputs-metadata-property": true, 80 | "no-output-native": true, 81 | "no-output-on-prefix": true, 82 | "no-output-rename": true, 83 | "no-outputs-metadata-property": true, 84 | "template-banana-in-box": true, 85 | "template-no-negated-async": true, 86 | "use-lifecycle-interface": true, 87 | "use-pipe-transform-interface": true 88 | }, 89 | "rulesDirectory": [ 90 | "codelyzer" 91 | ] 92 | } --------------------------------------------------------------------------------