Criando aplicativo com Electron – Gerando executável

Olá, há alguns dias vimos aqui no Clube dos Geeks como desenvolver um Aplicativo Desktop com NWjs, AngularJS, Bootstrap e Sqlite. Hoje ao invés de NWjs usaremos Electron, que é uma ferramenta semelhante desenvolvida pela Atom.

Se você só precisa saber como gerar o executável Clique aqui!

Também já desenvolvemos um pequeno aplicativo com Electron aqui no Clube dos Geeks, um Mecanismo de busca personalizada do Google com Electron.

Imagino que se você veio a este tutorial não está interessado em saber a história do Eletron, ou quem desenvolveu, blá, blá. Se estiver veja aqui. Vamos ao que interessa:

Downloads

Para iniciar faça download das tecnologias necessárias:

Estrutura de pastas e arquivos

Os arquivos e pastas de nossa aplicação serão organizadas da seguinte maneira:

Sem título-2.fw

Banco de dados

Abra o SQLiteStudio, crie um banco de dados salvando o arquivo na pasta model da sua aplicação com o nome database.db.

Conectado ao banco de dados crie a seguinte tabela:

CREATE TABLE pessoas (
    id         INTEGER    PRIMARY KEY AUTOINCREMENT,
    nome       CHAR (100),
    email      CHAR (100),
    nascimento DATE,
    endereco   CHAR (200),
    ativo      INT        DEFAULT (1) 
);

Usaremos o campo ativo para informar se a pessoa está ativa ou não, se não está ativa está excluída.

A aplicação

package.json

Para iniciar, criaremos o arquivo package.json da seguinte maneira:

{
  "name": "electron-angularjs-sqlite",
  "version": "1.0.0",
  "description": "Um aplicativo simples usando AngularJS, Sqlite, Electron e Bootstrap",
  "main": "main.js",
  "scripts": {
    "start": "electron main.js",
    "win32": "electron-packager . myApp --platform=win32 --arch=ia32",
    "win64": "electron-packager . myApp --platform=win32 --arch=x64",
    "linux32": "electron-packager . myApp --platform=linux --arch=ia32",
    "linux64": "electron-packager . myApp --platform=linux --arch=x64",
    "osx": "electron-packager . myApp --platform=darwin --arch=x64",
    "build": "electron-packager . --all"
  },
  "devDependencies": {
    "electron-prebuilt": "^0.36.0",
    "electron-packager":"^6.0.0",
    "sqlite-sync":"^0.2.1",
    "jquery":"^2.2.2",
    "angular": "^1.5.1",
    "angular-route": "^1.5.1",
    "angular-utils-pagination":"^0.11.0"
  }
}

Note que no campo script estão os comandos para gerar o executável do nosso programa em diversas plataformas e arquiteturas. Mais a frente falaremos mais um pouco sobre isso.

Instalando dependências

Para instalar as dependências abra seu terminal navegue até a pasta da sua aplicação usando o comando cd /pasta/da/sua/aplicacao, (exemplo c:/app) e execute o comando npm install. 

A instalação pode demorar um pouco dependendo de sua conexão com a internet.

main.js

O arquivo main.js é o arquivo principal de uma aplicação electron, pois ele quem cria a aplicação no sistema e controla seu ciclo de vida. Veja a estrutura do arquivo:

var electron = require('electron');
var app = require('app');
var BrowserWindow = require('browser-window');

// referência global para manter a instância da janela até que sejam fechadas pelo usuário então ele irá ser fechado quando o JavaScript fizer Garbage collection
var mainWindow = null;

// Sair da aplicação quando todas as janelas forem fechadas
app.on('window-all-closed', function() {
  if (process.platform != 'darwin') {
    app.quit();
  }
});

app.on('ready', function() {
  // Cria a janela do browser.
  mainWindow = new BrowserWindow({width: 1200, height: 700});

  // Carrega o arquivo html principal.
  mainWindow.loadURL('file://' + __dirname + '/index.html');

  // aber o DevTools. (console, inspecionar elemento, etc)
  // mainWindow.webContents.openDevTools(); 

  // Evento emitido quando a janela é fechada, usado para destruir instancia.
  mainWindow.on('closed', function() {
    mainWindow = null;
  });
});

Note que a linha 23 está comentada, mas se você quiser pode tirar o comentário. O comando irá mostrar o devTools com console, inspecionar elemento, etc.

index.html

Este arquivo será o principal da nossa aplicação, e deve ter o seguinte conteúdo:

<html ng-app="cdg">
<head>
	<title>Clube dos Geeks</title>
	<link rel="stylesheet" type="text/css" href="bootstrap/css/bootstrap.css">

	<!-- jQuery -->
	<script>window.$ = window.jQuery = require('jquery');</script>

	<!-- Bootstrap -->
	<script src="bootstrap/js/bootstrap.js"></script>

	<!-- Angular -->
	<script src="node_modules/angular/angular.js"></script>
	<!-- Angular - Pagination -->
	<script src="node_modules/angular-utils-pagination/dirPagination.js"></script>

	<!-- App -->
	<script src="app.js"></script>

	<!-- Controllers -->
	<script src="controllers/pessoaController.js"></script>

	<!-- Services -->
	<script src="services/dbService.js"></script>
</head>
<body>
	<!-- Menu -->
	<nav class="navbar navbar-default">
		<div class="container-fluid">
			<div class="navbar-header">
				<a class="navbar-brand" href="#/">
					Clube dos Geeks
				</a>
			</div>
		</div>
	</nav>
	<!-- Onde será carregada a view -->
	<div ng-view="" class="container"></div>
	<!-- Rodapé -->
	<nav class="navbar navbar-default navbar-fixed-bottom">
	  	<div class="container">
	  		<ul class="nav navbar-nav">
	  			<li>
	  				<a href="//clubedosgeeks.com.br">Clube dos Geeks</a>
	  			</li>
	  			<li>
	  				<a href="//jayralencar.com.br">Jayr Alencar</a>
	  			</li>
	  		</ul>
	   	</div>
	</nav>
</body>
</html>

Observe que na linha 1, usamos o atributo ng-app, que serve para instanciar a aplicação, para que o angular possa trabalhar.

app.js

Criaremos agora o arquivo responsável por iniciar e direcionar as rotas do angular, veja:

var app = angular.module('cdg', [require('angular-route'),'angularUtils.directives.dirPagination']);

app.config(function($routeProvider){
	$routeProvider.when("/pessoas", {
		templateUrl : "views/pessoa.html",
		controller : "pessoaController",
        access: { requiredLogin: false }
	});
});

services/dbService.js

Este arquivo será responsável pela conexão com o banco de dados, busca e consolidação das informações.

Como a biblioteca para SQLite – desenvolvida por nós – já tem funções de inserção e edição que facilita as querys, este service irá retornar apenas a instância da biblioteca. Veja:

"USE STRICT";
app.factory("dbService", function($http){
	var sqlite = require('sqlite-sync');
	var db = sqlite.connect('model/database.db');
	return db;
});

controllers/pessoaControllers.js

Este arquivo, como o nome sugere, faz o controle da view, trata as informações, busca e salva no banco de dados.

Como você provavelmente vai querer criar outros CRUD’s, deve tomar esse controller como base para os outros, assim como a view, que falaremos mais abaixo.

Veja o arquivo:

"USE STRICT";
app.controller("pessoaController", function($scope, $location, dbService){
	//Listando
	$scope.listaPessoas = function(){
		dbService.runAsync("SELECT * FROM pessoas WHERE ativo = 1", function(data){
			$scope.pessoas = data;
		});
	}

	//Salvando
	$scope.salvar = function(){
		if($scope.pessoa.id){
			//Editar
			var id = $scope.pessoa.id;
			delete $scope.pessoa.id;
			delete $scope.pessoa.$$hashKey; //Apaga elemento $$hashKey do objeto
			dbService.update('pessoas', $scope.pessoa, {id: id}); //entidade, dados, where
		}else{
			//nova
			dbService.insert('pessoas', $scope.pessoa); // entidade, dados
		}
		$scope.pessoa = {};
		$scope.listaPessoas();
		$('#modalPessoa').modal('hide');
	}

	//Abrindo para editar
	$scope.editar = function(dados){
		$scope.pessoa = dados;
		$('#modalPessoa').modal('show');
	}

	//Excluindo
	$scope.excluir = function(dados){
		if(confirm("Deseja realmente apagar o cadastro de "+dados.nome+"?")){
			dbService.update('pessoas', {ativo:0}, {id: dados.id});
			$scope.listaPessoas();
		}
	}
});

views/pessoa.html

Agora, na pasta views criaremos nosso arquivo de visão, onde os dados serão mostrados.

Aqui nos usamos a diretiva dirPagination para fazer e a paginação dos dados na tabela. Veja:

<div class="row">
	<div class="col-md-8">
		<button class="btn btn-primary" data-toggle="modal" data-target="#modalPessoa"><span class="glyphicon glyphicon-plus"></span> Nova Pessoa</button>
	</div>
	<div class="col-md-4">
		<input class="form-control" placeholder="Pesquisar" ng-model="pesquisar">
	</div>
</div>
<hr>
<div class="row">
	<div class="col-md-12">
		<table class="table table-striped" ng-init="listaPessoas()">
			<thead>
				<th>#</th>
				<th>Nome</th>
				<th>E-mail</th>
				<th>Endereço</th>
				<th>Nascimento</th>
				<th></th>
			</thead>
			<tbody>
				<!-- Listagem -->
				<tr dir-paginate="pessoa in pessoas|filter:pesquisar|itemsPerPage:8">
					<td>{{pessoa.id}}</td>
					<td>{{pessoa.nome}}</td>
					<td>{{pessoa.email}}</td>
					<td>{{pessoa.endereco}}</td>
					<td>{{pessoa.nascimento}}</td>
					<td>
						<button class="btn btn-info btn-xs" ng-click="editar(pessoa)"><span class="glyphicon glyphicon-pencil"></span> Editar</button>
						<button class="btn btn-danger btn-xs" ng-click="excluir(pessoa)"><span class="glyphicon glyphicon-trash"></span> Excluir</button>
					</td>
				</tr>
			</tbody>
		</table>
	</div>
</div>
<!-- Paginação -->
<div class="row">
	<div class="col-md-12 text-center">
		<dir-pagination-controls>
	    </dir-pagination-controls>
	</div>
</div>

<!-- Modal Cadastro e Edição -->
<div class="modal fade" id="modalPessoa" tabindex="-1" role="dialog" >
	<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">Pessoa</h4>
			</div>
			<div class="modal-body">
				<div class="row">
					<div class="col-md-6">
						<label>Nome</label>
						<input class="form-control" type="text" ng-model="pessoa.nome">
					</div>
					<div class="col-md-6">
						<label>E-mail</label>
						<input class="form-control" type="text" ng-model="pessoa.email">
					</div>
				</div>
				<div class="row">
					<div class="col-md-6">
						<label>Data de Nascimento</label>
						<input class="form-control" type="text" ng-model="pessoa.nascimento">
					</div>
					<div class="col-md-6">
						<label>Endereço</label>
						<input class="form-control" type="text" ng-model="pessoa.endereco">
					</div>
				</div>
			</div>
			<div class="modal-footer">
				<button type="button" class="btn btn-default" ng-click="pessoa = {}" data-dismiss="modal">Cancelar</button>
				<button type="button" class="btn btn-primary" ng-click="salvar()">Salvar</button>
			</div>
		</div>
	</div>
</div>

Executando

Se você voltar ao arquivo package.json, verá que entre os scripts está o start, que é o que usaremos para executar nossa aplicação.Então volte ao terminal e execute:

npm start

Gerando executável

Também nos scripts do arquivo package.json estão os comandos para gerar os executáveis. Para isso usamos o módulo electron-packager. Veja como empacotar:

Windows

Para gerar o executável para Windows com arquitetura ia32, execute:

npm run win32

*Neste caso deve se considerar a estrutura do nosso arquivo package.json

ou

electron-packager . myApp --platform=win32 --arch=ia32

Neste caso altere myApp para o nome de sua aplicação.

x64

npm run win64

*Neste caso deve se considerar a estrutura do nosso arquivo package.json

ou

electron-packager . myApp --platform=win32 --arch=x64

Linux

Para empacotar o seu programa para ser executado em sistemas operacionais Linux execute um dos os comandos a seguir no seu terminal usando linux32 ou ia32 para arquitetura 32 bits e linux64 ou x64 para arquitetura 64 bits.

npm run linux64

*Neste caso deve se considerar a estrutura do nosso arquivo package.json

ou

electron-packager . myApp --platform=linux --arch=x64

Mac OS

Para empacotar sua aplicação para rodar em OSX execute um dos comandos a seguir no seu terminal:

npm run osx

*Neste caso deve se considerar a estrutura do nosso arquivo package.json

ou

electron-packager . myApp --platform=darwin --arch=x64

GitHub

Este exemplo também está disponível no GitHub, veja aqui!

Prévia

Veja como nossa aplicação ficou!

Jayr Alencar

Doutorando em Ciências da Computação no Centro de Informática da Universidade Federal do Pernambuco (CIn - UFPE); Mestre pela mesma instituição; Formado em Análise e Desenvolvimento de Sistemas; Católico; Fã de O Senhor do Anéis.

Você pode gostar...

26 Resultados

  1. Dani disse:

    Ao executar o aplicativo não carrega nada ? , fiz varias alterações no packager.json e nada, cara vc executou esse sisteminha que vc fez.

  2. Daniel Carvalho disse:

    Boa Tarde. Primeiramente meus parabéns não só pelo artigo mas sim por sua dedicação com o site Clube dos geeks. Já fiz e refiz e infelizmente não consigo concluir. Instalei todos pacotes e mesmo assim da erro. Segue abaixo:

    Error: Bootstrap’s JavaScript requires jQuery bootstrap.js:8:9
    ReferenceError: require is not defined[Learn More] app.js:1:34
    TypeError: app is undefined[Learn More] pessoaController.js:2:1
    TypeError: app is undefined[Learn More]

  3. Eduardo disse:

    Matéria curta, direta. Sem blá, blá, blá!
    Me ajudou a iniciar meu projeo.

  4. Pablo disse:

    Hi Jayr, I was testing this example. It works perfectly with “npm start” but when I run it once packaged as executable, it gives an error in the console: no such file or directory, open: ‘model/database.db’

    The path to the db in dbService.js : var db = sqlite.connect(‘model/database.db’);
    Has to be modified to something else?

    Thanks in advance!

    Pablo

  5. Ricardo disse:

    Olá, eu criei uma aplicação em electron elétron e gerei o executável instalei ele no PC mais quando eu tento alterar ou inserir algo no banco de dados não funfa, e quando eu gero a aplicação como vc fez no exemplo acima ele grava no banco legal….

    Alguém pode ajudar?
    To usando o nsis pra Windows daí e gero o build

  6. Jessé Dorta disse:

    quando poe a opção –asar para proteger o conteúdo, ele compacta a pasta, mas o executável quando termina de ampacotar não acha os conteúdos depois:

    Failed to load resource: net::ERR_FILE_NOT_FOUND
    Failed to load resource: net::ERR_FILE_NOT_FOUND
    Uncaught Error: Cannot find module ‘jquery’
    Uncaught Error: Bootstrap’s JavaScript requires jQuery
    Uncaught ReferenceError: angular is not defined
    Uncaught TypeError: Cannot read property ‘controller’ of undefined
    Uncaught TypeError: Cannot read property ‘controller’ of undefined
    Uncaught TypeError: Cannot read property ‘factory’ of undefined

  7. David disse:

    Excelente tutorial, está me ajudando bastante, porém eu fiquei com uma dúvida.

    Como eu faço para apagar um valor no banco de dados?

    No exemplo do tutorial apenas o status da pessoa é atualizado para 0.

  8. David disse:

    Para apagar do banco de dados fiz o seguinte:

    dbService.update(‘clients’, {ativo:0}, {id: dados.id});
    $scope.listaClients();
    dbService.runAsync(“DELETE FROM clients WHERE ativo = 0”);

  9. Thiago Dias disse:

    Alguém saberia me dizer o que é necessário na máquina cliente que for rodar o executável do programa feito em electron?
    Algum programa especifico?
    O NodeJs?

  10. Humberto Junior disse:

    Jayr bom dia, quero lhe agradecer em compartilhar seu conhecimento conosco, porque quem esta aprendendo, quando tem um exemplo prático, o aprendizado e muito mais rápido, mais estou apanhando em apagar os dados no banco, poderia me dar uma dica como alterar este código para deletar do banco?

    //Excluindo
    $scope.excluir = function(dados){
    if(confirm(“Deseja realmente apagar o cadastro de “+dados.nome+”?”)){
    dbService.update(‘pessoas’, {ativo:0}, {id: dados.id});
    $scope.listaPessoas();
    }
    }

    Na verdade este trecho altera o “ativo para zero(0)” e assim o registro sai da tabela, mais assim o dado ainda fica no banco

  11. Júlia Fortes disse:

    Ola muito bom o seu post.
    Porem gostaria de pedir a sua ajuda.
    Bom, comecei a estudar o node a pouco tempo(por conta propria) sem auxilio da escola. E ainda tenho muitas duvidas….
    Mas o que eu queria saber é como se faz o controlo de acesso devutilizadores num app da electron.

    • Jayr Alencar disse:

      Oi Júlia, você pode criar uma tabela para usuários no banco de dados, com os campos id, nome, senha e criar uma página de login na primeira tela do seu aplicativo. Quando o usuário preencher o formulário e enviar, o seu programa verifica se o nome de usuário e senha estão no banco de dados, se sim, dá acesso ao usuário.

  12. Geoffrey disse:

    Olá!
    Eu consegui seguir o seu tutorial e todos estão funcionando perfeitamente bem. Eu tenho algumas outras perguntas que me ajudarão a estender este aplicativo. Eu tenho campo de quantidade na tabela, como posso obter o total / soma de todos? Segundo, como posso fazer operações matemáticas como adição, subtração, multiplicação etc. Por favor, ajude. Obrigado.

  1. 22 de junho de 2016

    […] mostrando como desenvolver aplicativos desktop usando tecnologias WEB, com dois frameworks: NW.js e Electron. Hoje mostraremos um caminho mais rápido e mais simples para […]

Deixe um comentário para Daniel Carvalho Cancelar resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *