{"id":488,"date":"2017-04-10T10:04:00","date_gmt":"2017-04-10T10:04:00","guid":{"rendered":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/?p=488"},"modified":"2020-06-09T13:46:11","modified_gmt":"2020-06-09T13:46:11","slug":"building-cross-platform-desktop-apps-with-angular-and-electron","status":"publish","type":"post","link":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-and-electron\/","title":{"rendered":"Building Cross Platform Desktop Apps with Angular and Electron"},"content":{"rendered":"\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-1 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"400\" height=\"400\" src=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201704_desktop_apps_angular_electron.png\" alt=\"\" class=\"wp-image-615\" srcset=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201704_desktop_apps_angular_electron.png 400w, https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201704_desktop_apps_angular_electron-150x150.png 150w\" sizes=\"auto, (max-width: 639px) 98vw, (max-width: 1199px) 64vw, 400px\" \/><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\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<\/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<\/div>\n<\/div>\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 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<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:40px\" 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 package.jsonto the Electron command (e.g. electron .\/project\/). In the latter case electron executes the file referenced by the 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<\/h2>\n\n\n\n<p>Because Electron doesn\u2019t constrain the used web technology stack, it\u2019s possible to use Angular to create an Electron Desktop App. To spin up your project, you can use <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/DevWurm\/angular-2-electron-seed\" target=\"_blank\">this project seed<\/a> and all configuration changes, which we will perform during this article are available in <a rel=\"noreferrer noopener\" href=\"https:\/\/gist.github.com\/DevWurm\/5c4507e32b521b76163c4ecba54bef97\" target=\"_blank\">this Gist<\/a>.<\/p>\n\n\n\n<p>For the purpose of this article we will build a Hello World app with Angular and Electron to demonstrate the setup of such a project. You can find the resulting application in <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/DevWurm\/hello-electron\" target=\"_blank\">this GitHub repository<\/a>. 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 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<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\/f661853af4819808d6f326660225a871470f04d1\">f661853<\/a><\/p>\n\n\n\n<p>In this project we will use <a href=\"https:\/\/yarnpkg.com\/en\/\">Yarn<\/a> as an alternative to <a href=\"https:\/\/www.npmjs.com\/\">npm<\/a> for installing and managing our dependencies. If you have not made the transition to Yarn yet you can use <a href=\"https:\/\/yarnpkg.com\/en\/docs\/migrating-from-npm\">this guide<\/a> for the migration. If you do not want to switch that\u2019s no problem either, you just have to use <a href=\"https:\/\/yarnpkg.com\/en\/docs\/migrating-from-npm#toc-cli-commands-comparison\">the equivalent npm commands<\/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<pre class=\"wp-block-code\"><code>yarn global add @angular\/cli<\/code><\/pre>\n\n\n\n<div style=\"height:40px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Although the Angular-CLI already switched to Yarn internally, it still sets up your projects via npm. If you do not want this you can configure the CLI to use Yarn by running<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ng set --global packageManager=yarn<\/code><\/pre>\n\n\n\n<div style=\"height:40px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Then you can run<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ng new hello-electron<\/code><\/pre>\n\n\n\n<div style=\"height:40px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>This creates a new Angular Project named <em>hello-electron<\/em>, which is already preconfigured as a web project. The installation of the project dependencies causes the <code>yarn.lock<\/code> file, <a href=\"https:\/\/yarnpkg.com\/en\/docs\/yarn-lock\">which pins your projects dependencies<\/a>. Commit this file to keep track of your dependencies correctly. 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 decoding=\"async\" src=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201704_desktop_apps_angular_electron_1-600x323.png\" alt=\"Screenshot: Angular-CLI default app\" class=\"wp-image-616\"\/><figcaption><em>Figure 1: Angular-CLI default app<\/em><\/figcaption><\/figure>\n\n\n\n<div style=\"height:40px\" 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\">Ejecting the project<\/h3>\n\n\n\n<p>Commit: <a href=\"https:\/\/github.com\/DevWurm\/hello-electron\/commit\/48da720336427d8c9ed23d4bdcac6c005c8e1e20\">48da720<\/a><\/p>\n\n\n\n<p>Angular-CLI brings a lot of tools for managing, building and supporting our project. The configuration and invocation of these tools is managed internally and automatically by the CLI and is accessed via the corresponding CLI commands. While this is very convenient it limits our influence on the build and tooling, especially if we want to achieve more exotic effects, which are out of the scope of the Angular-CLI. Because we want to make a lot of changes to the projects configuration during this article, we have to \u201ceject\u201d the project from the Angular-CLI. This means that all build and tool configuration files get generated and added to our project. After this some of the CLI commands (like build or serve) no longer work and get substituted by mostly equivalent Yarn \/ NPM scripts.<\/p>\n\n\n\n<p>To eject the Project run<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ng eject<\/code><\/pre>\n\n\n\n<div style=\"height:40px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>and install some new and some missing dependencies via<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    yarn install\n    yarn add --dev webpack html-webpack-plugin extract-text-webpack-plugin postcss-url @ngtools\/webpack<\/code><\/pre>\n\n\n\n<div style=\"height:40px\" 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\/54800baf85f51f2b97d0f9059015918bdd8713be\">54800ba<\/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<pre class=\"wp-block-code\"><code>yarn add --dev electron @types\/electron<\/code><\/pre>\n\n\n\n<div style=\"height:40px\" 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\/a16854d88650803cb4c53ef469661fd4983ffdbd\">a16854d<\/a><\/p>\n\n\n\n<p>The file <code>src\/index.html<\/code> is the entry point for our Angular application. We need to adapt the <code>base<\/code> tag from<\/p>\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:40px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>to<\/p>\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:40px\" 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\/dff6c21869b3e9a0f7787b196be08ddb2b8cb182\">dff6c21<\/a><\/p>\n\n\n\n<p>The file <code>src\/polyfills.ts<\/code> includes some <a rel=\"noreferrer noopener\" href=\"https:\/\/en.wikipedia.org\/wiki\/Polyfill\" target=\"_blank\">polyfills<\/a> into the project. The Polyfill <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/angular\/zone.js\/\" target=\"_blank\">zone.js<\/a>, which is necessary to detect asynchronous code execution <a rel=\"noreferrer noopener\" href=\"http:\/\/blog.thoughtram.io\/angular\/2016\/02\/01\/zones-in-angular-2.html\" target=\"_blank\">to trigger the Angular change detection correctly<\/a> by implementing a concept named <a rel=\"noreferrer noopener\" href=\"http:\/\/blog.thoughtram.io\/angular\/2016\/01\/22\/understanding-zones.html\" target=\"_blank\">\u201cZones\u201d<\/a> doesn\u2019t work by default with Node.js, which is used for some parts of the Code execution in Electron. To make the change detection work with Node.js, a different version of the zone.js polyfill called <code>zone-node<\/code> exists. To enable the correct change detection for Node.js as well for ordinary Angular Code we have to include the <code>zone-mix<\/code> polyfill. To do so, we have to change the line<\/p>\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:40px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>to<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    &#91;...]\n    import 'zone.js\/dist\/zone-mix';\n    &#91;...]<\/code><\/pre>\n\n\n\n<div style=\"height:40px\" 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:40px\" 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\/ae3cc50c4f18d217825ab65ec9ade9038c81017f\">ae3cc50<\/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.)<\/p>\n\n\n\n<p>To set up Webpack for our build open the <code>webpack.config.js<\/code> file and modify the returned Object in the following way:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    &#91;...]\n    module.exports = {\n      \/\/ inserted line\n      \"target\": \"electron-renderer\",\n      &#91;...]\n      \/\/ modified entry\n      \"node\": {\n        \"fs\": false,\n        \"global\": false,\n        \"crypto\": false,\n        \"tls\": false,\n        \"net\": false,\n        \"process\": false,\n        \"module\": false,\n        \"clearImmediate\": false,\n        \"setImmediate\": false,\n        \"Buffer\": false,\n        \"__filename\": false,\n        \"__dirname\": false\n      }\n    };<\/code><\/pre>\n\n\n\n<div style=\"height:40px\" 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 and to leave all references to Node core modules untouched. 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>yarn add --dev copy-webpack-plugin<\/code><\/p>\n\n\n\n<p>Now we can use it by including it in our <code>webpack.config.js<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    const CopyWebpackPlugin = require('copy-webpack-plugin');\n    &#91;...]\n    module.exports = {\n      &#91;...]\n      plugins: &#91;\n        &#91;...]\n        \/\/ inserted lines\n        new CopyWebpackPlugin(&#91;{\n          context: path.resolve(__dirname, \"src\"),\n          from: \"entry.js\"\n        }]),\n        &#91;...]\n      ],\n      &#91;...]\n    }<\/code><\/pre>\n\n\n\n<div style=\"height:40px\" 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\">Fixing the TypeScript configuration<\/h3>\n\n\n\n<p>Commit: <a href=\"https:\/\/github.com\/DevWurm\/hello-electron\/commit\/a775a795dcdc076e74a0f30f826b58b234e61fd7\">a775a79<\/a><\/p>\n\n\n\n<p>Because of a bug in the TypeScript loader used by the build the type root isn\u2019t resolved correctly by default. To fix this set the type roots explicitly in the TypeScript configuration files <code>src\/tsconfig.app.json<\/code>, <code>src\/tsconfig.app.json<\/code> and <code>e2e\/tsconfig.e2e.json<\/code> and add the entry <code>\"node\"<\/code> to the <code>types<\/code> array:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    {\n      \"compilerOptions\": {\n        &#91;...]\n        \"typeRoots\": &#91;\n            \"..\/node_modules\/@types\"\n        ],\n        types: &#91;\n          \"node\",\n          &#91;...]\n        ],\n        &#91;...]\n      },\n      &#91;...]\n    }<\/code><\/pre>\n\n\n\n<div style=\"height:40px\" 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\/9c4c3e400410bbe1d2996777b02b6388454f3f33\">9c4c3e4<\/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.<br>To do so, install the <a href=\"https:\/\/github.com\/twolfson\/karma-electron\" target=\"_blank\" rel=\"noreferrer noopener\">Electron launcher<\/a> for the <a href=\"https:\/\/karma-runner.github.io\/1.0\/index.html\" target=\"_blank\" rel=\"noreferrer noopener\">Karma test runner<\/a> via<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>yarn add --dev karma-electron<\/code><\/pre>\n\n\n\n<div style=\"height:40px\" 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:<\/p>\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        client: {\n          \/\/ inserted line\n          useIframe: false,\n          &#91;...]\n        },\n        &#91;...]\n        \/\/ modified line\n        browsers: &#91;'Electron'],\n        &#91;...]\n      });\n    };<\/code><\/pre>\n\n\n\n<div style=\"height:40px\" 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>With these changes we have set up the provided unit testing for our Angular-Electron app and can run the test via <code>yarn 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\/cf0451a7a31e33ee4ae1c91bae9a91d7e5fb973a\">cf0451a<\/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<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:40px\" 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\">Adapting the package configuration<\/h3>\n\n\n\n<p>Commit:&nbsp;<a href=\"https:\/\/github.com\/DevWurm\/hello-electron\/commit\/e426eddf205ba3b3e8921dbbe4ab53756660af21\">e426edd<\/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<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:40px\" 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>To run our app, we add the <code>run<\/code> script, which invokes Electron in our package directory:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    \"run\": \"electron .\",<\/code><\/pre>\n\n\n\n<div style=\"height:40px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>So running <code>yarn 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<pre class=\"wp-block-code\"><code>yarn add --dev concurrently<\/code><\/pre>\n\n\n\n<div style=\"height:40px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Then we modify the following script in our <code>package.json<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    \"start\": \"concurrently \\\"webpack-dev-server --port=4200\\\" \\\"electron src\/entry.live.js\\\"\",<\/code><\/pre>\n\n\n\n<div style=\"height:40px\" 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\/c5ccedf4f19227a814c580e4750eadd757ab0f67\">c5ccedf<\/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<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:40px\" 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<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:40px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>to<\/p>\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:40px\" 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<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:40px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>to<\/p>\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:40px\" 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\">Adding Devtron<\/h3>\n\n\n\n<p>Commit: <a href=\"https:\/\/github.com\/DevWurm\/hello-electron\/commit\/0832c1b306952c1c1c7d88a04629a7459ee72be1\">0832c1b<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/electron.atom.io\/devtron\/\">Devtron<\/a> is an extension for the Electron DevTools, which helps you developing your app by giving you more information about Electron internals, like the Inter-Process-Communication (IPC) mechanism or the established event listeners. Just install it to the project via<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>yarn add --dev devtron<\/code><\/pre>\n\n\n\n<div style=\"height:40px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>and install it to the DevTools right after opening them, when the app is started in development mode, by modifying the following section in <code>entry.js<\/code> and <code>entry.live.js<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    &#91;...]\n    function createWindow () {\n      &#91;...]\n      \/\/ Open the DevTools when in dev mode.\n      if(process.env.NODE_ENV=='development') {\n        win.webContents.openDevTools()\n        require('devtron').install()\n      }\n      &#91;...]\n    }\n    &#91;...]<\/code><\/pre>\n\n\n\n<div style=\"height:40px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>When launching your app with <code>NODE_ENV=development<\/code> set, you will now find a Devtron tab in the DevTools, which shows you the intended information.<\/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\/d0b7f3036e4e76b0e3200995a11638319cb37569\">d0b7f30<\/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 href=\"https:\/\/github.com\/electron-userland\/electron-packager\" target=\"_blank\" rel=\"noreferrer noopener\">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.<br>So let us install the tool<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>yarn add --dev electron-packager<\/code><\/pre>\n\n\n\n<div style=\"height:40px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>and include it into our workflow by adding the following scripts to our <code>package.json<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    \"package:linux\": \"electron-packager . $npm_package_name-$npm_package_version --ignore=src --ignore=node_modules --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=node_modules --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=node_modules --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=node_modules --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:40px\" 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). Because Webpack already bundles our dependencies into the JavaScript bundles inside the <code>dist<\/code> folder, we don\u2019t even need to include the dependencies in <code>node_modules<\/code>. 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. You may also add the <code>packages<\/code> folder to your <code>.gitignore<\/code> file.<\/p>\n\n\n\n<p>Now we have a completely configured project environment, for building an Angular-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\/888a4d2af3a4d56b39d94cfc707ebc0c7d99bb36\">888a4d2<\/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<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:40px\" 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<pre class=\"wp-block-code\"><code>yarn run build &amp;&amp; yarn run run<\/code><\/pre>\n\n\n\n<div style=\"height:40px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image size-medium\"><img decoding=\"async\" src=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2020\/06\/201704_desktop_apps_angular_electron_2-600x316.png\" alt=\"Screenshot &quot;Hello Electron app&quot;\" class=\"wp-image-617\"\/><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 and the Angular-CLI.<\/p>\n\n\n\n<p>In the <a href=\"https:\/\/sogehtsoftware.de\/2017\/01\/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 blog post we introduce the process of building a hybrid Desktop App with Angular using the Angular-CLI and Electron.<\/p>\n","protected":false},"author":68,"featured_media":619,"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":[261,263,271,300],"topics":[],"class_list":["post-488","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-web","tag-angular","tag-electron","tag-cross-platform","tag-cross-platform-desktop-apps"],"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 blog post we introduce the process of building a hybrid Desktop App with Angular 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-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 blog post we introduce the process of building a hybrid Desktop App with Angular 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-and-electron\/\" \/>\n<meta property=\"og:site_name\" content=\"Digital Innovation Blog\" \/>\n<meta property=\"article:published_time\" content=\"2017-04-10T10:04:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2020-06-09T13:46:11+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2017\/04\/201704_desktop_apps_angular_electron_fi.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1281\" \/>\n\t<meta property=\"og:image:height\" content=\"734\" \/>\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-and-electron\/\",\"url\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-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-and-electron\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-and-electron\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2017\/04\/201704_desktop_apps_angular_electron_fi.png\",\"datePublished\":\"2017-04-10T10:04:00+00:00\",\"dateModified\":\"2020-06-09T13:46:11+00:00\",\"author\":{\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/#\/schema\/person\/c356f48797f27ea4dae06817bc32afae\"},\"description\":\"In this blog post we introduce the process of building a hybrid Desktop App with Angular using the Angular-CLI and Electron.\",\"breadcrumb\":{\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-and-electron\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-and-electron\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-and-electron\/#primaryimage\",\"url\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2017\/04\/201704_desktop_apps_angular_electron_fi.png\",\"contentUrl\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2017\/04\/201704_desktop_apps_angular_electron_fi.png\",\"width\":1281,\"height\":734},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-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 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 blog post we introduce the process of building a hybrid Desktop App with Angular 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-and-electron\/","og_locale":"en_US","og_type":"article","og_title":"Building Cross Platform Desktop Apps ... - ZEISS Digital Innovation Blog","og_description":"In this blog post we introduce the process of building a hybrid Desktop App with Angular using the Angular-CLI and Electron.","og_url":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-and-electron\/","og_site_name":"Digital Innovation Blog","article_published_time":"2017-04-10T10:04:00+00:00","article_modified_time":"2020-06-09T13:46:11+00:00","og_image":[{"width":1281,"height":734,"url":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2017\/04\/201704_desktop_apps_angular_electron_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-and-electron\/","url":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-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-and-electron\/#primaryimage"},"image":{"@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-and-electron\/#primaryimage"},"thumbnailUrl":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2017\/04\/201704_desktop_apps_angular_electron_fi.png","datePublished":"2017-04-10T10:04:00+00:00","dateModified":"2020-06-09T13:46:11+00:00","author":{"@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/#\/schema\/person\/c356f48797f27ea4dae06817bc32afae"},"description":"In this blog post we introduce the process of building a hybrid Desktop App with Angular using the Angular-CLI and Electron.","breadcrumb":{"@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-and-electron\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-and-electron\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-and-electron\/#primaryimage","url":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2017\/04\/201704_desktop_apps_angular_electron_fi.png","contentUrl":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2017\/04\/201704_desktop_apps_angular_electron_fi.png","width":1281,"height":734},{"@type":"BreadcrumbList","@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/building-cross-platform-desktop-apps-with-angular-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 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\/2017\/04\/201704_desktop_apps_angular_electron_fi-600x344.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<\/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<\/a>","<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/category\/web\/\" class=\"advgb-post-tax-term\">Cross Platform Desktop Apps<\/a>"],"unlinked":["<span class=\"advgb-post-tax-term\">Angular<\/span>","<span class=\"advgb-post-tax-term\">Electron<\/span>","<span class=\"advgb-post-tax-term\">Cross platform<\/span>","<span class=\"advgb-post-tax-term\">Cross Platform Desktop Apps<\/span>"]}},"comment_count":"0","relative_dates":{"created":"Posted 9 years ago","modified":"Updated 6 years ago"},"absolute_dates":{"created":"Posted on April 10, 2017","modified":"Updated on June 9, 2020"},"absolute_dates_time":{"created":"Posted on April 10, 2017 10:04 am","modified":"Updated on June 9, 2020 1:46 pm"},"featured_img_caption":"","series_order":"","_links":{"self":[{"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/posts\/488","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=488"}],"version-history":[{"count":6,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/posts\/488\/revisions"}],"predecessor-version":[{"id":618,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/posts\/488\/revisions\/618"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/media\/619"}],"wp:attachment":[{"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/media?parent=488"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/categories?post=488"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/tags?post=488"},{"taxonomy":"topics","embeddable":true,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/topics?post=488"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}