En el día de hoy vamos a ver en que consiste Electron js, una librería para crear aplicaciones de escritorio con tecnologías web, y para ello vamos a desarrollar una app muy sencilla para la gestión de notas y recordatorios usando ExpressJS, MongoDB y Angular JS (Stack MEAN).

¿Qué es electron js?

Electron js es un framework desarrollado con la intención de facilitar la vida enormemente al programador. Entre su ventajas, destaca la de poder crear apps de escritorio multiplataforma, es decir, para distintos sistemas operativos, usando tecnologías web. Si manejas bien los lenguajes del desarrollo web como HTML, CSS y JavaScript, con este framework, podrás crear apps sin tener que aprender un lenguaje nuevo. Al utilizar HTML y CSS para representar los datos, es más sencillo crear apps responsive. Una vez entendidas las características de Electron vamos a crear un ejemplo sencillo para ver mejor como funciona.

Cómo empezar a usar Electron con ExpressJS

Antes de instalar Electron js, tenemos que instalar Node y NPM en nuestro equipo. Nos dirigimos a su página, lo descargamos y lo instalamos: https://nodejs.org/en/. NPM lo pudes bajar e instalar desde aquí: https://www.npmjs.com/ Para instalar Electron, tenemos que abrir la terminal para introduccir el siguiente comando:

npm install electron --save-dev

Bien, para manejar las notas y recordatorios de nuestra app, vamos a utilizar ExpressJS junto con MongoDB como base de datos. Para el frontend, vamos a utilizar AngularJS, es decir, vamos a utilizar el stack MEAN. Para el que no lo conozca, el stack MEAN consiste en desarrollar páginas webs, utilizando solo el lenguaje JavaScript, es decir, AngularJS, NodeJS y MongoDB, todos estos frameworks utilizan la sintaxis de JavaScript. Ahora vamos a descargar o clonar el siguiente proyecto:

https://github.com/theallmightyjohnmanning/electron-express

Una vez descargado, ejecutamos el comando npm install para que instale todas las dependencias. Si hasta aquí todo ha ido bien, si ejecutamos el comando npm start nos debería abrir la aplicación.

Desarrollo de la aplicación

Para insertar los datos en la base de datos MongoDB vamos a utilizar Moongose para gestionarlo de una manera más sencilla. Para ello tenemos que añadir una dependencia nueva en el archivo packcage.json

"mongoose": "^4.4.12"

Si ahora, vuelves a hacer npm install, debería descargarse Moongose correctamente. Para conectar nuestra app con la base de datos en MongoDB tenemos que modificar el fichero app.js para dejarlo parecido a esto:

module.exports = () => {
  // Load The FS Module & The Config File
  fs = require('fs');

  // Load The Path Module
  path = require('path');
  mongoose = require('mongoose');      


 mongoose.connect('AQUI METE LA URL DE LA BASE DE DATOS MONGODB');     // connect to mongoDB database on modulus.io


  config = JSON.parse(fs.readFileSync('config.json'));

  // Load Express Module
  express = require('express');
  app 	= express();

  // Load Body Parser Module
  bodyParser = require('body-parser');
  app.use(bodyParser.json());
  app.use(bodyParser.urlencoded({extended: false}));

  // Load Express Handlebars Module & Setup Express View Engine
  expressHandlebars = require('express-handlebars');
  app.set('views', __dirname+'/views/'); // Set The Views Directory
  app.engine('html', expressHandlebars({ // Setup View Engine Middleware

    layoutsDir:__dirname + '/views/layouts',
  	defaultLayout: 'main',
  	extname: '.html',
  	helpers: {
  		section: function(name, options) {

  			if(!this._sections) {
  				this._sections = {};
  			}

  			this._sections[name] = options.fn(this);
  			return null;
  		}
  	}
  }));
  app.set('view engine', 'html');

  // Load Express Session Module
  session = require('express-session');
  app.use(session({ // Setup Session Middleware

  	secret: config.session.secret,
  	saveUninitialized: true,
  	resave: true
  }));

  // Load Connect Flash Module
  flash = require('connect-flash');
  app.use(flash());
  app.use(function(req, res, next) { // Setup Global Flash Message Middleware

  	res.locals.success_msg 	= req.flash('success_msg');
  	res.locals.error_msg 	= req.flash('error_msg');
  	res.locals.error 			= req.flash('error');
  	res.locals.user 			= req.user || null;
  	next();
  });

  // Load The Bcrypt Module
  bcrypt = require('bcryptjs');

  // Setup Globally Included Directories
  app.use(express.static(path.join(__dirname, '/../bower_components/')));
  app.use(express.static(path.join(__dirname, '/../node_modules/')));
  app.use(express.static(path.join(__dirname, '/../controllers/')));
  app.use(express.static(path.join(__dirname, '/../public/')));

  // Load Available Modules For Dependancy Injection Into Models & Routes
  modules = {
  	app: app,
  	bcrypt: bcrypt,
  	bodyParser: bodyParser,
  	config: config,
  	express: express,
  	expressHandlebars: expressHandlebars,
  	flash: flash,
  	fs: fs,
  	path: path,
  	session: session
  };

  // Setup Globally Included Routes
  fs.readdirSync(path.join(__dirname, 'routes')).forEach(function(filename) {

  	if(~filename.indexOf('.js'))
  		require(path.join(__dirname, 'routes/'+filename))(modules);
  });

  // Start The HTTP Server
  app.listen(config.server.port, config.server.host);
}

Ahora vamos a crear nuestro modelo para las notas y recordatorios. Este modelo lo puedes personalizar con los parámetros que necesites, en mi caso he creado un archivo dentro de la carpeta app/models/ llamado todo.js

var mongoose = require('mongoose');

module.exports = mongoose.model('Todo', {
    text : String,
    done: { type: Boolean, default: false },
    created: { type: Date, default: Date.now },
    type: { type: String, default:"Class" },
                     
 });

Ahora vamos a modificar el archivo pages.js que está situado dentro de la carpeta app/routes. En este archivo es donde gestionaremos las llamadas a la API Rest que estamos creando.


 = require('../models/todo');
module.exports = function() {

	app.get('/api/todos', function(req, res) {

		// use mongoose to get all todos in the database
		Todo.find(function(err, todos) {

			// if there is an error retrieving, send the error. nothing after res.send(err) will execute
			if (err)
				res.send(err)

			res.json(todos); // return all todos in JSON format
		});
	});

	app.post('/api/todos', function(req, res) {

		// create a todo, information comes from AJAX request from Angular
		Todo.create({
			text : req.body.text,
			done : false
		}, function(err, todo) {
			if (err)
				res.send(err);

			// get and return all the todos after you create another
			Todo.find(function(err, todos) {
				if (err)
					res.send(err)
				res.json(todos);
			});
		});

	});


	app.delete('/api/todos/:todo_id', function(req, res) {
		Todo.remove({
			_id : req.params.todo_id
		}, function(err, todo) {
			if (err)
				res.send(err);

			// get and return all the todos after you create another
			Todo.find(function(err, todos) {
				if (err)
					res.send(err)
				res.json(todos);
			});
		});
	});

	app.patch('/api/todos/:todo_id', function(req, res) {
		
		Todo.findById(req.params.todo_id, function (err, todo) {  
    // Handle any possible database errors
    if (err) {
        res.status(500).send(err);
    } else {
        // Update each attribute with any possible attribute that may have been submitted in the body of the request
        // If that attribute isn't in the request body, default back to whatever it was before.
        
		
		todo.done = !todo.done;
        
        // Save the updated document back to the database
        todo.save(function (err, todo) {
            if (err) {
                res.status(500).send(err)
            }


			Todo.find(function(err, todos) {
				if (err)
					res.send(err)
					
				res.json(todos);
			});
			
           
        });

		
    }
});


	});


app.get('*', function(req, res) {
		res.sendfile('./public/index.html'); // load the single view file (angular will handle the page changes on the front-end)
	});

}

Básicamente con esta API podemos gestionar las llamadas de GET, DELETE, y POST para crear, modificar y eliminar notas.

Cómo usar AngularJS para crear una web de notas

Ahora vamos a gestionar la parte del frontend, es decir, la representación de los datos. En el archivo core.js creamos las siguientes funciones:

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

function mainController($scope, $http) {
    $scope.formData = {};

    // when landing on the page, get all todos and show them
    $http.get('/api/todos')
        .success(function (data) {

            angular.forEach(data, function (value, key) {

                value.created = moment(value.created).fromNow();




            });

            $scope.todos = data;
            console.log(data);

        })
        .error(function (data) {
            console.log('Error: ' + data);
        });

    // when submitting the add form, send the text to the node API
    $scope.createTodo = function () {
        $http.post('/api/todos', $scope.formData)
            .success(function (data) {
                $scope.formData = {}; // clear the form so our user is ready to enter another
                angular.forEach(data, function (value, key) {


                    value.created = moment(value.created).fromNow();




                });

                $scope.todos = data;
                console.log(data);
            })
            .error(function (data) {
                console.log('Error: ' + data);
            });
    };

    // delete a todo after checking it
    $scope.deleteTodo = function (id) {
        $http.delete('/api/todos/' + id)
            .success(function (data) {

                  angular.forEach(data, function (value, key) {

                value.created = moment(value.created).fromNow();




            });


                $scope.todos = data;
                console.log(data);
            })
            .error(function (data) {
                console.log('Error: ' + data);
            });
    };



    $scope.doneTodo = function (id) {
        $http.patch('/api/todos/' + id)
            
            .success(function (data) {

                  angular.forEach(data, function (value, key) {

                value.created = moment(value.created).fromNow();




            });


                $scope.todos = data;
                console.log(data);
            })
            .error(function (data) {
                console.log('Error: ' + data);
            });
    };

}

Lo que hago es crear 4 funciones. La primera funcion, lo que hace es llamar a la API para coger todas las notas. La segunda función se encarga de coger el texto que metemos con el teclado en el input de la app, y crea la nota, al crearla, utilizando la librería de moment.js lo que hago es añadir la fecha actual en la nota que acabo de crear. Posteriormente vuelvo a listar todas las notas. La tercera función se encarga de eliminar una determinada nota. La última función sirve para marcar una nota o tarea como completada.

Ahora vamos a crear el archivo HTML, para ello modificamos el HTML de la carpeta public:

<!-- index.html -->
<!doctype html>
<!-- ASSIGN OUR ANGULAR MODULE -->
<html ng-app="scotchTodo">

<head>
    <!-- META -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- Optimize mobile viewport -->
    <title>Task Box</title>
    <!-- SCROLLS -->
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
    <!-- load bootstrap -->
    <link rel="stylesheet" href="css/index.css">
    <!-- load bootstrap -->
    <!-- SPELLS -->
    <script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
    <!-- load jquery -->
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.32/angular.min.js"></script>
    <!-- load angular -->
    <script src="core.js"></script>
    <script src="js/moment.js"></script>
</head>
<!-- SET THE CONTROLLER AND GET ALL TODOS -->

<body ng-controller="mainController">
    <div class="container">
        <br>
        <!-- HEADER AND TODO COUNT -->
        <h2 class="green">TO-DO <span class="label label-info">{{ todos.length }}</span></h2>
        <hr class="green_hr">
        <!-- TODO LIST -->
        <div id="todo-list">
            <!-- LOOP OVER THE TODOS IN $scope.todos -->
            <h4>
                <div ng-if="!todo.done" class="checkbox row" ng-repeat="todo in todos">
                    <div class="col-xs-4"><label for="checkbox1">
                      <div> {{ todo.text }} </div> 
                      
                    </label>
                    </div>
                    <div class="col-xs-8">
                        <div class="left-column">
                            <small class="date" id="date">{{ todo.created }}</small>
                            <buttom id="checkbox1" class="btn btn_delete" ng-click="doneTodo(todo._id)"> <span class="glyphicon glyphicon-ok green delete" aria-hidden="true"></span></buttom>
                            <buttom id="checkbox1" class="btn btn_delete" ng-click="deleteTodo(todo._id)"> <span class="glyphicon glyphicon-remove red delete" aria-hidden="true"></span></buttom>
                        </div>
                    </div>
                </div>
                <hr class="green_hr">
                <div ng-if="todo.done" class="checkbox row" ng-repeat="todo in todos">
                    <div class="col-xs-4"><label for="checkbox1">
                      
                      <div  class="done"> {{ todo.text }} </div> 
                    </label>
                    </div>
                    <div class="col-xs-8">
                        <div class="left-column">
                            <small class="date" id="date">{{ todo.created }} {{ todo.type }}</small>
                            <buttom id="checkbox1" class="btn btn_delete" ng-click="doneTodo(todo._id)"> <span class="glyphicon glyphicon-ok grey delete" aria-hidden="true"></span></buttom>
                            <buttom id="checkbox1" class="btn btn_delete" ng-click="deleteTodo(todo._id)"> <span class="glyphicon glyphicon-remove red delete" aria-hidden="true"></span></buttom>
                        </div>
                    </div>
                </div>
            </h4>
        </div>
    </div>
    <!-- FORM TO CREATE TODOS -->
    <div id="todo-form" class="row">
        <form>
            <div class="input-group">
                <input type="text" class="form-control input-lg text-center input1" placeholder="TO-DO task" ng-model="formData.text">
                <span class="input-group-btn">
                        <button type="submit" class="btn btn-lg button1" ng-click="createTodo()"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span></button>
                        
                </span>
            </div>
            <!-- /input-group -->
        </form>
        <script src="js/index.js"></script>
</body>

</html>

Lo que hago en primer lugar, es mostrar una lista con las notas y tareas sin realizar. Para ello con Angular hago un bucle recorriendo todas las notas, si la nota no esta completada crea un div con todos sus parámetros. A la derecha de cada nota añado un botón para poder marcarla como completada con ng-click=“doneTodo(todo._id) y otro botón para eliminarla completamente de la base de datos ng-click=“deleteTodo(todo._id) Debajo, añado una lista con las tareas que ya hayan sido completadas, el mecanismo es el mismo, simplemente que si la nota ha sido completada se muestra. Por último, en la parte inferior un input para añadir el texto de la nota y el botón para añadir. También podemos añadir CSS a nuestra app para darle estilos, en mi caso me he decantado por los siguiente estilos:

body{
        background-color: #1E1A25;
        color: white;



}

.green{

    color: #39FAB4;
}

.label-info{
    background-color: #39FAB4;
    border-radius: 50%;
    color:#1E1A25; 
    
}

.green_hr{
    border-top: 1px solid #39FAB4;
}

#todo-form{

    position: fixed;
    width: 90%;
    bottom: 0;
    left: 50%;
    margin-left: -43.5%;

}

.button1{
    background-color: transparent;
    color: #39FAB4;
    border: none;
}

.button1:hover{
    background-color: #39FAB4;
    color: #1E1A25;
}

.input1{
    background: transparent;
    border-color: #39FAB4;
    color: white;
}


.red{
    color: #f45c42;

}

.grey{
    color: #999;
}

.left-column{
    margin-top: -6px;
}

.row{
    margin-bottom: 30px;
}

.done{
    color: #999;
}



Si todo ha ido bien, al ejecutar la app con npm start debería aparecer algo parecido a esto:

tutorial electron español notas y recordatorios

Y hasta aquí el artículo de hoy, te animo a que sigas investigando acerca de Electron ya que es una tecnología con mucho potencial, la cual puedes utilizar para hacer apps muy chulas en poco tiempo y de una manera muy sencilla.




También te puede intersar...