This will walk you through setting up NW.js and Angular-CLI from scratch.
"Why not just do all this for me and just give me a boilerplate"
Do you want a fish, or do you want to learn how to fish? Plus, boilerplates already exist, you can look around for them.
Editors Note:
Due to the limitations of Dev.to some codeblocks below are not formatted well. Sorry about that, out of my control.
Step 1: Angular CLI
Angular is pretty complex, there's a lot of tooling required to get your standard Angular app up off the ground. Fortunately they offer a tool to generate and scaffold your projects.
npm install -g @angular/cli
-
ng new
- Fill out the options for app name, routing, Sass pre-processing, etc
- It will auto-create a folder with your app name
- The CLI will produce a default project with files and install its dependencies
Step 2: Verify Angular CLI worked
Before we add anything else, let's just make sure everything is working as expected. We'll navigate to the project folder, start the server and look at it in the browser.
cd your-app-name
-
npm start
- This will run a local webserver with a note of what port it is running on. For me it was port
4200
.
- This will run a local webserver with a note of what port it is running on. For me it was port
- Visit the local server in your browser.
- Open the file
src/app/app.component.ts
- Change the
title = 'your-app-name';
totitle = 'Does this update automatically?';
- Save the file and look in the browser, after a second or two, you should see the text on the page change automatically!
Step 3: Adding in NW.js
Now that we know the basic Angular project works as expected, we can start adding NW.js and set this up for development.
- Kill the local webserver you had running
npm install --save-dev nw@sdk concurrently
- Open the
package.json
file - In the
"scripts"
section, change:"start": "ng serve",
- to:
"start": "concurrently \"ng serve --port=8964\" \"nw .\"",
-
On the root of the
package.json
object add these values:
"main": "http://localhost:8964", "node-remote": "http://localhost:8964", "window": { "width": 960, "height": 600, "min_width": 700, "min_height": 500, "icon": "your-app-logo.png" },
For some (assumingly stupid) reason Angular adds a "global" object when it loads. Since Node.js uses a
global
object in a similar way to how browsers use awindow
object, NW.js exposes it for easy access. We need to move the Node global object somewhere else so that Angular can selfishly use that name space.Open the
src/index.html
file-
In the
<head>
section add this in:
<script> window.nw_global = window.global; window.global = undefined; </script>
NW.js stores all of it's API in the
window.nw
object, because Angular has this weird thing for Typescript, we need to inform it of the existence ofwindow.nw
.-
Open the
src/polyfill.ts
and add this:
declare global { interface Window { nw: any; } }
Step 4: Verifying NW.js loads
- Now you can run
npm start
- NW.js should pop up and try to load the localhost address specified in the
"main"
ofpackage.json
. Since Webpack takes a while to spin up the local server, you should see a message that the page couldn't load. Once the server is up you can refresh the page in NW.js to see the app. - The window size will match what was placed in the
"window"
section ofpackage.json
. There are many other settings that can be added here as well. -
Important Note: Normally in NW.js you would just access
nw
directly, but due to the glories of Typescript, you must instead access it withwindow.nw
. Similarly you would normally be able to accessprocess
andrequire
directly (Node.js specific features), but now you will need to get them fromwindow.nw.process
andwindow.nw.require
. But you still have access to them and can alias them with variables however you like. - Let's go back to the file
your-app-name/src/app/app.component.ts
- Change the
title = 'Does this update automatically?';
totitle = String(window.nw.require('fs').readFileSync('./package.json'));
- Save and you should see the app automatically reload and display the contents of the
package.json
file on the page which was accessed using Node's built-in file system modulefs
. You can also access any 3rd-party node_modules this way.
The End
And that's it! You now have a dev environment that runs normally, but inside your desktop app. You can write Angular code and access Node commands right from the DOM!
But what about packaging for distribution
We'll have to save that for another time. For now you can look at the NW.js documentation and maybe try it out on a simpler "Hello World" app first, then apply those ideas. Who knows, maybe you'll be the one to write up those instructions for others.
If you have any other issues with Angular and NW.js, look at this Github Issue:
References:
Top comments (10)
This is the best tutorial I've seen for integrating Angular and NW JS (learning the latter now) and it's helpful to be able to launch them both with a simple "npm start". I also love how you can simply specify the window options in package.json and it just works. Thanks for this tutorial!
The problem with using
"main": "localhost:8964",
is that you can't debug bg code,because there isn't any
(only front end code from "remote" page).
Try to inspect process or global variable in devtools.
Sorry
It works with "node-remote" set.
Thanks for the tutorial. But when I used window.nw, i am getting nw as undefined. Please help.
You can see examples here: github.com/nwutils/nw-angular-cli-...
Either you aren't running your code in NW.js or you are running it from a Node.js context (in which case you could use
nw
directly, orglobal.nw
).Thank you sir for this great tutorial. I'm trying to use my pre-existing angular project (with ngx-admin) in an nw.js app. Following your instruction, I've been able to run the angular local server and nw app just fine for development but the problem is that in nw.js context, the UI cannot get pass the index.html file and got stuck there while this is not the case when running the web in browser.
This is my package.json.
{
"main": "localhost:8964",
"node-remote": "localhost:8964",
"window": {
"width": 960,
"height": 600,
"min_width": 700,
"min_height": 500
},
"name": "ngx-admin",
"version": "7.0.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+github.com/akveo/ngx-admin.git"
},
"bugs": {
"url": "github.com/akveo/ngx-admin/issues"
},
"scripts": {
"ng": "ng",
"conventional-changelog": "conventional-changelog",
"start": "concurrently \"npm run start:web\" \"wait-on localhost:8964 && nw .\"",
"start:web": "ng serve --port=8964",
"build": "ng build",
"build:prod": "npm run build -- --prod --aot",
"test": "ng test",
"test:coverage": "rimraf coverage && npm run test -- --code-coverage",
"lint": "ng lint",
"lint:fix": "ng lint ngx-admin-demo --fix",
"lint:styles": "stylelint ./src/*/.scss",
"lint:ci": "npm run lint && npm run lint:styles",
"pree2e": "webdriver-manager update --standalone false --gecko false",
"e2e": "ng e2e",
"docs": "compodoc -p src/tsconfig.app.json -d docs",
"docs:serve": "compodoc -p src/tsconfig.app.json -d docs -s",
"prepush": "npm run lint:ci",
"release:changelog": "npm run conventional-changelog -- -p angular -i CHANGELOG.md -s",
"postinstall": "ngcc --properties es2015 es5 browser module main --first-only --create-ivy-entry-points --tsconfig \"./src/tsconfig.app.json\""
},
"dependencies": {
"@akveo/ng2-completer": "^9.0.1",
"@angular/animations": "^11.0.9",
"@angular/cdk": "11.0.4",
"@angular/common": "^11.0.9",
"@angular/compiler": "^11.0.9",
"@angular/core": "^11.0.9",
"@angular/forms": "^11.0.9",
"@angular/google-maps": "^11.0.4",
"@angular/material": "^11.0.4",
"@angular/material-moment-adapter": "^11.2.13",
"@angular/platform-browser": "^11.0.9",
"@angular/platform-browser-dynamic": "^11.0.9",
"@angular/router": "^11.0.9",
"@asymmetrik/ngx-leaflet": "3.0.1",
"@nebular/auth": "7.0.0",
"@nebular/eva-icons": "7.0.0",
"@nebular/security": "7.0.0",
"@nebular/theme": "7.0.0",
"@swimlane/ngx-charts": "^14.0.0",
"@swimlane/ngx-datatable": "^20.0.0",
"angular2-chartjs": "0.4.1",
"axiom-ngx-tree": "^1.0.1",
"bootstrap": "4.3.1",
"chart.js": "2.7.1",
"ckeditor": "4.7.3",
"classlist.js": "1.1.20150312",
"core-js": "2.5.1",
"echarts": "^4.0.2",
"eva-icons": "^1.1.3",
"intl": "1.2.5",
"ionicons": "2.0.1",
"js-sha256": "^0.9.0",
"leaflet": "1.2.0",
"moment": "^2.29.1",
"nebular-icons": "1.1.0",
"ng-otp-input": "^1.8.1",
"ng2-ckeditor": "^1.2.9",
"ng2-smart-table": "^1.6.0",
"ngx-echarts": "^4.2.2",
"node-sass": "^4.12.0",
"normalize.css": "6.0.0",
"pace-js": "1.0.2",
"roboto-fontface": "0.8.0",
"rxjs": "6.6.2",
"rxjs-compat": "6.3.0",
"socicon": "3.0.5",
"style-loader": "^1.1.3",
"tinymce": "4.5.7",
"tslib": "^2.0.0",
"typeface-exo": "0.0.22",
"wait-on": "^6.0.1",
"web-animations-js": "^2.3.2",
"zone.js": "~0.10.3"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.1100.7",
"@angular/cli": "^11.0.7",
"@angular/compiler-cli": "^11.0.9",
"@angular/language-service": "11.0.9",
"@compodoc/compodoc": "1.0.1",
"@fortawesome/fontawesome-free": "^5.2.0",
"@types/d3-color": "1.0.5",
"@types/googlemaps": "^3.39.3",
"@types/jasmine": "^3.3.0",
"@types/jasminewd2": "2.0.3",
"@types/leaflet": "1.2.3",
"@types/node": "^12.11.1",
"codelyzer": "^6.0.0",
"concurrently": "^7.0.0",
"conventional-changelog-cli": "1.3.4",
"husky": "0.13.3",
"jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~5.1.1",
"karma-chrome-launcher": "~3.1.0",
"karma-cli": "1.0.1",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0",
"npm-run-all": "4.0.2",
"nw": "^0.18.7",
"protractor": "~7.0.0",
"rimraf": "2.6.1",
"stylelint": "7.13.0",
"ts-node": "3.2.2",
"tslint": "~6.1.0",
"tslint-language-service": "^0.9.9",
"typescript": "4.0.5"
}
}
and how do you use it to work with nw-builder, or something else?
github.com/nwutils/nw-angular-cli-...
All set up!
npm install && npm run build
Hi , how do you get the release when you finished your angular nw.js application ? Thanks for everything !
github.com/nwutils/nw-angular-cli-...