เริ่มทำ topic web app โดยมี student กับ mock-students

parent da7b218d
const express = require('express');
const app = express();
const port = 3000;
app.get('/', (req,res) => res.send('Hello world') );
app.get('/about', (req,res) => res.send("I'm paul.") );
app.listen(port, () => {
console.log('Yeah yeah server started...');
})
This diff is collapsed.
{
"name": "expressapp01",
"version": "1.0.0",
"description": "test express api",
"main": "index.js",
"dependencies": {
"express": "^4.17.1"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"expressjs",
"node",
"topic1"
],
"author": "paul",
"license": "ISC"
}
Hello World
\ No newline at end of file
const http = require('http');
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer((req, response) => {
console.log('มีคน request มานะ');
response.statusCode = 200;
response.setHeader('Content-Type', 'text/html');
response.end(`<h1>Hello World<h1>`);
});
server.listen(port, hostname, () => {
console.log(`เปิดเว็บไว้ที่ http://${hostname}:${port}/`);
});
\ No newline at end of file
"use strict";
exports.__esModule = true;
var electron_1 = require("electron");
var win = null;
electron_1.app.on('ready', function () {
win = new electron_1.BrowserWindow({ width: 500, height: 400 });
win.setTitle('หน้าต่าง');
win.on('closed', function () { win = null; });
});
import { app, BrowserWindow } from 'electron'
let win = null
app.on('ready', () => {
win = new BrowserWindow({ width: 500, height: 400 })
win.setTitle('หน้าต่าง')
win.on('closed', () => { win = null })
})
This diff is collapsed.
{
"name": "app0501",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"electron": "^9.1.0"
}
}
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>CSUBU Lecturers</title>
<style>
.avatar {
vertical-align: middle;
width: 50px;
height: 50px;
border-radius: 50%;
}
</style>
</head>
<body>
<div>
<div>
<img class="avatar" src="https://picsum.photos/id/1001/84/84" alt="Dr.K">
Dr. K
</div>
<div><img class="avatar" src="https://picsum.photos/id/1002/84/84" alt="Assist.Prof.Chayaporn">Assist.Prof.Chayaporn</div>
<div><img class="avatar" src="https://picsum.photos/id/1023/84/84" alt="Dr.Tossaporn">Dr.Tossaporn</div>
<div><img class="avatar" src="https://picsum.photos/id/1004/84/84" alt="Dr.Phaichayon">Dr.Phaichayon</div>
<div><img class="avatar" src="https://picsum.photos/id/1005/84/84" alt="Dr.Woody">Dr.Woody</div>
<div><img class="avatar" src="https://picsum.photos/id/1006/84/84" alt="Aj.Wasana">Aj.Wasana</div>
<div><img class="avatar" src="https://picsum.photos/id/1027/84/84" alt="Dr.Paul">Dr.Paul</div>
<div><img class="avatar" src="https://picsum.photos/id/1008/84/84" alt="Dr.Suphawadee">Dr.Suphawadee</div>
<div><img class="avatar" src="https://picsum.photos/id/1009/84/84" alt="Aj.Wayo">Aj.Wayo</div>
</div>
<footer>
Node <script>document.write(process.versions.node)</script>,
Chrome <script>document.write(process.versions.chrome)</script>,
and Electron <script>document.write(process.versions.electron)</script>.
</footer>
</body>
</html>
"use strict";
exports.__esModule = true;
var electron_1 = require("electron");
var win = null;
function createWindow() {
win = new electron_1.BrowserWindow({ width: 500, height: 400 });
win.setTitle('CSUBU Lecturers');
win.loadFile('index.html');
win.on('closed', function () { win = null; });
}
electron_1.app.on('ready', function () {
createWindow();
});
import { app, BrowserWindow } from 'electron';
let win = null;
function createWindow() {
win = new BrowserWindow({width: 500, height: 400})
win.setTitle('CSUBU Lecturers')
win.loadFile('index.html')
win.on('closed', () => { win = null })
}
app.on('ready', () => {
createWindow()
})
This diff is collapsed.
{
"name": "csubuapp-0502",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"go": "tsc index.ts && electron .",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"electron": "^9.1.0"
}
}
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
}
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For the full list of supported browsers by the Angular framework, please see:
# https://angular.io/guide/browser-support
# You can see what browsers were selected by your queries by running:
# npx browserslist
last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major versions
last 2 iOS major versions
Firefox ESR
not IE 9-10 # Angular support for IE 9-10 has been deprecated and will be removed as of Angular v11. To opt-in, remove the 'not' prefix on this line.
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events*.json
speed-measure-plugin*.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
# Csubuapp0503
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.0.2.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"csubuapp0503": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/csubuapp0503",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": true,
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.scss"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "csubuapp0503:build"
},
"configurations": {
"production": {
"browserTarget": "csubuapp0503:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "csubuapp0503:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.scss"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "csubuapp0503:serve"
},
"configurations": {
"production": {
"devServerTarget": "csubuapp0503:serve:production"
}
}
}
}
}},
"defaultProject": "csubuapp0503"
}
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
browserName: 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({
spec: {
displayStacktrace: StacktraceOption.PRETTY
}
}));
}
};
\ No newline at end of file
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('csubuapp0503 app is running!');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo(): Promise<unknown> {
return browser.get(browser.baseUrl) as Promise<unknown>;
}
getTitleText(): Promise<string> {
return element(by.css('app-root .content span')).getText() as Promise<string>;
}
}
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es2018",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage/csubuapp0503'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};
This diff is collapsed.
{
"name": "csubuapp0503",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "~10.0.3",
"@angular/common": "~10.0.3",
"@angular/compiler": "~10.0.3",
"@angular/core": "~10.0.3",
"@angular/forms": "~10.0.3",
"@angular/platform-browser": "~10.0.3",
"@angular/platform-browser-dynamic": "~10.0.3",
"@angular/router": "~10.0.3",
"rxjs": "~6.5.5",
"tslib": "^2.0.0",
"zone.js": "~0.10.3"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.1000.2",
"@angular/cli": "~10.0.2",
"@angular/compiler-cli": "~10.0.3",
"@types/node": "^12.11.1",
"@types/jasmine": "~3.5.0",
"@types/jasminewd2": "~2.0.3",
"codelyzer": "^6.0.0",
"jasmine-core": "~3.5.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~5.0.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~3.3.0",
"karma-jasmine-html-reporter": "^1.5.0",
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "~3.9.5"
}
}
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
<div>
<img *ngFor="let lecturer of lecturers" class="avatar" [src]="lecturer.image" [alt]="lecturer.name">
</div>
.avatar {
vertical-align: middle;
width: 50px;
height: 50px;
border-radius: 50%;
}
import { TestBed, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule
],
declarations: [
AppComponent
],
}).compileComponents();
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'csubuapp0503'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('csubuapp0503');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement;
expect(compiled.querySelector('.content span').textContent).toContain('csubuapp0503 app is running!');
});
});
import { Component } from '@angular/core';
interface Lecturer {
name: string;
image: string;
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'csubuapp0503';
lecturers: Lecturer[] = [
{ image: 'https://picsum.photos/id/1001/84/84', name: 'Dr.K' },
{ image: 'https://picsum.photos/id/1002/84/84', name: 'Assist.Prof.Chayaporn' },
{ image: 'https://picsum.photos/id/1003/84/84', name: 'Dr.Tossaporn' },
{ image: 'https://picsum.photos/id/1004/84/84', name: 'Dr.Phaichayon' },
{ image: 'https://picsum.photos/id/1005/84/84', name: 'Dr.Woody' },
{ image: 'https://picsum.photos/id/1006/84/84', name: 'Aj.Wasana' },
{ image: 'https://picsum.photos/id/1027/84/84', name: 'Dr.Paul' },
{ image: 'https://picsum.photos/id/1008/84/84', name: 'Dr.Suphawadee' },
{ image: 'https://picsum.photos/id/1009/84/84', name: 'Aj.Wayo' }
];
}
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
export const environment = {
production: true
};
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Csubuapp0503</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
/* You can add global styles to this file, and also import other style files */
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: {
context(path: string, deep?: boolean, filter?: RegExp): {
keys(): string[];
<T>(id: string): T;
};
};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"files": [
"src/main.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.d.ts"
]
}
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"importHelpers": true,
"target": "es2015",
"module": "es2020",
"lib": [
"es2018",
"dom"
]
}
}
/*
This is a "Solution Style" tsconfig.json file, and is used by editors and TypeScript’s language server to improve development experience.
It is not intended to be used to perform a compilation.
To learn more about this file see: https://angular.io/config/solution-tsconfig.
*/
{
"files": [],
"references": [
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.spec.json"
},
{
"path": "./e2e/tsconfig.json"
}
]
}
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine"
]
},
"files": [
"src/test.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}
{
"extends": "tslint:recommended",
"rules": {
"align": {
"options": [
"parameters",
"statements"
]
},
"array-type": false,
"arrow-return-shorthand": true,
"curly": true,
"deprecation": {
"severity": "warning"
},
"component-class-suffix": true,
"contextual-lifecycle": true,
"directive-class-suffix": true,
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
],
"eofline": true,
"import-blacklist": [
true,
"rxjs/Rx"
],
"import-spacing": true,
"indent": {
"options": [
"spaces"
]
},
"max-classes-per-file": false,
"max-line-length": [
true,
140
],
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-empty": false,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-non-null-assertion": true,
"no-redundant-jsdoc": true,
"no-switch-case-fall-through": true,
"no-var-requires": false,
"object-literal-key-quotes": [
true,
"as-needed"
],
"quotemark": [
true,
"single"
],
"semicolon": {
"options": [
"always"
]
},
"space-before-function-paren": {
"options": {
"anonymous": "never",
"asyncArrow": "always",
"constructor": "never",
"method": "never",
"named": "never"
}
},
"typedef": [
true,
"call-signature"
],
"typedef-whitespace": {
"options": [
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
},
{
"call-signature": "onespace",
"index-signature": "onespace",
"parameter": "onespace",
"property-declaration": "onespace",
"variable-declaration": "onespace"
}
]
},
"variable-name": {
"options": [
"ban-keywords",
"check-format",
"allow-pascal-case"
]
},
"whitespace": {
"options": [
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type",
"check-typecast"
]
},
"no-conflicting-lifecycle": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-inputs-metadata-property": true,
"no-output-native": true,
"no-output-on-prefix": true,
"no-output-rename": true,
"no-outputs-metadata-property": true,
"template-banana-in-box": true,
"template-no-negated-async": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true
},
"rulesDirectory": [
"codelyzer"
]
}
\ No newline at end of file
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false
{
"env": {
"browser": true,
"node": true,
"es6": true,
"es2017": true
},
"overrides": [
{
"files": ["*.ts"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 10,
"project": [
"./src/tsconfig.app.json",
"./src/tsconfig.spec.json",
"./e2e/tsconfig.e2e.json"
],
"sourceType": "module",
"ecmaFeatures": {
"modules": true
}
},
"plugins": [
"@typescript-eslint",
"@angular-eslint/eslint-plugin"
],
"rules": {
"@typescript-eslint/indent": [
"error", 2, {
"SwitchCase": 1,
"CallExpression": {"arguments": "first"},
"FunctionExpression": {"parameters": "first"},
"FunctionDeclaration": {"parameters": "first"}
}
],
"@typescript-eslint/no-empty-function": 0,
"@typescript-eslint/no-var-requires": 0,
"@typescript-eslint/no-explicit-any": 0,
"@typescript-eslint/no-unsafe-call": 0,
"@typescript-eslint/no-unsafe-member-access": 0,
"@typescript-eslint/no-unsafe-assignment": 0,
"@typescript-eslint/no-unsafe-return": 0,
"@typescript-eslint/no-floating-promises": 0,
"@angular-eslint/use-injectable-provided-in": "error",
"@angular-eslint/no-attribute-decorator": "error"
}
},
{
"files": ["*.component.html"],
"parser": "@angular-eslint/template-parser",
"plugins": ["@angular-eslint/template"],
"rules": {
"@angular-eslint/template/banana-in-a-box": "error",
"@angular-eslint/template/no-negated-async": "error"
}
}
]
}
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
/app-builds
/release
main.js
src/**/*.js
!src/karma.conf.js
*.js.map
# dependencies
/node_modules
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
testem.log
/typings
package-lock.json
# e2e
/e2e/*.js
!/e2e/protractor.conf.js
/e2e/*.map
# System Files
.DS_Store
Thumbs.db
save=true
save-exact=true
os:
- linux
- osx
language: node_js
node_js:
- '12'
- '10'
dist: xenial
sudo: required
services:
- xvfb
before_script:
- export DISPLAY=:99.0
install:
- npm set progress=false
- npm install
script:
- ng lint
- npm run test
- npm run e2e
- npm run build
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Electron Main Renderer",
"type": "node",
"request": "launch",
"protocol": "inspector",
// Prelaunch task compiles main.ts for Electron & starts Angular dev server.
"preLaunchTask": "Build.All",
"cwd": "${workspaceFolder}",
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
"runtimeArgs": [
"--serve",
".",
"--remote-debugging-port=9222"
],
"windows": {
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
}
}, {
"name": "Karma Attach Chrome",
"type": "chrome",
"request": "attach",
"port": 9222,
"webRoot": "${workspaceFolder}/",
"sourceMaps": true,
"timeout": 30000,
"trace": true
}
]
}
\ No newline at end of file
{
"version": "2.0.0",
"tasks": [
{
"label": "Build.All",
"type": "shell",
"command": "npm run electron:serve-tsc && ng serve",
"isBackground": true,
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": {
"owner": "typescript",
"source": "ts",
"applyTo": "closedDocuments",
"fileLocation": ["relative", "${cwd}"],
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": "^.*",
"endsPattern": "^.*Compiled successfully.*"
}
}
}
]
}
\ No newline at end of file
This diff is collapsed.
Copyright 2020 - Maxime GRIS
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
[![Angular Logo](https://www.vectorlogo.zone/logos/angular/angular-icon.svg)](https://angular.io/) [![Electron Logo](https://www.vectorlogo.zone/logos/electronjs/electronjs-icon.svg)](https://electronjs.org/)
![Maintained][maintained-badge]
[![Travis Build Status][build-badge]][build]
[![Make a pull request][prs-badge]][prs]
[![License](http://img.shields.io/badge/Licence-MIT-brightgreen.svg)](LICENSE.md)
[![Watch on GitHub][github-watch-badge]][github-watch]
[![Star on GitHub][github-star-badge]][github-star]
[![Tweet][twitter-badge]][twitter]
# Introduction
Bootstrap and package your project with Angular 10 and Electron 8 (Typescript + SASS + Hot Reload) for creating Desktop applications.
Currently runs with:
- Angular v10.0.3
- Electron v9.1.0
- Electron Builder v22.7.0
With this sample, you can :
- Run your app in a local development environment with Electron & Hot reload
- Run your app in a production environment
- Package your app into an executable file for Linux, Windows & Mac
/!\ Hot reload only pertains to the renderer process. The main electron process is not able to be hot reloaded, only restarted.
/!\ Angular 10.x CLI needs Node 10.13 or later to work correctly.
## Getting Started
Clone this repository locally :
``` bash
git clone https://github.com/maximegris/angular-electron.git
```
Install dependencies with npm :
``` bash
npm install
```
There is an issue with `yarn` and `node_modules` when the application is built by the packager. Please use `npm` as dependencies manager.
If you want to generate Angular components with Angular-cli , you **MUST** install `@angular/cli` in npm global context.
Please follow [Angular-cli documentation](https://github.com/angular/angular-cli) if you had installed a previous version of `angular-cli`.
``` bash
npm install -g @angular/cli
```
## To build for development
- **in a terminal window** -> npm start
Voila! You can use your Angular + Electron app in a local development environment with hot reload !
The application code is managed by `main.ts`. In this sample, the app runs with a simple Angular App (http://localhost:4200) and an Electron window.
The Angular component contains an example of Electron and NodeJS native lib import.
You can disable "Developer Tools" by commenting `win.webContents.openDevTools();` in `main.ts`.
## Included Commands
|Command|Description|
|--|--|
|`npm run ng:serve:web`| Execute the app in the browser |
|`npm run build`| Build the app. Your built files are in the /dist folder. |
|`npm run build:prod`| Build the app with Angular aot. Your built files are in the /dist folder. |
|`npm run electron:local`| Builds your application and start electron
|`npm run electron:build`| Builds your application and creates an app consumable based on your operating system |
**Your application is optimised. Only /dist folder and node dependencies are included in the executable.**
## You want to use a specific lib (like rxjs) in electron main thread ?
YES! You can do it! Just by importing your library in npm dependencies section (not **devDependencies**) with `npm install --save`. It will be loaded by electron during build phase and added to your final package. Then use your library by importing it in `main.ts` file. Quite simple, isn't it ?
## Browser mode
Maybe you want to execute the application in the browser with hot reload ? Just run `npm run ng:serve:web`.
**Note that you can't use Electron or NodeJS native libraries in this case.** Please check `providers/electron.service.ts` to watch how conditional import of electron/Native libraries is done.
## Branch & Packages version
- Angular 4 & Electron 1 : Branch [angular4](https://github.com/maximegris/angular-electron/tree/angular4)
- Angular 5 & Electron 1 : Branch [angular5](https://github.com/maximegris/angular-electron/tree/angular5)
- Angular 6 & Electron 3 : Branch [angular6](https://github.com/maximegris/angular-electron/tree/angular6)
- Angular 7 & Electron 3 : Branch [angular7](https://github.com/maximegris/angular-electron/tree/angular7)
- Angular 8 & Electron 7 : Branch [angular8](https://github.com/maximegris/angular-electron/tree/angular8)
- Angular 9 & Electron 7 : Branch [angular9](https://github.com/maximegris/angular-electron/tree/angular9)
- Angular 10 & Electron 9 : (master)
[build-badge]: https://travis-ci.org/maximegris/angular-electron.svg?branch=master&style=style=flat-square
[build]: https://travis-ci.org/maximegris/angular-electron
[license-badge]: https://img.shields.io/badge/license-Apache2-blue.svg?style=style=flat-square
[license]: https://github.com/maximegris/angular-electron/blob/master/LICENSE.md
[prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square
[prs]: http://makeapullrequest.com
[github-watch-badge]: https://img.shields.io/github/watchers/maximegris/angular-electron.svg?style=social
[github-watch]: https://github.com/maximegris/angular-electron/watchers
[github-star-badge]: https://img.shields.io/github/stars/maximegris/angular-electron.svg?style=social
[github-star]: https://github.com/maximegris/angular-electron/stargazers
[twitter]: https://twitter.com/intent/tweet?text=Check%20out%20angular-electron!%20https://github.com/maximegris/angular-electron%20%F0%9F%91%8D
[twitter-badge]: https://img.shields.io/twitter/url/https/github.com/maximegris/angular-electron.svg?style=social
[maintained-badge]: https://img.shields.io/badge/maintained-yes-brightgreen
theme: jekyll-theme-architect
\ No newline at end of file
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"angular-electron": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"architect": {
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"outputPath": "dist",
"index": "src/index.html",
"main": "src/main.ts",
"tsConfig": "src/tsconfig.app.json",
"polyfills": "src/polyfills.ts",
"assets": [
"src/assets"
],
"styles": [
"src/styles.scss"
],
"scripts": [],
"customWebpackConfig": {
"path": "./angular.webpack.js"
}
},
"configurations": {
"dev": {
"optimization": false,
"outputHashing": "all",
"sourceMap": true,
"extractCss": true,
"namedChunks": false,
"aot": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": false,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.dev.ts"
}
]
},
"web": {
"optimization": false,
"outputHashing": "all",
"sourceMap": true,
"extractCss": true,
"namedChunks": false,
"aot": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": false,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.web.ts"
}
]
},
"production": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
]
}
}
},
"serve": {
"builder": "@angular-builders/custom-webpack:dev-server",
"options": {
"browserTarget": "angular-electron:build"
},
"configurations": {
"dev": {
"browserTarget": "angular-electron:build:dev"
},
"web": {
"browserTarget": "angular-electron:build:web"
},
"production": {
"browserTarget": "angular-electron:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "angular-electron:build"
}
},
"test": {
"builder": "@angular-builders/custom-webpack:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills-test.ts",
"tsConfig": "src/tsconfig.spec.json",
"karmaConfig": "src/karma.conf.js",
"scripts": [],
"styles": [
"src/styles.scss"
],
"assets": [
"src/assets"
],
"customWebpackConfig": {
"path": "./angular.webpack.js",
"target": "electron-renderer"
}
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"eslintConfig": ".eslintrc.json",
"tsConfig": [
"src/tsconfig.app.json",
"src/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"angular-electron-e2e": {
"root": "e2e",
"projectType": "application",
"architect": {
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"eslintConfig": ".eslintrc.json",
"tsConfig": [
"e2e/tsconfig.e2e.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"defaultProject": "angular-electron",
"schematics": {
"@schematics/angular:component": {
"prefix": "app",
"style": "scss"
},
"@schematics/angular:directive": {
"prefix": "app"
}
}
}
/**
* Custom angular webpack configuration
*/
module.exports = (config, options) => {
config.target = 'electron-renderer';
if (options.customWebpackConfig.target) {
config.target = options.customWebpackConfig.target;
} else if (options.fileReplacements) {
for(let fileReplacement of options.fileReplacements) {
if (fileReplacement.replace !== 'src/environments/environment.ts') {
continue;
}
let fileReplacementParts = fileReplacement['with'].split('.');
if (['dev', 'prod', 'test', 'electron-renderer'].indexOf(fileReplacementParts[1]) < 0) {
config.target = fileReplacementParts[1];
}
break;
}
}
return config;
}
const Application = require('spectron').Application;
const electronPath = require('electron'); // Require Electron from the binaries included in node_modules.
const path = require('path');
export default function setup(): void {
beforeEach(async function () {
this.app = new Application({
// Your electron path can be any binary
// i.e for OSX an example path could be '/Applications/MyApp.app/Contents/MacOS/MyApp'
// But for the sake of the example we fetch it from our node_modules.
path: electronPath,
// Assuming you have the following directory structure
// |__ my project
// |__ ...
// |__ main.js
// |__ package.json
// |__ index.html
// |__ ...
// |__ test
// |__ spec.js <- You are here! ~ Well you should be.
// The following line tells spectron to look and use the main.js file
// and the package.json located 1 level above.
args: [path.join(__dirname, '..')],
webdriverOptions: {}
});
await this.app.start();
const browser = this.app.client;
await browser.waitUntilWindowLoaded();
browser.timeouts('script', 15000);
});
afterEach(function () {
if (this.app && this.app.isRunning()) {
return this.app.stop();
}
});
}
import { expect } from 'chai';
import { SpectronClient } from 'spectron';
import commonSetup from './common-setup';
describe('angular-electron App', function () {
commonSetup.apply(this);
let browser: any;
let client: SpectronClient;
beforeEach(function () {
client = this.app.client;
browser = client as any;
});
it('should display message saying App works !', async function () {
const text = await browser.getText('app-home h1');
expect(text).to.equal('App works !');
});
it('creates initial windows', async function () {
const count = await client.getWindowCount();
expect(count).to.equal(1);
});
});
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"types": [
"mocha"
]
},
"include": [
"e2e/**/*.ts"
]
}
{
"productName": "angular-electron",
"directories": {
"output": "release/"
},
"files": [
"**/*",
"!**/*.ts",
"!*.code-workspace",
"!LICENSE.md",
"!package.json",
"!package-lock.json",
"!src/",
"!e2e/",
"!hooks/",
"!angular.json",
"!_config.yml",
"!karma.conf.js",
"!tsconfig.json",
"!tslint.json"
],
"win": {
"icon": "dist/assets/icons",
"target": [
"portable"
]
},
"mac": {
"icon": "dist/assets/icons",
"target": [
"dmg"
]
},
"linux": {
"icon": "dist/assets/icons",
"target": [
"AppImage"
]
}
}
import { app, BrowserWindow, screen } from 'electron';
import * as path from 'path';
import * as url from 'url';
let win: BrowserWindow = null;
const args = process.argv.slice(1),
serve = args.some(val => val === '--serve');
function createWindow(): BrowserWindow {
const electronScreen = screen;
const size = electronScreen.getPrimaryDisplay().workAreaSize;
// Create the browser window.
win = new BrowserWindow({
x: 0,
y: 0,
width: size.width,
height: size.height,
webPreferences: {
nodeIntegration: true,
allowRunningInsecureContent: (serve) ? true : false,
},
});
if (serve) {
win.webContents.openDevTools();
require('electron-reload')(__dirname, {
electron: require(`${__dirname}/node_modules/electron`)
});
win.loadURL('http://localhost:4200');
} else {
win.loadURL(url.format({
pathname: path.join(__dirname, 'dist/index.html'),
protocol: 'file:',
slashes: true
}));
}
// Emitted when the window is closed.
win.on('closed', () => {
// Dereference the window object, usually you would store window
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
win = null;
});
return win;
}
try {
app.allowRendererProcessReuse = true;
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
// Added 400 ms to fix the black background issue while using transparent window. More detais at https://github.com/electron/electron/issues/15947
app.on('ready', () => setTimeout(createWindow, 400));
// Quit when all windows are closed.
app.on('window-all-closed', () => {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (win === null) {
createWindow();
}
});
} catch (e) {
// Catch Error
// throw e;
}
{
"name": "angular-electron",
"version": "8.0.0",
"description": "Angular 10 with Electron (Typescript + SASS + Hot Reload)",
"homepage": "https://github.com/maximegris/angular-electron",
"author": {
"name": "Maxime GRIS",
"email": "maxime.gris@gmail.com"
},
"keywords": [
"angular",
"angular 10",
"electron",
"typescript",
"eslint",
"spectron",
"sass"
],
"main": "main.js",
"private": true,
"scripts": {
"postinstall": "electron-builder install-app-deps",
"ng": "ng",
"start": "npm-run-all -p electron:serve ng:serve",
"build": "npm run electron:serve-tsc && ng build --base-href ./",
"build:dev": "npm run build -- -c dev",
"build:prod": "npm run build -- -c production",
"ng:serve": "ng serve",
"ng:serve:web": "ng serve -c web -o",
"electron:serve-tsc": "tsc -p tsconfig-serve.json",
"electron:serve": "wait-on tcp:4200 && npm run electron:serve-tsc && npx electron . --serve",
"electron:local": "npm run build:prod && npx electron .",
"electron:build": "npm run build:prod && electron-builder build",
"test": "ng test --watch=false",
"test:watch": "ng test",
"e2e": "npm run build:prod && cross-env TS_NODE_PROJECT='e2e/tsconfig.e2e.json' mocha --timeout 300000 --require ts-node/register e2e/**/*.e2e.ts",
"version": "conventional-changelog -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md",
"lint": "ng lint"
},
"devDependencies": {
"@angular-builders/custom-webpack": "9.2.0",
"@angular-devkit/build-angular": "0.1000.2",
"@angular-eslint/builder": "0.0.1-alpha.32",
"@angular-eslint/eslint-plugin": "0.0.1-alpha.32",
"@angular-eslint/eslint-plugin-template": "0.0.1-alpha.32",
"@angular-eslint/template-parser": "0.0.1-alpha.32",
"@angular/cli": "10.0.2",
"@angular/common": "10.0.3",
"@angular/compiler": "10.0.3",
"@angular/compiler-cli": "10.0.3",
"@angular/core": "10.0.3",
"@angular/forms": "10.0.3",
"@angular/language-service": "10.0.3",
"@angular/platform-browser": "10.0.3",
"@angular/platform-browser-dynamic": "10.0.3",
"@angular/router": "10.0.3",
"@ngx-translate/core": "13.0.0",
"@ngx-translate/http-loader": "6.0.0",
"@types/jasmine": "3.5.11",
"@types/jasminewd2": "2.0.8",
"@types/mocha": "7.0.2",
"@types/node": "12.12.6",
"@typescript-eslint/eslint-plugin": "3.6.0",
"@typescript-eslint/eslint-plugin-tslint": "3.6.0",
"@typescript-eslint/parser": "3.6.0",
"chai": "4.2.0",
"conventional-changelog-cli": "2.0.34",
"core-js": "3.6.5",
"cross-env": "7.0.2",
"electron": "9.1.0",
"electron-builder": "22.7.0",
"electron-reload": "1.5.0",
"eslint": "7.4.0",
"eslint-plugin-import": "2.22.0",
"jasmine-core": "3.5.0",
"jasmine-spec-reporter": "5.0.2",
"karma": "5.1.0",
"karma-coverage-istanbul-reporter": "3.0.3",
"karma-electron": "6.3.0",
"karma-jasmine": "3.3.1",
"karma-jasmine-html-reporter": "1.5.4",
"mocha": "7.2.0",
"npm-run-all": "4.1.5",
"rxjs": "6.6.0",
"spectron": "11.1.0",
"ts-node": "8.10.2",
"tslib": "2.0.0",
"typescript": "3.9.6",
"wait-on": "5.0.1",
"webdriver-manager": "12.1.7",
"zone.js": "0.10.3"
},
"engines": {
"node": ">=10.13.0"
}
}
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { PageNotFoundComponent } from './shared/components';
import { HomeRoutingModule } from './home/home-routing.module';
import { DetailRoutingModule } from './detail/detail-routing.module';
const routes: Routes = [
{
path: '',
redirectTo: 'home',
pathMatch: 'full'
},
{
path: '**',
component: PageNotFoundComponent
}
];
@NgModule({
imports: [
RouterModule.forRoot(routes),
HomeRoutingModule,
DetailRoutingModule
],
exports: [RouterModule]
})
export class AppRoutingModule { }
:host {
}
\ No newline at end of file
import { TestBed, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
import { TranslateModule } from '@ngx-translate/core';
import { ElectronService } from './core/services';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [AppComponent],
providers: [ElectronService],
imports: [RouterTestingModule, TranslateModule.forRoot()]
}).compileComponents();
}));
it('should create the app', async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
});
import { Component } from '@angular/core';
import { ElectronService } from './core/services';
import { TranslateService } from '@ngx-translate/core';
import { AppConfig } from '../environments/environment';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
constructor(
private electronService: ElectronService,
private translate: TranslateService
) {
this.translate.setDefaultLang('en');
console.log('AppConfig', AppConfig);
if (electronService.isElectron) {
console.log(process.env);
console.log('Run in electron');
console.log('Electron ipcRenderer', this.electronService.ipcRenderer);
console.log('NodeJS childProcess', this.electronService.childProcess);
} else {
console.log('Run in browser');
}
}
}
import 'reflect-metadata';
import '../polyfills';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule, HttpClient } from '@angular/common/http';
import { CoreModule } from './core/core.module';
import { SharedModule } from './shared/shared.module';
import { AppRoutingModule } from './app-routing.module';
// NG Translate
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { HomeModule } from './home/home.module';
import { DetailModule } from './detail/detail.module';
import { AppComponent } from './app.component';
// AoT requires an exported function for factories
export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
FormsModule,
HttpClientModule,
CoreModule,
SharedModule,
HomeModule,
DetailModule,
AppRoutingModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
})
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
declarations: [],
imports: [
CommonModule
]
})
export class CoreModule { }
import { TestBed } from '@angular/core/testing';
import { ElectronService } from './electron.service';
describe('ElectronService', () => {
beforeEach(() => TestBed.configureTestingModule({}));
it('should be created', () => {
const service: ElectronService = TestBed.get(ElectronService);
expect(service).toBeTruthy();
});
});
import { Injectable } from '@angular/core';
// If you import a module but never use any of the imported values other than as TypeScript types,
// the resulting javascript file will look as if you never imported the module at all.
import { ipcRenderer, webFrame, remote } from 'electron';
import * as childProcess from 'child_process';
import * as fs from 'fs';
@Injectable({
providedIn: 'root'
})
export class ElectronService {
ipcRenderer: typeof ipcRenderer;
webFrame: typeof webFrame;
remote: typeof remote;
childProcess: typeof childProcess;
fs: typeof fs;
get isElectron(): boolean {
return !!(window && window.process && window.process.type);
}
constructor() {
// Conditional imports
if (this.isElectron) {
this.ipcRenderer = window.require('electron').ipcRenderer;
this.webFrame = window.require('electron').webFrame;
this.remote = window.require('electron').remote;
this.childProcess = window.require('child_process');
this.fs = window.require('fs');
}
}
}
export * from './electron/electron.service';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Routes, RouterModule } from '@angular/router';
import { DetailComponent } from './detail.component';
const routes: Routes = [
{
path: 'detail',
component: DetailComponent
}
];
@NgModule({
declarations: [],
imports: [CommonModule, RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class DetailRoutingModule {}
<div class="container">
<h1 class="title">
{{ 'PAGES.DETAIL.TITLE' | translate }}
</h1>
<a routerLink="/">{{ 'PAGES.DETAIL.BACK_TO_HOME' | translate }}</a>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { DetailComponent } from './detail.component';
import { TranslateModule } from '@ngx-translate/core';
import { RouterTestingModule } from '@angular/router/testing';
describe('DetailComponent', () => {
let component: DetailComponent;
let fixture: ComponentFixture<DetailComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [DetailComponent],
imports: [TranslateModule.forRoot(), RouterTestingModule]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(DetailComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should render title in a h1 tag', async(() => {
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain(
'PAGES.DETAIL.TITLE'
);
}));
});
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-detail',
templateUrl: './detail.component.html',
styleUrls: ['./detail.component.scss']
})
export class DetailComponent implements OnInit {
constructor() { }
ngOnInit(): void { }
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { DetailRoutingModule } from './detail-routing.module';
import { DetailComponent } from './detail.component';
import { SharedModule } from '../shared/shared.module';
@NgModule({
declarations: [DetailComponent],
imports: [CommonModule, SharedModule, DetailRoutingModule]
})
export class DetailModule {}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home.component';
const routes: Routes = [
{
path: 'home',
component: HomeComponent
}
];
@NgModule({
declarations: [],
imports: [CommonModule, RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class HomeRoutingModule {}
<div class="container">
<h1 class="title">
<img *ngFor="let lecturer of lecturers" class="avatar" [src]="lecturer.image" [alt]="lecturer.name">
</h1>
</div>
:host {
}
.container {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
/* background: url(../../../assets/background.jpg) no-repeat center fixed; */
background: url(https://scontent.fbkk5-1.fna.fbcdn.net/v/t1.0-9/39454048_1810841615638442_1784990723204972544_n.jpg?_nc_cat=0&oh=73a6f62947ecc335ae3b5ba80bd87317&oe=5BF0CEEA) no-repeat center fixed;
-webkit-background-size: cover;
background-size: cover;
.title {
color: white;
background-color: rgba(200,245,200,0.65);
margin:0;
padding:20px 20px;
border-radius: 5%;
}
.avatar {
vertical-align: middle;
width: 50px;
height: 50px;
border-radius: 50%;
}
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { HomeComponent } from './home.component';
import { TranslateModule } from '@ngx-translate/core';
import { RouterTestingModule } from '@angular/router/testing';
describe('HomeComponent', () => {
let component: HomeComponent;
let fixture: ComponentFixture<HomeComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [HomeComponent],
imports: [TranslateModule.forRoot(), RouterTestingModule]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HomeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should render title in a h1 tag', async(() => {
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain(
'PAGES.HOME.TITLE'
);
}));
});
import { Component, OnInit } from '@angular/core';
interface Lecturer {
name: string;
image: string;
}
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
lecturers: Lecturer[] = [
{ image: 'https://picsum.photos/id/1001/84/84', name: 'Dr.K' },
{ image: 'https://picsum.photos/id/1002/84/84', name: 'Assist.Prof.Chayaporn' },
{ image: 'https://picsum.photos/id/1003/84/84', name: 'Dr.Tossaporn' },
{ image: 'https://picsum.photos/id/1004/84/84', name: 'Dr.Phaichayon' },
{ image: 'https://picsum.photos/id/1005/84/84', name: 'Dr.Woody' },
{ image: 'https://picsum.photos/id/1006/84/84', name: 'Aj.Wasana' },
{ image: 'https://picsum.photos/id/1027/84/84', name: 'Dr.Paul' },
{ image: 'https://picsum.photos/id/1008/84/84', name: 'Dr.Suphawadee' },
{ image: 'https://picsum.photos/id/1009/84/84', name: 'Aj.Wayo' }
];
constructor() { }
ngOnInit() {
}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HomeRoutingModule } from './home-routing.module';
import { HomeComponent } from './home.component';
import { SharedModule } from '../shared/shared.module';
@NgModule({
declarations: [HomeComponent],
imports: [CommonModule, SharedModule, HomeRoutingModule]
})
export class HomeModule {}
export * from './page-not-found/page-not-found.component';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PageNotFoundComponent } from './page-not-found.component';
describe('PageNotFoundComponent', () => {
let component: PageNotFoundComponent;
let fixture: ComponentFixture<PageNotFoundComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [PageNotFoundComponent]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PageNotFoundComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-page-not-found',
templateUrl: './page-not-found.component.html',
styleUrls: ['./page-not-found.component.scss']
})
export class PageNotFoundComponent implements OnInit {
constructor() {}
ngOnInit(): void {}
}
export * from './webview/webview.directive';
import { WebviewDirective } from './webview.directive';
describe('WebviewDirective', () => {
it('should create an instance', () => {
const directive = new WebviewDirective();
expect(directive).toBeTruthy();
});
});
import { Directive } from '@angular/core';
@Directive({
selector: 'webview'
})
export class WebviewDirective {
constructor() { }
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { PageNotFoundComponent } from './components/';
import { WebviewDirective } from './directives/';
import { FormsModule } from '@angular/forms';
@NgModule({
declarations: [PageNotFoundComponent, WebviewDirective],
imports: [CommonModule, TranslateModule, FormsModule],
exports: [TranslateModule, WebviewDirective, FormsModule]
})
export class SharedModule {}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment