{"id":532,"date":"2016-12-14T14:42:25","date_gmt":"2016-12-14T14:42:25","guid":{"rendered":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/?p=532"},"modified":"2020-09-10T14:12:08","modified_gmt":"2020-09-10T14:12:08","slug":"building-cross-platform-desktop-apps-with-angular-2-and-electron","status":"publish","type":"post","link":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-2-and-electron\/","title":{"rendered":"Building Cross Platform Desktop Apps with Angular 2 and Electron"},"content":{"rendered":"\n<p>The rapid development in the world of web technologies, especially in SPA frameworks like <a rel=\"noreferrer noopener\" href=\"https:\/\/angular.io\/\" target=\"_blank\">Angular 2<\/a> and <a rel=\"noreferrer noopener\" href=\"https:\/\/facebook.github.io\/react\/\" target=\"_blank\">React<\/a>, empowers developers to build enterprise scale, platform independent applications with a flexible and vivid technology stack. Although these applications can fulfill numerous use cases they may fall short in others because of the limitations web applications have. Especially the limited environment integration and system access capabilities make some kinds of applications impossible (e.g. missing file system access) or hurt the user experience (e.g. no offline usage, no integration into application menu or task bar in most cases).<\/p>\n\n\n\n<p>The <a href=\"http:\/\/electron.atom.io\/\" target=\"_blank\" rel=\"noreferrer noopener\">Electron Framework<\/a> by <a href=\"https:\/\/github.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">GitHub<\/a> attempts to tackle those problems while preserving the advantages of web technologies by wrapping conventional web apps as native applications and giving them powerful access to the underlying system and their environment.<\/p>\n\n\n\n<p>In this article we will go over the process of building a hybrid Desktop App with Angular 2 using the Angular-CLI and Electron.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">About Electron<\/h2>\n\n\n\n<p>Electron is an open source framework developed by GitHub for the <a rel=\"noreferrer noopener\" href=\"https:\/\/atom.io\/\" target=\"_blank\">Atom project<\/a>. It integrates web technology based applications into the system, to make them work like native applications. To do so, it uses the rendering component of the <a rel=\"noreferrer noopener\" href=\"https:\/\/www.chromium.org\/\" target=\"_blank\">Chromium browser<\/a> to render the web views, while executing the JavaScript elements of the app in the <a rel=\"noreferrer noopener\" href=\"https:\/\/nodejs.org\/\" target=\"_blank\">Node.js<\/a> runtime environment to provide access to the underlying system. In addition Electron brings additional APIs to integrate the application into the environment (e.g. with keyboard listeners, notifications, tray integration, etc.) it runs in.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Creating an Electron application<\/h3>\n\n\n\n<p>To run a web app in Electron, you need to provide an entry point script to the electron binary (which is available as binary package or as NPM module). You can find an example of an entry point script <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/electron\/electron-quick-start\/blob\/master\/main.js\" target=\"_blank\">at the Electron quick start repo<\/a>. The entry point (which is run in the <a rel=\"noreferrer noopener\" href=\"http:\/\/electron.atom.io\/docs\/tutorial\/quick-start\/#main-process\" target=\"_blank\"><strong>Main Process<\/strong><\/a>) can register global handlers for the app (e.g. keyboard shortcuts, behavior on closing, etc.) and creates one or multiple windows for the app via<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>    const {app, BrowserWindow} = require('electron')\n    const path = require('path')\n    const url = require('url')\n    \/\/ Create the browser window.\n    win = new BrowserWindow({width: 800, height: 600})\n    \/\/ and load the index.html of the app.\n    win.loadURL(url.format({\n      pathname: path.join(__dirname, 'index.html'),\n      protocol: 'file:',\n      slashes: true\n    }))<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Each of the windows runs the specified web page (<code>index.html<\/code> in this example) and the associated JavaScript in a <a href=\"http:\/\/electron.atom.io\/docs\/tutorial\/quick-start\/#renderer-process\" target=\"_blank\" rel=\"noreferrer noopener\"><strong>Render Process<\/strong><\/a>. All the JavaScript in this Render Process has access to the full <a href=\"https:\/\/nodejs.org\/api\/\" target=\"_blank\" rel=\"noreferrer noopener\">Node.js API<\/a> and all available <a href=\"https:\/\/www.npmjs.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">NPM packages<\/a>, to the DOM, which is visually represented by the Chromium renderer of this process, as well as to parts of the <a href=\"http:\/\/electron.atom.io\/docs\/api\/\" target=\"_blank\" rel=\"noreferrer noopener\">Electron API<\/a>.<\/p>\n\n\n\n<p>You can pass an entry point script to Electron either via the command line arguments of the Electron binary (e.g. <code>electron .\/index.js<\/code>) or you pass a directory containing an&nbsp;package.jsonto the Electron command (e.g. electron .\/project\/). In the latter case electron executes the file referenced by the&nbsp;mainfield of the package.json.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Building desktop apps with Angular 2<\/h2>\n\n\n\n<p>Because Electron doesn\u2019t constrain the used web technology stack, it\u2019s possible to use Angular 2 to create an Electron Desktop App. To spin up your project, you can use <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/DevWurm\/angular-electron-seed\/tree\/version1\" target=\"_blank\">this project seed<\/a> and all configuration changes, which we will perform during this article are available in this Gist.<\/p>\n\n\n\n<p>For the purpose of this article we will build a Hello World app with Angular 2 and Electron to demonstrate the setup of such a project. You can find the resulting application in this GitHub repository. The commit which corresponds to the upcomming changes will be linked on top of every section.<\/p>\n\n\n\n<p>We will use the <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/angular\/angular-cli\" target=\"_blank\">Angular-CLI<\/a> which is a tool for generating Angular 2 Projects without having to bother with the configuration and selection of all necessary and fast evolving tools and best practices in the Web ecosystem. In addition to the setup off <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/angular\/angular-cli#linting-and-formatting-code\" target=\"_blank\">linting<\/a>, <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/angular\/angular-cli#creating-a-build\" target=\"_blank\">building<\/a>, <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/angular\/angular-cli#bundling\" target=\"_blank\">bundling<\/a> and <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/angular\/angular-cli#running-unit-tests\" target=\"_blank\">testing<\/a> tools it brings some neat little features like <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/angular\/angular-cli#generating-components-directives-pipes-and-services\" target=\"_blank\">code scaffolding<\/a> or a <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/angular\/angular-cli#generating-and-serving-an-angular2-project-via-a-development-server\" target=\"_blank\">development server<\/a>.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p><strong>Note<br><\/strong>Unfortunaletly the following process just works until version <code>1.0.0-beta.19-3<\/code> of the Angular-CLI. When using higher versions, the change detection stops working correctly. There is already an <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/angular\/zone.js\/issues\/533\" target=\"_blank\">issue<\/a> regarding this problem, but no solution yet.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Setting up the project<\/h3>\n\n\n\n<p>Commit: <a href=\"https:\/\/github.com\/DevWurm\/hello-electron\/commit\/b0c23807ab99835d7c3a6a0e604bd651a6e4d204\" target=\"_blank\" rel=\"noreferrer noopener\">b0c2380<\/a><\/p>\n\n\n\n<p>Let\u2019s start by setting up a new mostly configured project with the <code>new<\/code> command of the Angular-CLI. So install the Angular-CLI on your system with<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>npm install -g angular-cli@1.0.0-beta.19-3<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Then run<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>ng new hello-electron<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>This creates a new Angular 2 Project named <em>hello-electron<\/em>, which is already preconfigured as a web project. To test if there were any issues while setting up the base project, you can run the default application. To do so, run <code>ng serve<\/code> and head your browser to the location which is printed in the terminal (normally this is http:\/\/localhost:4200). You should see the Text \u201c<em>App works!<\/em>\u201c<\/p>\n\n\n\n<figure class=\"wp-block-image size-medium\"><img loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"323\" src=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_2-600x323.png\" alt=\"Screenshot Angular-CLI default app\" class=\"wp-image-630\" srcset=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_2-600x323.png 600w, https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_2-1024x551.png 1024w, https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_2-768x413.png 768w, https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_2-1536x827.png 1536w, https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_2-2048x1102.png 2048w, https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_2-640x345.png 640w, https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_2-1200x646.png 1200w, https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_2-1920x1034.png 1920w\" sizes=\"auto, (max-width: 639px) 98vw, (max-width: 1199px) 64vw, 600px\" \/><figcaption><em>Figure 1: Angular-CLI default app<\/em><\/figcaption><\/figure>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Because the Angular-CLI isn\u2019t aware of the possibility of building Electron Desktop Apps (yet) neither the build will work out of the box nor the build results will run in Electron. To achieve this, we need to reconfigure some parts of the setup.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Installing Electron<\/h3>\n\n\n\n<p>Commit: <a href=\"https:\/\/github.com\/DevWurm\/hello-electron\/commit\/2f36d96a3798994d8fe12ef8ef67395ba32d4424\" target=\"_blank\" rel=\"noreferrer noopener\">2f36d96<\/a><\/p>\n\n\n\n<p>To use electron (and the corresponding TypeScript typings), we install it locally into our project for development purposes to set a working version and list it in our dev-dependencies:<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>npm install --save-dev electron @types\/electron<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Adapting entry point web page<\/h3>\n\n\n\n<p>Commit: <a href=\"https:\/\/github.com\/DevWurm\/hello-electron\/commit\/fa4313d5bc420953a2b4ef2bae4fd0a827dfb416\" target=\"_blank\" rel=\"noreferrer noopener\">fa4313d<\/a><\/p>\n\n\n\n<p>The file <code>src\/index.html<\/code> is the entry point for our Angular 2 application. We need to adapt the <code>base<\/code> tag from<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>    &lt;!doctype html>\n    &lt;html>\n      &lt;head>\n        &#91;...]\n        &lt;base href=\"\/\">\n        &#91;...]\n      &lt;\/head>\n      &#91;...]\n    &lt;\/html><\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>to<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>    &lt;!doctype html>\n    &lt;html>\n      &lt;head>\n        &#91;...]\n        &lt;base href=\".\/\">\n        &#91;...]\n      &lt;\/head>\n      &#91;...]\n    &lt;\/html><\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>because Electron doesn\u2019t resolve included files relatively to the current domain (like web browsers do), but relatively to the current file tree, so the <code>base<\/code> <code>\/<\/code> would resolve relatively to the file system root. We want the dependencies to get resolved relatively to the location of <code>index.html<\/code>, so we use <code>.\/<\/code>.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Setting the correct polyfills<\/h3>\n\n\n\n<p>Commit: <a href=\"https:\/\/github.com\/DevWurm\/hello-electron\/commit\/90fb541d990d758467295074fbbf989976b8cc79\" target=\"_blank\" rel=\"noreferrer noopener\">90fb541<\/a><\/p>\n\n\n\n<p>The file <code>src\/polyfills.ts<\/code> includes some <a href=\"https:\/\/en.wikipedia.org\/wiki\/Polyfill\" target=\"_blank\" rel=\"noreferrer noopener\">polyfills<\/a> into the project. The Polyfill <a href=\"https:\/\/github.com\/angular\/zone.js\/\" target=\"_blank\" rel=\"noreferrer noopener\">zone.js<\/a>, which is necessary to detect asynchronous code execution <a href=\"http:\/\/blog.thoughtram.io\/angular\/2016\/02\/01\/zones-in-angular-2.html\" target=\"_blank\" rel=\"noreferrer noopener\">to trigger the Angular 2 change detection correctly<\/a> by implementing a concept named <a href=\"http:\/\/blog.thoughtram.io\/angular\/2016\/01\/22\/understanding-zones.html\" target=\"_blank\" rel=\"noreferrer noopener\">\u201cZones\u201d<\/a> doesn\u2019t work by default with Node.js, which is used for the Code execution in Electron. To make the change detection work with Node.js, we have to use a different version of the zone.js polyfill, which is fortunately already installed with zone.js. To do so, we have to change the line<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>    &#91;...]\n    import 'zone.js\/dist\/zone';\n    &#91;...]<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>to<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>    &#91;...]\n    import 'zone.js\/dist\/zone-node';\n    &#91;...]<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>in <code>src\/polyfills.ts<\/code>.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Adapting the Webpack configuration<\/h3>\n\n\n\n<p>Commit: <a href=\"https:\/\/github.com\/DevWurm\/hello-electron\/commit\/7793bb183bb24af76cb7501b118bd959ff184737\" target=\"_blank\" rel=\"noreferrer noopener\">7793bb1<\/a><\/p>\n\n\n\n<p>The Angular-CLI uses <a href=\"https:\/\/webpack.github.io\/\" target=\"_blank\" rel=\"noreferrer noopener\">Webpack<\/a> to handle the project build, the development server and particularly the module bundling. (Theoretically we don\u2019t need module bundling when using Electron, because Node.js has native support for modules, but because Webpack handles so much different things of our projects infrastructure, it is easier to configure Webpack for Electron and bundle our project, than it is to rebuild all the infrastructure ourselves.) Unfortunately the Angular-CLI hides the Webpack configuration it uses for building the project, so there is no official way to adapt the build in the current version of the CLI (<code>v1.0.0-beta.20-4<\/code>). (There are <a href=\"https:\/\/github.com\/angular\/angular-cli\/issues\/1656#issuecomment-240171375\" target=\"_blank\" rel=\"noreferrer noopener\">plans of the CLI-Team<\/a> to establish a configuration interface for CLI-Project, but there are neither precise ideas nor implementations of this feature yet.) The only possibility I found was to locate the configuration files the CLI uses to build the current project, make a copy of the relevant ones, modify the copy and overwrite the original ones with the modified copies.<br>To do so copy the file <code>node_modules\/angular-cli\/models\/webpack-build-common.js<\/code> into your project root.<\/p>\n\n\n\n<p>After this open the file and modify the returned Object in the following way:<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>    &#91;...]\n    function getWebpackCommonConfig(projectRoot, environment, appConfig, baseHref) {\n        &#91;...]\n        return {\n            \/\/ inserted line\n            target: 'electron-renderer',\n            &#91;...],\n        };\n    }\n    exports.getWebpackCommonConfig = getWebpackCommonConfig;<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>This configures Webpack not to build for a Web environment but for an Electron rendering Process. Basically this allows you to include Node.js core modules as well as using browser and Electron specific APIs (like the DOM) in your application (read more about <a href=\"https:\/\/webpack.github.io\/docs\/configuration.html#target\" target=\"_blank\" rel=\"noreferrer noopener\">different targets in the Webpack documentation<\/a>).<\/p>\n\n\n\n<p>We also want our Electron entry point script (which we will add to the project shortly) to be automatically copied from our project sources to our build result.<br>We can achieve this with Webpack by installing the <a href=\"https:\/\/github.com\/kevlened\/copy-webpack-plugin\" target=\"_blank\" rel=\"noreferrer noopener\"><code>copy-webpack-plugin<\/code><\/a> into our project via<code>npm install --save-dev copy-webpack-plugin<\/code><\/p>\n\n\n\n<p>Now we can use it by including it in our <code>webpack-build-common.js<\/code>:<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>    \"use strict\";\n    var CopyWebpackPlugin = require('copy-webpack-plugin');\n    &#91;...]\n    function getWebpackCommonConfig(projectRoot, environment, appConfig, baseHref) {\n        &#91;...]\n        return {\n            &#91;...]\n            plugins: &#91;\n                &#91;...]\n                \/\/ inserted lines\n                new CopyWebpackPlugin(&#91;{\n                    context: path.resolve(appRoot),\n                    from: \"entry.js\"\n                }]),\n                &#91;...]\n            ],\n            &#91;...]\n        };\n    }\n    exports.getWebpackCommonConfig = getWebpackCommonConfig;<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>This copies the file <code>entry.js<\/code> from our source folder to the root of our build result.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Adapting unit test configuration<\/h3>\n\n\n\n<p>Commit: <a href=\"https:\/\/github.com\/DevWurm\/hello-electron\/commit\/4c97cd43b1f59995ccfa4e31e94f2ac8b2aaa711\" target=\"_blank\" rel=\"noreferrer noopener\">4c97cd4<\/a><\/p>\n\n\n\n<p>Easy and integrated unit testing is another major feature of the Angular-CLI, which does not run out of the box, when using Electron as environment. All tests, which involve features of the Node.js environment fail, because they are not available in the normal Browser, which the preconfigured test runner uses to run the tests in. To run our unit test, we have to configure the test runner, to run our tests in Electron, too.<\/p>\n\n\n\n<p>To do so, install the <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/twolfson\/karma-electron\" target=\"_blank\">Electron launcher<\/a> for the <a rel=\"noreferrer noopener\" href=\"https:\/\/karma-runner.github.io\/1.0\/index.html\" target=\"_blank\">Karma test runner<\/a> via<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>npm install --save-dev karma-electron<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Then open the file <code>karma.conf.js<\/code> and perform the following modifications:<br>Locate the line<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>    &#91;...]\n    module.exports = function (config) {\n      config.set({\n        &#91;...]\n        plugins: &#91;\n          require('karma-jasmine'),\n          \/\/ modified line, previously: require('karma-chrome-launcher')\n          require('karma-electron'),\n          &#91;...]\n        ],\n        &#91;...]\n        \/\/ modified line\n        browsers: &#91;'Electron'],\n        \/\/ inserted lines\n        client: {\n          useIframe: false\n        },\n        &#91;...]\n      });\n    };<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>This configures Karma to use Electron and execute every test in a new Browser (Electron) window, instead of in an <code>iFrame<\/code> in the same window. This is necessary, because <code>iFrames<\/code> in Electron have no access to the Node.js environment, while windows have.<\/p>\n\n\n\n<p>Unfortunately there are some more caveats, we have to consider:<br>The first one is, that the <a href=\"https:\/\/jasmine.github.io\/\" target=\"_blank\" rel=\"noreferrer noopener\">Jasmine (assertion framework)<\/a> version, which is used by the Angular-CLI has a bug, which occurs when including Node modules in your code. This bug is fixed in the version <code>2.5+<\/code> of Jasmine. In addition to this the corresponding typings only work until version <code>2.5.41<\/code> . So run<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>    npm install --save-dev jasmine-core@^2.5 \n    npm install --save-dev --save-exact @types\/jasmine@2.5.41<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>to install a version of Jasmine, which is above version <code>2.5<\/code>. This overwrites the preinstalled version.<br>The second problem is that the Angular-CLI uses a different Webpack configuration for building the tests than for building the regular distributions. So we have to set the <code>target<\/code> property in <code>node_modules\/angular-cli\/models\/webpack-build-test.js<\/code>, too. Therefore copy the file into your project root and set the <code>target<\/code> property of the <code>return<\/code>ed object to <code>'electron-renderer'<\/code>, like in the last section.<\/p>\n\n\n\n<p>With these changes we have set up the provided unit testing for our Angular2-Electron app and can run the test via <code>ng test<\/code> or <code>npm run test<\/code>.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Configure End-To-End testing<\/h3>\n\n\n\n<p>Commit: <a href=\"https:\/\/github.com\/DevWurm\/hello-electron\/commit\/1de3cc868b73a38bd46af64b3ea3fb1016f5dbcf\" target=\"_blank\" rel=\"noreferrer noopener\">1de3cc8<\/a><\/p>\n\n\n\n<p>The Angular-CLI also brings a setup for End-To-End (aka. e2e, aka. UI-Testing) which can easily be configured to use Electron as execution environment instead of Google Chrome because it can provide the same webdriver interface as Chrome. To configure the e2e testing framework (called <a href=\"http:\/\/www.protractortest.org\/#\/\" target=\"_blank\" rel=\"noreferrer noopener\">Protractor<\/a>) open the file <code>protractor.conf.js<\/code> and adapt it the following way:<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>    &#91;...]\n    exports.config = {\n      &#91;...]\n      capabilities: {\n        \/\/ inserted lines\n        chromeOptions: {\n          binary: '.\/node_modules\/electron\/dist\/electron',\n          args: &#91;'--test-type=webdriver']\n        },\n        'browserName': 'chrome'\n      },\n      &#91;...]\n    };<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Now Protractor uses Electron instead of Chrome and all your tests will work, even when they use Node.js features. Be aware, that the End-To-End testing setup of the Angular-CLI requires a running dev server, which we will configure in the next section.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Setting up the package configuration<\/h3>\n\n\n\n<p>Commit: <a href=\"https:\/\/github.com\/DevWurm\/hello-electron\/commit\/11efe51f70995c68c34e50624d2c455dbf2d0234\" target=\"_blank\" rel=\"noreferrer noopener\">11efe51<\/a><\/p>\n\n\n\n<p>To optimize our workflow I highly recommend setting some properties and scripts in our projects <code>package.json<\/code>.<\/p>\n\n\n\n<p>To make Electron behave correctly, when initialized in the projects root directory, set the <code>main<\/code> field of the <code>package.json<\/code>:<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>    {\n      &#91;...]\n      \"main\": \"dist\/entry.js\",\n      &#91;...]\n    }<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p><code>dist\/entry.js<\/code> will be the location of our Electron entrypoint in the build result.<\/p>\n\n\n\n<p>We also want our modified Webpack config to get automatically copied into the correct directory after the install step in our project. Because copying works differently when working on different operating systems, we can use the NPM package <a href=\"https:\/\/www.npmjs.com\/package\/cpx\" target=\"_blank\" rel=\"noreferrer noopener\"><code>cpx<\/code><\/a> to perform the copy step platform independently (if you do not care about developing on different platforms, use the copy command of your OS instead of <code>cpx<\/code> [<code>cp<\/code> on UNIX likes, <code>copy<\/code> on Windows]). Install <code>cpx<\/code><\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>npm install --save-dev cpx<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>and add the following line to the <code>scripts<\/code> block of your <code>package.json<\/code>:<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>    \"postinstall\": \"cpx webpack-build-common.js node_modules\/angular-cli\/models &amp;&amp; cpx webpack-build-test.js node_modules\/angular-cli\/models\",<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>The <code>postinstall<\/code> script (also called the <code>postinstall<\/code> hook) is automatically invoked after running <code>npm install<\/code>. So after running <code>npm install<\/code> your modified Webpack config will be adopted by the Angular-CLI.<\/p>\n\n\n\n<p>Because I am used to build my project with <code>npm run build<\/code>, I map the NPM <code>build<\/code> script to the <code>build<\/code> command of the Angular-CLI, by adding<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>    \"build\": \"ng build\",\n    \"build:prod\": \"ng build --prod\",<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>to the scripts section of my <code>package.json<\/code>.<\/p>\n\n\n\n<p>To run our app, we add the <code>run<\/code> script, which initializes Electron in our package directory:<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>    \"run\": \"electron .\",<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>So running <code>npm run run<\/code> will launch our Electron app.<\/p>\n\n\n\n<p>Now we have a project setup, which will allow us to build, test and run our app, but the Angular-CLI provides another very neat feature called LiveReload. LiveReload spins up a webserver via the Webpack dev server, which serves our app wrapped in some more JavaScript, that reloads our app automatically from the server when it changes, while our sources get recompiled every time they are modified. This makes it possible to develop our app while constantly seeing the current state and without having to compile and run it by hand every time we make a change. Because we use Electron instead of a browser and we want to build desktop apps, we load our app directly from the build result and have no connection to the LiveReload server by default. To use this feature, we have to crate a second, slightly modified Electron entry point for development purposes and load this one to use LiveReload. We now create an additional NPM script, which concurrently starts the dev server and our Electron binary with our \u201clive\u201d entry point (which we will create later). To do so, we use the tool <a href=\"https:\/\/www.npmjs.com\/package\/concurrently\" target=\"_blank\" rel=\"noreferrer noopener\"><code>concurrently<\/code><\/a>, which we install with<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>npm install --save-dev concurrently<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Then we add the following NPM script to our <code>package.json<\/code>:<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>    \"start\": \"concurrently \\\"ng serve\\\" \\\"electron src\/entry.live.js\\\"\",<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Creating the entry points<\/h3>\n\n\n\n<p>Commit: <a href=\"https:\/\/github.com\/DevWurm\/hello-electron\/commit\/213cae614c1b8896a3787345e3d296d3677a7796\" target=\"_blank\" rel=\"noreferrer noopener\">213cae6<\/a><\/p>\n\n\n\n<p>To load our App into Electron, we need to create an entry point at <code>src\/entry.js<\/code>, whose main purpose is to load the entry point HTML document (which is generated in the build process). We use <a href=\"https:\/\/gist.github.com\/DevWurm\/5c4507e32b521b76163c4ecba54bef97#file-entry-js\" target=\"_blank\" rel=\"noreferrer noopener\">this<\/a> slightly modified version of the example entry point from the Electron documentation. Basically the entry point loads the file <code>index.html<\/code> in the same directory as the entry point itself into a new window by calling<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>    win.loadURL(url.format({\n      pathname: path.join(__dirname, 'index.html'),\n      protocol: 'file:',\n      slashes: true\n    }))<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>It also handles general application lifecycle events like application start or exit and registers environment integration like keyboard bindings or tray icons.<br>A small modification opens the developer tools, when the app is started with the environment variable <code>NODE_ENV=development<\/code> set.<\/p>\n\n\n\n<p>To make use of LiveReloading, we also need the alternative entry point <code>src\/entry.live.js<\/code> which you can find <a href=\"https:\/\/gist.github.com\/DevWurm\/5c4507e32b521b76163c4ecba54bef97#file-entry-live-js\" target=\"_blank\" rel=\"noreferrer noopener\">here<\/a>. This entry point is mostly the same as <code>src\/entry.js<\/code>. The only difference is, that the application entry point page gets loaded from the dev server instead of the file system. By default the dev server serves at <code>http:\/\/localhost:4200<\/code>, so we change the lines<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>    &#91;...]\n    function createWindow () {\n      &#91;...]\n      win.loadURL(url.format({\n        pathname: path.join(__dirname, 'index.html'),\n        protocol: 'file:',\n        slashes: true\n      }))\n      &#91;...]\n    }\n    &#91;...]<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>to<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>    &#91;...]\n    function createWindow () {\n      &#91;...]\n      win.loadURL(url.format({\n        pathname: 'localhost:4200',\n        protocol: 'http:',\n        slashes: true\n      }))\n      &#91;...]\n    }\n    &#91;...]<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>We also want to set a short timeout, so we reduce the chance of loading the file from the server, before the server has finished building (you may adapt the time according to your project size and the resulting build time). To do so, we change the <code>createWindow<\/code> function:<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>    &#91;...]\n    function createWindow () {\n      \/\/ content\n      &#91;...]\n    }\n    &#91;...]<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>to<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>    &#91;...]\n    function createWindow () {\n      setTimeout(() => {\n        \/\/ content\n        &#91;...]\n      }, 12000)\n    }\n    &#91;...]<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Because the dev server only serves the static files and handles the changes, it is not relevant, if the code inside the application bundle is written for the browser or for Electron and Electron executes any provided code in the bundle, whether it is generated by your sources or by the dev server. So the page gets loaded and reloads itself, when the dev server recognizes changes.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Application bundling<\/h3>\n\n\n\n<p>Commit: <a href=\"https:\/\/github.com\/DevWurm\/hello-electron\/commit\/c432f59f5f3c012910e84e046170db0707fb6c4d\" target=\"_blank\" rel=\"noreferrer noopener\">c432f59<\/a><\/p>\n\n\n\n<p>After building our application inside our development environment, we want to distribute our app as platform specific packages. Package distribution in the Electron world usually works by packing all the necessary application resources together with the Electron binary for the specific target platform. The binary is renamed to the name of your application, so when running the resulting executable, Electron is started, locates your entry point (because it is noted in the <code>package.json<\/code>) and loads your application.<br>Conveniently there is a tool called <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/electron-userland\/electron-packager\" target=\"_blank\">electron-packager<\/a>, which automates the process of packaging your application for different platforms and architectures and carries the responsibility for creating all the platform specific packaging formats, configuration files, icon formats, etc.<\/p>\n\n\n\n<p>So let us install the tool<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>npm install --save-dev electron-packager<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>and include it into our workflow by adding the following NPM scripts to our <code>package.json<\/code>:<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>    \"package:linux\": \"electron-packager . $npm_package_name-$npm_package_version --ignore=src --ignore=e2e --ignore=.*\\\\.conf\\\\.js --ignore=\\\"(angular-cli|tsconfig)\\\\.json\\\" --ignore=webpack.*\\\\.js --out=packages --platform=linux --arch=all --overwrite\",\n    \"package:mac\": \"electron-packager . $npm_package_name-$npm_package_version --ignore=src --ignore=e2e --ignore=.*\\\\.conf\\\\.js --ignore=\\\"(angular-cli|tsconfig)\\\\.json\\\" --ignore=webpack.*\\\\.js --out=packages --platform=darwin --arch=all --overwrite \",\n    \"package:win\": \"electron-packager . $npm_package_name-$npm_package_version --ignore=src --ignore=e2e --ignore=.*\\\\.conf\\\\.js --ignore=\\\"(angular-cli|tsconfig)\\\\.json\\\" --ignore=webpack.*\\\\.js --out=packages --platform=win32 --arch=all --overwrite \",\n    \"package:all\": \"electron-packager . $npm_package_name-$npm_package_version --ignore=src --ignore=e2e --ignore=.*\\\\.conf\\\\.js --ignore=\\\"(angular-cli|tsconfig)\\\\.json\\\" --ignore=webpack.*\\\\.js --out=packages --all --overwrite\",<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>These scripts create the application packages for the specified platforms in the directory <code>packages<\/code> and the <code>--ignore<\/code> flags prevents the inclusion of files, which are unnecessary for the package (some directories like your <code>devDependencies<\/code> or <code>.git<\/code> are ignored by default). You can also build your packages on your CI system to automate your application distribution, but it has to be said, that packaging for Windows on non-Windows systems requires Wine, which on the other hand may not deal very well with headless systems out of the box.<\/p>\n\n\n\n<p>Now we have a completely configured project environment, for building an Angular-2-Electron App, with all the features, which are available through the Angular-CLI. We can now go on with creating our little app itself.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Building our app<\/h3>\n\n\n\n<p>Commit: <a href=\"https:\/\/github.com\/DevWurm\/hello-electron\/commit\/891fce1927d54986471ecd57e7f15e6c282d0939\" target=\"_blank\" rel=\"noreferrer noopener\">891fce1<\/a><\/p>\n\n\n\n<p>To make our app say hello we need to set the value of the <code>title<\/code> property in the <code>AppComponent<\/code> class in <code>src\/app\/app.component.ts<\/code> to our desired value, let\u2019s say \u2018Hello Electron!\u2019<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>    &#91;...]\n    export class AppComponent {\n      title = 'Hello Electron!';\n    }<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>To make our tests pass, we need to set this text in the components test <code>src\/app\/app.component.spec.ts<\/code> as well es in the e2e test specification <code>e2e\/app.e2e-spec.ts<\/code>.<\/p>\n\n\n\n<p>After this we can build and run our app with<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"wp-block-code\"><code>npm run build &amp;&amp; npm run run<\/code><\/pre>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image size-medium\"><img loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"316\" src=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_3-600x316.png\" alt=\"Screenshot Hello Electron app\" class=\"wp-image-629\" srcset=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_3-600x316.png 600w, https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_3-1024x539.png 1024w, https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_3-768x404.png 768w, https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_3-1536x809.png 1536w, https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_3-2048x1078.png 2048w, https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_3-640x337.png 640w, https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_3-1200x632.png 1200w, https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_3-1920x1011.png 1920w\" sizes=\"auto, (max-width: 639px) 98vw, (max-width: 1199px) 64vw, 600px\" \/><figcaption><em>Figure 2: Hello Electron app<\/em><\/figcaption><\/figure>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>With this setup you can build powerful desktop applications, while being supported by the rapidly evolving ecosystem of Node.js, Angular 2 and the Angular-CLI.<\/p>\n\n\n\n<p>In the <a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-a-file-explorer-with-angular-2-and-electron\/\">next article<\/a> we will build a simple file explorer with these technologies to gain a deeper insight into the power of Node.js and the Electron API.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this article we introduce the process of building a hybrid Desktop App with Angular 2 using the Angular-CLI and Electron.<\/p>\n","protected":false},"author":68,"featured_media":702,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"advgb_blocks_editor_width":"","advgb_blocks_columns_visual_guide":"","footnotes":""},"categories":[8],"tags":[260,263,300,342],"topics":[],"class_list":["post-532","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-web","tag-angular-2","tag-electron","tag-cross-platform-desktop-apps","tag-electron-framework"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v24.0 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Building Cross Platform Desktop Apps ... - ZEISS Digital Innovation Blog<\/title>\n<meta name=\"description\" content=\"In this article we introduce the process of building a hybrid Desktop App with Angular 2 using the Angular-CLI and Electron.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-2-and-electron\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Building Cross Platform Desktop Apps ... - ZEISS Digital Innovation Blog\" \/>\n<meta property=\"og:description\" content=\"In this article we introduce the process of building a hybrid Desktop App with Angular 2 using the Angular-CLI and Electron.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-2-and-electron\/\" \/>\n<meta property=\"og:site_name\" content=\"Digital Innovation Blog\" \/>\n<meta property=\"article:published_time\" content=\"2016-12-14T14:42:25+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2020-09-10T14:12:08+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_3_fi.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1204\" \/>\n\t<meta property=\"og:image:height\" content=\"677\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Leo Lindhorst\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Leo Lindhorst\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"17 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-2-and-electron\/\",\"url\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-2-and-electron\/\",\"name\":\"Building Cross Platform Desktop Apps ... - ZEISS Digital Innovation Blog\",\"isPartOf\":{\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-2-and-electron\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-2-and-electron\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_3_fi.png\",\"datePublished\":\"2016-12-14T14:42:25+00:00\",\"dateModified\":\"2020-09-10T14:12:08+00:00\",\"author\":{\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/#\/schema\/person\/c356f48797f27ea4dae06817bc32afae\"},\"description\":\"In this article we introduce the process of building a hybrid Desktop App with Angular 2 using the Angular-CLI and Electron.\",\"breadcrumb\":{\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-2-and-electron\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-2-and-electron\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-2-and-electron\/#primaryimage\",\"url\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_3_fi.png\",\"contentUrl\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_3_fi.png\",\"width\":1204,\"height\":677},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-2-and-electron\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Building Cross Platform Desktop Apps with Angular 2 and Electron\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/#website\",\"url\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/\",\"name\":\"Digital Innovation Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/#\/schema\/person\/c356f48797f27ea4dae06817bc32afae\",\"name\":\"Leo Lindhorst\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2024\/06\/Lindhorst_Leo_Profilbild_300x300px-150x150.jpg\",\"contentUrl\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2024\/06\/Lindhorst_Leo_Profilbild_300x300px-150x150.jpg\",\"caption\":\"Leo Lindhorst\"},\"description\":\"Leo Lindhorst is a Management Consultant with ZEISS Digital Innovation. He is working together with his clients in the healthcare, medical technology and pharmaceuticals industries, to define the vision for their digital product landscape, consults them on the best technological and organizational solution for their ambitions and supports them in setting up and leading their software development program.Together with the teams of ZEISS Digital Innovation, Leo is ensuring the delivery of the client\u2019s innovative digital health solutions from design, over implementation to successful operations in the field.\",\"url\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/author\/enleolindhorst\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Building Cross Platform Desktop Apps ... - ZEISS Digital Innovation Blog","description":"In this article we introduce the process of building a hybrid Desktop App with Angular 2 using the Angular-CLI and Electron.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-2-and-electron\/","og_locale":"en_US","og_type":"article","og_title":"Building Cross Platform Desktop Apps ... - ZEISS Digital Innovation Blog","og_description":"In this article we introduce the process of building a hybrid Desktop App with Angular 2 using the Angular-CLI and Electron.","og_url":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-2-and-electron\/","og_site_name":"Digital Innovation Blog","article_published_time":"2016-12-14T14:42:25+00:00","article_modified_time":"2020-09-10T14:12:08+00:00","og_image":[{"width":1204,"height":677,"url":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_3_fi.png","type":"image\/png"}],"author":"Leo Lindhorst","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Leo Lindhorst","Est. reading time":"17 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-2-and-electron\/","url":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-2-and-electron\/","name":"Building Cross Platform Desktop Apps ... - ZEISS Digital Innovation Blog","isPartOf":{"@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-2-and-electron\/#primaryimage"},"image":{"@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-2-and-electron\/#primaryimage"},"thumbnailUrl":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_3_fi.png","datePublished":"2016-12-14T14:42:25+00:00","dateModified":"2020-09-10T14:12:08+00:00","author":{"@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/#\/schema\/person\/c356f48797f27ea4dae06817bc32afae"},"description":"In this article we introduce the process of building a hybrid Desktop App with Angular 2 using the Angular-CLI and Electron.","breadcrumb":{"@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-2-and-electron\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-2-and-electron\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-2-and-electron\/#primaryimage","url":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_3_fi.png","contentUrl":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_3_fi.png","width":1204,"height":677},{"@type":"BreadcrumbList","@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-2-and-electron\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/"},{"@type":"ListItem","position":2,"name":"Building Cross Platform Desktop Apps with Angular 2 and Electron"}]},{"@type":"WebSite","@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/#website","url":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/","name":"Digital Innovation Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/#\/schema\/person\/c356f48797f27ea4dae06817bc32afae","name":"Leo Lindhorst","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/#\/schema\/person\/image\/","url":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2024\/06\/Lindhorst_Leo_Profilbild_300x300px-150x150.jpg","contentUrl":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2024\/06\/Lindhorst_Leo_Profilbild_300x300px-150x150.jpg","caption":"Leo Lindhorst"},"description":"Leo Lindhorst is a Management Consultant with ZEISS Digital Innovation. He is working together with his clients in the healthcare, medical technology and pharmaceuticals industries, to define the vision for their digital product landscape, consults them on the best technological and organizational solution for their ambitions and supports them in setting up and leading their software development program.Together with the teams of ZEISS Digital Innovation, Leo is ensuring the delivery of the client\u2019s innovative digital health solutions from design, over implementation to successful operations in the field.","url":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/author\/enleolindhorst\/"}]}},"author_meta":{"display_name":"Leo Lindhorst","author_link":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/author\/enleolindhorst\/"},"featured_img":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201612_Building_Cross_Platform_Desktop_Apps_3_fi-600x337.png","coauthors":[],"tax_additional":{"categories":{"linked":["<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/category\/web\/\" class=\"advgb-post-tax-term\">Web<\/a>"],"unlinked":["<span class=\"advgb-post-tax-term\">Web<\/span>"]},"tags":{"linked":["<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/category\/web\/\" class=\"advgb-post-tax-term\">Angular 2<\/a>","<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/category\/web\/\" class=\"advgb-post-tax-term\">Electron<\/a>","<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/category\/web\/\" class=\"advgb-post-tax-term\">Cross Platform Desktop Apps<\/a>","<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/category\/web\/\" class=\"advgb-post-tax-term\">Electron Framework<\/a>"],"unlinked":["<span class=\"advgb-post-tax-term\">Angular 2<\/span>","<span class=\"advgb-post-tax-term\">Electron<\/span>","<span class=\"advgb-post-tax-term\">Cross Platform Desktop Apps<\/span>","<span class=\"advgb-post-tax-term\">Electron Framework<\/span>"]}},"comment_count":"0","relative_dates":{"created":"Posted 9 years ago","modified":"Updated 6 years ago"},"absolute_dates":{"created":"Posted on December 14, 2016","modified":"Updated on September 10, 2020"},"absolute_dates_time":{"created":"Posted on December 14, 2016 2:42 pm","modified":"Updated on September 10, 2020 2:12 pm"},"featured_img_caption":"","series_order":"","_links":{"self":[{"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/posts\/532","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/users\/68"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/comments?post=532"}],"version-history":[{"count":10,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/posts\/532\/revisions"}],"predecessor-version":[{"id":1031,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/posts\/532\/revisions\/1031"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/media\/702"}],"wp:attachment":[{"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/media?parent=532"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/categories?post=532"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/tags?post=532"},{"taxonomy":"topics","embeddable":true,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/topics?post=532"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}