[Tutorial] Node.js Webkit Desktop app with AngularJS - A Todo List


In this post, we’ll implement a simple Node.js Desktop application with Node-webkit, Bootstrap and AngularJS :) This is a recipe to help you getting started with Desktop development with Node. To show you the basic about Node.js, NW.js and AngularJS, we’ll create a simple Todo application.

So, let’s start :)

Node.js & AngularJS

I - AngularJS

AngularJS is a JavaScript MVC framework developed by Google that lets you build well structured, easily testable, and maintainable front-end applications.

It’s a powerful JS framework to develop big web apps easily, because it provides a good interface between your HTML(views), logic(models) and configuration files.

AngularJS is one of the most used JS frameworks, and in this post, I’ll show you how to use its features to help you with Desktop app development.

To get a better and complete tutorial about AngularJS, I recommend you to check this Toptal post: http://www.toptal.com/angular-js/a-step-by-step-guide-to-your-first-angularjs-app

A Step-by-Step Tutorial for Your First AngularJS App

In this tutorial, AngularJS will help us with the application views, routing and logic.

II - NW.js - Meet the Node Webkit

NW.js is an app runtime based on Chromium and Node.js that allows you to create native apps in HTML and JavaScript!

It also lets you call Node.js modules directly from the DOM and enables a new way of writing native applications with all Web technologies.

Using NW.js brings a lot of advantages:

  • Apps written in modern HTML5, CSS3, JS and WebGL.
  • Complete support for Node.js APIs and all its third party modules.
  • Good performance: Node and WebKit run in the same thread: Function calls are made straightforward; objects are in the same heap and can just reference each other;
  • Easy to package and distribute apps.
  • Available on Linux, Mac OS X and Windows

For more information, visit the NW.js web site

About the Tutorial

You can get this tutorial source code at github:

Get updates Follow @aron-bordin
Star it: Star
Contribute: Fork
Download: Download

And this is the Running demo:

Desktop development with Node.js and AngularJS

III - Hey Yeoman! Give me some directions!

To create a new project, it’s a good idea to use Yeoman. Yeoman helps you to create new projects, providing best practices and tools to help you stay productive. It’s a project generator, so you basically needs to select a project template and Yeoman prepares everything for you.

You can check for project generators here: http://yeoman.io/generators/

IV - Creating the project

First, if you don’t have Yeoman installed, just run the command to install it globally:

  npm install -g yo

Also, in this tutorial you’ll need Bower and Grunt. So, run:

  npm install -g grunt bower

Here we’ll be using the Node-webkit generator https://github.com/Dica-Developer/generator-node-webkit

  npm install -g generator-node-webkit

Now, go to your project directory(or just create a new one) and run the following command to download the template:

  yo node-webkit

Type basic information about your project(name, description, etc).

We’ll use TODC Bootstrap with Bootstrap to design the UI. Add them as a dependency::

bower install bootstrap --save
bower install todc-bootstrap --save
bower install angular --save
bower install angular-route --save
bower install jquery --save
npm install lowdb --save

Install the project dependencies with:

npm install
bower install

V - Build and Run

(You can read more about node-webkit-generator here)

On a Linux use:

$ grunt dist-linux # for 64bit
$ grunt dist-linux32 # for 32bit

on Windows:

$ grunt dist-win

On Mac:

$ grunt dist-mac # for 64bit
$ grunt dist-mac32 # for 32bit

You can see the build output in the dist folder.

After building, you can run with:

cd dist/<YourPlatform>
./node-webkit app.nw/

If everything is working, you should see your first NW.js app running:

NodeJS Hello Todo

Your application settings goes on app/package.json. Edit the

field to use the index.html.

"name": "Todo List",
"main": "index.html",
"version": "0.0.1",
"single-instance": true,
"window": {
  "title": "Todo List",
  "width": 650,
  "height": 650,
  "min_width": 650,
  "min_height": 650,
  "toolbar": false
"chromium-args": "--child-clean-exit"

In this post, to ignore jshint errors, open the Gruntfile.js and find


and inside the array, remove


VI - Application UI

Let’s start the development ;) Now you’ll do everything in the app folder.

First, a simple overview of the Application. To show you the basic about AngularJS routes, this app will have two views:

  • Todo List
    • List Todos
    • View
    • Delete
  • Todo Editor
    • Used to create a Task

So, first start with the


<!doctype html>
<html lang="en" ng-app="todoApp">
<script src="public/libs/angular/angular.min.js"></script>
<script src="public/libs/angular-route/angular-route.min.js"></script>
<script src="public/libs/jquery/dist/jquery.min.js"></script>
<script src="public/libs/bootstrap/dist/js/bootstrap.min.js"></script>

<link href="public/libs/todc-bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="public/libs/todc-bootstrap/dist/css/todc-bootstrap.min.css" rel="stylesheet">
<link href="public/libs/todc-bootstrap/examples/non-responsive/non-responsive.css" rel="stylesheet">
<link href="css/app.css" rel="stylesheet">

<script src="js/app.js"></script>
<script src="js/controllers.js"></script>

  <div ng-view></div>


create the


var todoApp = angular.module('todoApp', [


function($routeProvider) {
    when('/', {
      templateUrl: 'views/index.html',
      controller: 'IndexCtrl'
    when('/todo', {
      templateUrl: 'views/todo.html',
      controller: 'TodoCtrl'
      redirectTo: '/'

Here we have some important points.

The app.js initialize your application with

var todoApp = angu ...
and then set the application routes in the routeProvider.

Notice that, when your app navigates to

, the
will be loaded.

This file is usually used to configure your app settings.

Now, create the

, this file will contains the app logic.

var todoControllers = angular.module('todoControllers', []);


.controller('IndexCtrl', ['$scope', '$routeParams',
  function($scope, $routeParams){

.controller('TodoCtrl', ['$scope', '$routeParams', '$location',
  function($scope, $routeParams, $location){


This code create the application controller and initialize two controllers,


and just to test, on



You can run the app now, and if everything is working, you’ll see the hello world :)

If it’s working, just update your app/index.html’s body:

  <nav class="navbar navbar-default navbar-fixed-top">
    <div class="container">
      <div class="navbar-header">
        <a class="navbar-brand" href="#/">Todo List</a>
      <div id="navbar">
        <ul class="nav navbar-nav navbar">
          <li><a href="/todo">New Task</a></li>
          <li><a ng-click="exit()" href="#">Exit</a></li>
  <div class="container">
    <div ng-view></div>

This is a simple html template with the Navbar at top, and then we add a Angular view inside


We need to move the html body bellow, then, edit the


body {
  padding-top: 40px;
  padding-bottom: 30px;
.container {
  width: 100%!important;

VII - New Task

Now, let’s implement the New Task logic and view.

First, create the

with the following layout:

<form class="form-horizontal">
  <div class="form-group">
    <label for="task" class="col-sm-2 control-label">Task Name</label>
    <div class="col-sm-10">
      <input type="text" class="form-control" id="task" placeholder="Task Name" ng-model="task.name">
  <div class="form-group">
    <label for="" class="col-sm-2 control-label">Task Description</label>
    <div class="col-sm-10">
      <textarea  class="form-control" id="description" ng-model="task.description"></textarea>
  <div class="form-group">
    <div class="col-sm-offset-2 col-sm-10">
      <div class="checkbox">
          <input type="checkbox" ng-model="task.done"> Done

  <div class="form-group">
    <div class="col-sm-offset-2 col-sm-10">
      <a href="#" class="btn btn-default">Cancel</a>
      <a ng-click="newTask()" class="btn btn-success">Save</a>

This is a simple html form. The ng-click=”newTask()” will call the

function in the controller.

And form fields use ng-model directive to bind data.

Here, to save the tasks, we are using Node.js lowdb, a simple json database.

Add the following code in app.js

var low = require('lowdb');
var db = low('db.json');

And then update your


.controller('TodoCtrl', ['$scope', '$routeParams', '$location',
  function($scope, $routeParams, $location){
    $scope.task = {
      'name': '',
      'description': '',
      'done': false

    $scope.newTask = function(){
      var t = $scope.task;
      var data = {name: t.name, description: t.description, done: t.done};

In this controller, we create an empty task and declare the newTask function, that will push the new task to the json db.

VIII - Listing Tasks

Let’s start with the view/index.html

<table class="table">
  <caption>My tasks</caption>
    <tr ng-repeat="task in tasks">
        <div class="btn-group btn-group-xs" role="group">
          <button type="button" class="btn btn-danger" ng-click="removeTask(task)"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span> Delete</button>
          <button type="button" class="btn btn-info" ng-click="viewTask(task)"><span class="glyphicon glyphicon-eye-open" aria-hidden="true"></span> View</button>

<div class="modal fade" id="modalView" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
        <h4 class="modal-title" id="myModalLabel"></h4>
      <div class="modal-body">
      <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>

First we declare a html table to display our data, with two buttons: “View”, “Delete”. And then we declare a modal to display the task.

And you can update your


.controller('IndexCtrl', ['$scope', '$routeParams',
  function($scope, $routeParams){
    $scope.tasks = db('todo').cloneDeep();
    $scope.my_task = null;

    $scope.removeTask = function(task){
      var query = {name: task.name, description: task.description, done: task.done};
      $scope.tasks = db('todo').cloneDeep();

    $scope.viewTask = function(task){
      $scope.my_task = task;

This code loads the tasks when loading and declare the functions to delete and view tasks.

Check the full demo working:

Desktop development with Node.js and AngularJS

If you prefer, you can now easily add more features to this simple Node.js Webkit Desktop App :)


Aron Bordin

Aron Bordin

Aron Bordin
Computer Science Student and AI researcher. Always coding something fun :)

[Tutorial] Android Material Icon Library

### Welcome!In this post, I'll show you how to use [Material Icon Library](https://github.com/code-mc/material-icon-lib).This is a really...… Continue reading