Nesse tutorial vamos criar um app utilizando os plugins SQLite, Toast, Local notification e Badge da biblioteca ngCordova.
O código fonte de exemplo está disponível no GitHub.
O que é ngCordova?
O ngCordova é um AngularJS wrapper desenvolvido para utilizar os plugins mais populares do Cordova com o Ionic Framework. Além das funcionalidades que serão apresentadas nesse tutorial, com essa biblioteca é possível tirar uma foto, acessar sua localização, entre outros.
O ngCordova é open source e utiliza a licença MIT, ou seja, você pode utiliza-lo inclusive comercialmente.
O app que será criado
Iniciando o projeto
Vamos começar criando o projeto com o Ionic.
ionic start ToDoApp blank
Para instalar o ngCordova utilizando o bower com a linha de comando abaixo ou você pode baixar os arquivos diretamente do site e incluir no projeto.
$ bower install ngCordova
É necessário incluir o arquivo ng-cordova.js ou ng-cordova.min.js no arquivo index.html antes do arquivo cordova.js.
<script src="lib/ngCordova/dist/ng-cordova.js"></script>
<script src="cordova.js"></script>
Após esse processo é necessário injetar o ngCordova no seu modulo angular.
angular.module('myApp', ['ngCordova'])
Observações importantes:
- Os pluguins do cordova só funcionam no emulador ou no próprio dispositivo, ou seja, não é possível testar diretamente no navegador.
- Antes de utilizar qualquer método de um plugin é importante verificar se o cordova foi totalmente carregado no dispositivo. Para fazer isso basta seguir o exemplo abaixo.
$ionicPlatform.ready(function() {
$cordovaPlugin.someFunction().then(success, error);
});
Instalando os plugins
Utilizando CLI, navegue até a pasta do seu projeto e execute os comandos abaixo para cada plugin.
SQLite
Esse é um plugin para acessar um banco de dados SQLite local.
$ cordova plugin add https://github.com/litehelpers/Cordova-sqlite-storage.git
Toast
Esse plugin exibe mensagens em um simples popup. Excelente para mensagens não intrusivas.
$ cordova plugin add https://github.com/EddyVerbruggen/Toast-PhoneGap-Plugin.git
Local notification
Esse plugin gera notificações para o usuário. Pode ser utilizado para avisar ao usuário que ele tem alguma tarefa a fazer no app.
$ cordova plugin add https://github.com/katzer/cordova-plugin-local-notifications.git
Badge
Esse plugin incrementa o numero que fica ao lado do ícone do aplicativo. Ideal para mostrar, por exemplo, a quantidade de tarefas que o usuário tem pendente no app.
$ cordova plugin add https://github.com/katzer/cordova-plugin-badge.git
Como utilizar o plugin SQLite
Toda a documentação do pluguin você pode encontrar clicando aqui ou aqui.
Os códigos abaixo são exemplos de como utilizar o plugin.
Inserindo:
module.controller('MyCtrl', function($scope, $cordovaSQLite) {
var db = $cordovaSQLite.openDB({ name: "ToDo.db", iosDatabaseLocation: 'Default' });
var query = 'insert into ToDo (title, description, done) values (?,?,?)';
$cordovaSQLite.execute(db, query, ['Titulo', 'Descrição', 0]).then(function(result) {
console.log("insertId: " + result.insertId);
console.log("rowsAffected: " + result.rowsAffected);
}, function (error) {
console.error(error);
});
});
Atualizando:
module.controller('MyCtrl', function($scope, $cordovaSQLite) {
var db = $cordovaSQLite.openDB({ name: "ToDo.db", iosDatabaseLocation: 'Default' });
var query = 'update ToDo set title = ?, description = ?, done = ? where id = ?';
$cordovaSQLite.execute(db, query, ['Titulo', 'Descrição', 0, 1]).then(function(result) {
console.log("insertId: " + result.insertId);
console.log("rowsAffected: " + result.rowsAffected);
}, function (error) {
console.error(error);
});
});
Buscando dados:
module.controller('MyCtrl', function($scope, $cordovaSQLite) {
var db = $cordovaSQLite.openDB({ name: "ToDo.db", iosDatabaseLocation: 'Default' });
var query = 'select * from ToDo';
$cordovaSQLite.execute(db, query, []).then(function(result) {
for (var i = 0; i < result.rows.length; i++) {
var row = result.rows.item(i);
console.log(row);
}
}, function (error) {
console.error(error);
});
});
Deletando:
module.controller('MyCtrl', function($scope, $cordovaSQLite) {
var db = $cordovaSQLite.openDB({ name: "ToDo.db", iosDatabaseLocation: 'Default' });
var query = 'delete ToDo where id = ?';
$cordovaSQLite.execute(db, query, [1]).then(function(result) {
console.log("insertId: " + result.insertId);
console.log("rowsAffected: " + result.rowsAffected);
}, function (error) {
console.error(error);
});
});
Como utilizar o plugin Toast
Toda a documentação do pluguin você pode encontrar clicando aqui ou aqui.
O código abaixo é um exemplo de como utilizar o plugin.
module.controller('MyCtrl', function($cordovaToast) {
$cordovaToast
.show('Sua mensagem aqui.', 'long', 'center')
.then(function(success) {
// success
}, function (error) {
// error
});
});
Como utilizar o plugin Local notification
Toda a documentação do pluguin você pode encontrar clicando aqui ou aqui.
O código abaixo é um exemplo de como utilizar o plugin.
module.controller('MyCtrl', function($cordovaLocalNotification) {
$cordovaLocalNotification.schedule({
id: 1, //id da notificação
title: 'Tarefa salva com sucesso.',
text: 'Você tem uma tarefa para ser executada. Mãos a obra! =]'
});
});
Como utilizar o plugin Badge
Toda a documentação do pluguin você pode encontrar clicando aqui ou aqui.
O código abaixo é um exemplo de como utilizar o plugin.
module.controller('MyCtrl', function($cordovaBadge) {
$cordovaBadge.set(3).then(function() {
//sucess
}, function(error) {
console.error(error);
});
});
Criando os arquivos do app
A primeira coisa a ser feita é criar o modulo angular adicionando as dependências do Ionic e do ngCordova.
var app = angular.module('todo', ['ionic', 'ngCordova'])
.run(function($ionicPlatform, DbFactory) {
$ionicPlatform.ready(function() {
if(window.cordova && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
cordova.plugins.Keyboard.disableScroll(true);
}
if(window.StatusBar) {
StatusBar.styleDefault();
}
});
});
Agora vamos definir toadas as rotas do app.
app.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
cache: false,
url: '/home',
templateUrl: 'templates/list.html',
controller: 'ToDoListController'
})
.state('new', {
cache: false,
url: '/new',
templateUrl: 'templates/edit.html',
controller: 'ToDoEditController'
})
.state('edit', {
cache: false,
url: '/edit/:id',
templateUrl: 'templates/edit.html',
controller: 'ToDoEditController'
});
$urlRouterProvider.otherwise('/home')
});
Vamos criar um factory para encapsular os acessos ao banco de dados.
app.factory('DbFactory', function ($ionicPlatform, $cordovaSQLite, $rootScope) {
return {
/**
* Inicializa a conexação com o banco de dados
*/
initialize: function () {
if (!$rootScope.db) {
//abrindo a conexão com o banco de dados
//caso ele não exista ele será criado
var db = $cordovaSQLite.openDB({ name: "ToDo.db", iosDatabaseLocation: 'Default' });
//salvando o db em uma variavel global para não ter que carregar toda vez
$rootScope.db = db;
//sql para criar a tabela
var createTable = "CREATE TABLE IF NOT EXISTS ToDo ( " +
"id integer primary key autoincrement, " +
"title varchar(50) not null, " +
"description varchar(250) not null," +
"done integer not null)";
//criando a tabela onde serão gravados os registros
$cordovaSQLite.execute(db, createTable).then(function (res) {
console.log("Tabela criada");
}, function (error) {
console.error("Ocorreu algum erro ao criar a tabela. " + error);
});
}
},
/**
* Recupera dados do banco de dados
*/
getData: function (query, whereParam) {
this.initialize();
return $cordovaSQLite.execute($rootScope.db, query, whereParam)
.then(function (result) {
return { success: true, rows: result.rows };
}, function (error) {
return { success: false, message: error };
});
},
/**
* Salva dados no banco de dados
*/
saveData: function (query, args) {
this.initialize();
return $cordovaSQLite.execute($rootScope.db, query, args)
.then(function (result) {
return { success: true, id: result.insertId, rowsAffected: result.rowsAffected };
}, function (error) {
return { success: false, message: error };
});
}
}
});
Vamos criar um factory para fazer o CRUD de uma tarefa.
app.factory('ToDoFactory', function (DbFactory) {
return {
/**
* Busca todas as tarefas do banco de dados
*/
all: function () {
return DbFactory.getData("select * from ToDo order by title").then(function (result) {
var items = [];
if (result.success) {
for (var i = 0; i < result.rows.length; i++) {
var row = result.rows.item(i);
items.push(row);
}
}
return items;
});
},
/**
* Salva uma tarefa no banco de dados
*/
save: function (item) {
var query = '';
var args = [];
if (item.id) {
query = 'update ToDo set title = ?, description = ? where id = ?';
args = [item.title, item.description, item.id]
} else {
query = 'insert into ToDo (title, description, done) values (?,?,?)';
args = [item.title, item.description, 0];
}
return DbFactory.saveData(query, args).then(function (result) {
return result;
});
},
/**
* Deleta uma tarefa do banco de dados
*/
delete: function(id) {
return DbFactory.saveData('delete from ToDo where id = ?', [id]).then(function (result) {
return result;
});
},
/**
* Recupera uma tarefa do banco de dados
*/
get: function (id) {
return DbFactory.getData("select * from ToDo where id = ?", [id]).then(function (result) {
if (result.success && result.rows.length > 0) {
return result.rows.item(0);
}
});
},
/**
* Marca uma tarefa como concluída
*/
done: function(id) {
return DbFactory.saveData('update ToDo set done = ? where id = ?', [1, id]).then(function (result) {
return result;
});
},
/**
* Recupera a quantidade de tarefas não concluídas
*/
countNotDone: function() {
return DbFactory.getData("select COUNT(*) as Qtd from ToDo where done = ?", [0]).then(function (result) {
var count = 0;
if (result.success) {
count = result.rows.item(0).Qtd;
}
return count;
});
}
}
});
Agora vamos criar um controller para listar as tarefas.
app.controller('ToDoListController', function ($scope, $ionicPlatform, $ionicListDelegate, $cordovaToast, $cordovaBadge, ToDoFactory) {
//utilizando a função $ionicPlatform.ready para verificar se o dispositivo (celular, tablet, ...)
//foi carregado completamente.
//Esse teste é importante sempre ao executar qualquer metodo de qualquer plugin do cordova
$ionicPlatform.ready(function () {
//buscando as tarefas cadastradas no banco
ToDoFactory.all().then(function (result) {
$scope.items = result;
});
//setando o badge para o total de tarefas não concluídas
ToDoFactory.countNotDone().then(function (count) { $cordovaBadge.set(count); });
});
//função para deletar uma tarefa do banco
$scope.delete = function ($index, id) {
$ionicPlatform.ready(function () {
//deletando o registro do banco
ToDoFactory.delete(id).then(function (result) {
//se deletou com sucesso
if (result.success) {
//remove o item da lista para não ter que recarregar toda a lista de novo
$scope.items.splice($index, 1);
$cordovaToast.showShortBottom('Tarefa excluída com suceso.');
//setando o badge para o total de tarefas não concluídas
ToDoFactory.countNotDone().then(function (count) { $cordovaBadge.set(count); });
} else { //se ocorreu algum erro para deletar
$cordovaToast.showShortBottom('Ocorreu um erro ao salvar a tarefa. Erro: ' + result.message);
}
});
});
};
//função para marcar uma tarefa como concluída
$scope.done = function ($index, id) {
$ionicPlatform.ready(function () {
//marcando o registro como concluído no banco
ToDoFactory.done(id).then(function (result) {
//se marcou com sucesso
if (result.success) {
//marco o item da lista como concluído para não ter que recarregar toda a lista de novo
$scope.items[$index].done = 1;
//fecha as opções abertas com o swipe na tela
$ionicListDelegate.closeOptionButtons();
$cordovaToast.showShortBottom('Tarefa concluída com suceso.');
//setando o badge para o total de tarefas não concluídas
ToDoFactory.countNotDone().then(function (count) { $cordovaBadge.set(count); });
} else { //se ocorreu algum erro para marcar como concluída
$cordovaToast.showShortBottom('Ocorreu um erro ao salvar a tarefa. Erro: ' + result.message);
}
});
});
}
});
E agora um controller para criar/alterar uma tarefa.
app.controller('ToDoEditController', function ($scope, $state, $stateParams, $ionicPlatform, $cordovaToast,
$cordovaLocalNotification, $cordovaBadge, ToDoFactory) {
//inicializando o objeto que vai ser o modelo da tela de cadastro
$scope.model = {};
//caso seja informado um id, é porque está editando um registro
if ($stateParams.id) {
$ionicPlatform.ready(function () {
//busca o registro no banco
ToDoFactory.get($stateParams.id).then(function (item) {
//popula o objeto de modelo com os dados do banco
$scope.model = item;
});
});
}
//função para salvar uma tarefa no banco
$scope.onSubmit = function () {
$ionicPlatform.ready(function () {
if ($scope.model) {
//salvando o registro no banco
ToDoFactory.save($scope.model)
.then(function (result) {
//se foi salvo com sucesso
if (result.success) {
$cordovaToast.showShortBottom('Tarefa salva com suceso.');
$state.go('home'); //voltando para a tela inicial
//gerando uma notificação local
$cordovaLocalNotification.schedule({
id: result.id,
title: 'Tarefa salva com sucesso.',
text: 'Você tem uma tarefa para ser executada. Mãos a obra! =]'
});
//setando o badge para o total de tarefas não concluídas
ToDoFactory.countNotDone().then(function (count) { $cordovaBadge.set(count); });
} else { //se ocorrer algum erro
$cordovaToast.showShortBottom('Ocorreu um erro ao salvar a tarefa. Erro: ' + result.message);
}
});
}
});
}
});
Por fim, vamos criar os templates das telas para listar e cadastrar as tarefas.
Tela de criar/alterar uma tarefa.
<ion-view title="New todo" class="bar-positive">
<ion-content class="has-header padding">
<form novalidate ng-submit="onSubmit();">
<div class="list">
<label class="item item-input item-floating-label">
<span class="input-label">Titulo</span>
<input type="text" placeholder="Titulo" ng-model="model.title">
</label>
<label class="item item-input item-floating-label">
<span class="input-label">Descrição</span>
<textarea cols="5" rows="10" placeholder="Descrição" ng-model="model.description"></textarea>
</label>
</div>
<button class="button button-block button-positive" type="submit">
Salvar
</button>
</form>
</ion-content>
</ion-view>
Tela para listar as tarefas.
<ion-view title="ToDo App" class="bar-positive">
<ion-nav-buttons side="secondary">
<button class="button icon ion-plus" ui-sref="new"></button>
</ion-nav-buttons>
<ion-content class="has-header padding">
<ion-list can-swipe="true">
<ion-item ng-repeat="item in items" ng-class="item.done == 1 ? 'todo-done' : ''">
<h2>{{ item.title }}</h2>
<p>{{ item.description }}</p>
<ion-option-button class="ion-checkmark button-balanced" ng-click="done($index, item.id)"></ion-option-button>
<ion-option-button class="ion-edit button-positive" ui-sref="edit({ id: item.id})"></ion-option-button>
<ion-option-button class="ion-trash-a button-assertive" ng-click="delete($index, item.id)"></ion-option-button>
</ion-item>
</ion-list>
</ion-content>
</ion-view>
E por fim, o arquivo index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<title></title>
<link href="lib/ionic/css/ionic.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
<!-- ionic/angularjs js -->
<script src="lib/ionic/js/ionic.bundle.js"></script>
<!--ngCordova-->
<script src="lib/ngCordova/dist/ng-cordova.js"></script>
<!-- cordova script (this will be a 404 during development) -->
<script src="cordova.js"></script>
<!-- your app's js -->
<script src="js/app.js"></script>
<script src="js/app.routes.js"></script>
<script src="js/app.controllers.js"></script>
<script src="js/app.services.js"></script>
</head>
<body ng-app="todo">
<ion-nav-bar class="bar-positive">
<ion-nav-back-button class="button-icon icon ion-arrow-left-b"> </ion-nav-back-button>
</ion-nav-bar>
<ion-nav-view></ion-nav-view>
</body>
</html>
Eu espero que esse tutorial tenha sido útil para você. Qualquer dúvida deixe seu comentário abaixo.
O código fonte de exemplo está disponível no GitHub.