Ionic Framework

A introdução que faltava ao Ionic 2

Era uma vez…

Há cerca de dois anos e meio eu postei o meu primeiro artigo sobre o Ionic Framework (Aplicativos mobile com o AngularJS e Ionic) e desde então Web Platform evoluiu, o ES6 passou a se chamar ES2015 e falta pouco para ser totalmente suportado pelos navegadores, o ES7 virou ES2016 e está logo ali.

Ferramentas como Babel, Typescript e Flow se tornaram comuns. Grunt e Gulp foram aos poucos sendo substituídos pelo Webpack (e o Rollup.js já tá vindo aí). Além disso, o código front-end evoluiu pra uma arquitetura baseada em componentes e isso muito se deve ao React e seus derivados.

E nesse novo mundo front-end o AngularJS 1.x perdeu espaço. O two-way data binding que nós tanto gostávamos era, na verdade, sofrível para a performance. Os escopos eram uma zona. Tínhamos várias formas de fazer a mesma coisa (controller vs directive, service vs factory). A declaração de diretivas era muito complexa. E, por fim, não era possível fazer Server Side Rendering (a não ser que você fosse pelo longo e tortuoso caminho dos hacks obscuros).

E apesar de tudo isso o Ionic Framework continuava sendo uma ferramenta fantástica. Ele possibilitou que muitos de nós conseguissemos criar nossas aplicações sem perder tempo escrevendo o mesmo código de diferentes formas pra duas ou mais plataformas.

Porém, isso não quer dizer que ele não tinha seus problemas. Tinha sim. E muitos. O Ionic Framework foi inicialmente pensado para o iOS e lá ele funcionava muito bem, inclusive com look and feel nativo. Só depois ele foi sendo adaptado (leia-se: remendado) para o Android. E isso nunca permitiu que ele fosse um framework 100% lá. Além disso, no Android, nós temos uma infinidade de dispositivos com inúmeras características de software, performance, resolução, sensores, etc. que prejudicavam ainda mais a experiência no sistema operacional.

É claro que com um pouco de trabalho duro era possível alcançar um nível de qualidade aceitável em nossos aplicativos. E, como bem sabemos, a lei de Moore tá aí pra deixar nossos aplicativos legados cada dia mais rápidos.

Recomeço

Mas vamos avançar um pouco no tempo.

Com todo o aprendizado adquirido pela comunidade front-end nos últimos anos a equipe do AngularJS decidiu reescrever ele inteiro para atender as necessidades dos aplicativos do presente (quiçá do futuro) e, por mais que a ideia fosse boa e, provavelmente, necessária, houve resistência da comunidade. Afinal seria necessário reaprender a usar o Angular. A comunidade ficou dividida.

Mas o que parecia um problema para a equipe do Ionic pode ter se tornado o seu maior trunfo. Com a eminência do Angular 2 e sua total reescrita a equipe do Ionic decidiu comprar a ideia e também reescrever o Ionic. Só que agora usando todo o conhecimento adquirido ao longo dos anos de desenvolvimento do framework.

E hoje, meus amigos, foi lançada oficialmente a nova versão do Ionic. Eu já tenho usado o Ionic 2 desde o beta 4, ou seja, já tem quase 1 ano e posso dizer que passei por uns perrengues por ser early adopter, mas ele evoluiu muito quando comparado ao seu antecessor.

Ainda temos alguns bugs, mas, no geral, uma aplicação criada com o Ionic 2 é muito, mas muito, mas muito melhor que uma criada no Ionic 1. Sem contar que é muito mais fácil dar manutenção conforme a aplicação vai crescendo.

O que tem de novo?

Por herdar as caracaterísticas do Angular 2, o framework agora é mais performático e é basicamente Javascript, ao contrário do Angular 1 (onde precisávamos aprender um monte de coisas pra podermos trabalhar [$scope, $q, controller, directive, etc.]).

É importante ressaltar que agora nós podemos tirar proveito do ES2015 ES6 (tecnicamente é Typescript) para manter nosso código melhor, mais legível e organizado.

O Ionic 2 foi projetado com o conceito de temas, ou seja, por padrão ele já segue as guidelines de design do iOS, do Material Design e do Windows Phone. A estrutura e a forma de personalizar os componentes também ficaram muito mais claros, já que o SASS está mais bem estruturado e o processo de build já vem com ele configurado out of the box.

Por falar em estrutura e build, o Ionic 2 está muito mais simples e organizado nesses pontos. Não temos mais várias formas de fazer as mesmas coisas. Possuímos uma CLI que nos ajuda a gerar automaticamente páginas, componentes, providers, pipes, tabs e diretivas. E, como se não bastasse, nos fornece um processo de build bem definido, que vai compilar, otimizar e minificar nosso aplicativo (pretendo aprofundar mais em como funciona esse processo aqui no blog futuramente).

Calma, que já tá acabando. Foram adicionados novos componentes de UI que antes não existiam (como o DateTime picker). O ngCordova “evoluiu” para o Ionic Native. Existe, por padrão, uma nova ferramenta para captura de erros. Agora existe suporte aos famigerados Progressive Web Apps, dessa forma não estamos mais presos ao controle das lojas de aplicativos.

E pra finalizar: não existe mais um sistema de navegação baseado em URLs. E isso é uma coisa boa, acredite. A equipe do Ionic percebeu que um sistema de navegação baseado em URLs como o ngRoute ou o UI-Router limitavam os aplicativos a forma como as aplicações funcionam na web. E na web tudo isso faz sentido, já que dependemos das URLs para sair navegando por aí. Mas nos aplicativos isso era uma limitação, já que neles a gente não depende de URLs para navegar. Tudo o que nós precisamos é poder exibir uma página e remover essa página da pilha de navegação. Por mais que esse conceito pareça simples, ele nos trás a possibilidade de possuirmos múltiplas pilhas de navegação que compartilham das mesmas páginas e funcionam de forma independente, ficou complexo? Pretendo discorrer sobre o tema em um próximo artigo.

Conclusão

Mudou muita coisa, mas vale a pena dar uma olhada. E, se não der para migrar seus antigos aplicativos, no mínimo comece a escrever os novos usando o Ionic 2.

Existe um guia para migração que você pode encontrar clicando aqui e para mais informações sobre o lançamento oficial é só acessar o post: Announcing Ionic 2.0.0 Final.

Pretendo voltar a escrever mais aqui, ainda não sei se vou me comprometer a postar com certa regularidade (já que tentei outras vezes e fracassei miseravelmente), mas pretendo falar sobre o novo sistema de navegação, testes automatizados, integração contínua, typescript, offline first e por aí vai. Então, nos vemos em breve.

recipes

Ionic Market: Recipes Starter

Fala pessoal, beleza? Durante esse mês de janeiro eu trabalhei duro em um projeto pessoal para testar as minhas habilidades com o Ionic Framework e o resultado foi o aplicativo Mini Receitas que acabou indo parar no Featured Apps do Showcase do Ionic por alguns dias como um dos melhores aplicativos criados com o Framework.

Pois bem, é hora de seguir em frente e compartilhar mais um pouco do meu conhecimento com vocês. Acabei de lançar o código base do meu aplicativo Mini Receitas lá no Ionic Market.

Fiz esse projeto pensando muito em performance e manutenabilidade. Logo, vocês terão acesso a um aplicativo rápido, com código organizado. Além de vir com integração com plataformas como AppTentive e CodePush.

Ainda tem muitas features que desejo implementar nesse projeto, como integração com Facebook, analytics, notificações push, deeplinks, anúncios, etc. e será através do feedback da comunidade que irei me guiar dentre as tecnologias disponí­veis.

tappx

Promova seu aplicativo de graça

Eu sei, o título desse post até parece sensacionalista e com o objetivo de conseguir cliques, mas, pelo menos dessa vez, não é. Nesse ano de 2016 eu me propus um desafio: viver só com a grana proveniente de meus aplicativos para dispositivos móveis e pra isso eu estabeleci que faria um aplicativo por mês até o fim do ano.

Daí você pergunta: "Ok, mas o que isso tem a ver com promoção de aplicativos gratuita?". Bom, a princípio nada, exceto que para conseguir viver disso eu preciso que os aplicativos tenham muitos downloads, porém com o grande número de aplicativos nas lojas hoje a concorrência é muito grande e provavelmente meus aplicativos não vão ganhar usuários se eu não encontrar formas de chegar até eles.

E aí que entram técnicas como ASO, que é o equivalente ao SEO para aplicativos, e os Ads, afinal, eu posso anunciar meus aplicativos. Mas aí entra um outro porém, o custo de aquisição do usuário. Enquanto o ASO é um conjunto de técnicas para melhorar o rankeamento nas lojas, na maioria das vezes não é necessário gastar dinheiro com ele. Já com os Ads a conversa muda.

O custo médio de aquisição de usuários varia bastante de acordo com inúmeros fatores, como: país, tema do aplicativo, idade do público alvo, etc.. E, infelizmente, eu não tenho muita grana pra investir, por exemplo, R$ 1,00 para cada nova instalação do aplicativo, e olha que esse é um valor relativamente baixo.

E foi aí que eu conheci o Tappx.

Tappx

O Tappx é um Ad Network para promoção de anúncios diferente das outras. Nela, a cada cliques em anúncios veiculados por ela em seu aplicativo você ganha 500 tappix, que é a moeda de troca da plataforma, com eles você pode criar suas próprias campanhas onde a cada clique que seu anúncio receber você paga 500 tappix, ou seja, para cada vez que alguém clicar em um anúncio no seu aplicativo você ganha o direito de exibir anúncios nos aplicativos de outras pessoas até que alguém clique no seu anúncio.

O interessante é que a comunidade Tappx hoje é muito forte e existem alguns aplicativos bem populares que a utilizam. Ontem, em meu primeiro teste com a plataforma, eu consegui milhares de impressões do meu anúncio em poucas horas. Infelizmente eu não tinha tappix o sufuciente para conseguir muitos cliques, mas aí entra outro fator interessante sobre a plataforma: a cada pessoa indicada por você que passar a usar a plataforma você ganha 50.000 tappix.

Ionic

O Tappx possui integração tanto para Android quanto para iOS, então se você desenvolve nativo é só baixar a SDK deles e ser feliz. No entanto, também é possível integrar com outras plataformas como Unity, Firefox OS e Cordova.

O meu novo aplicativo (Mini Receitas) foi desenvolvido em Cordova/Ionic e eu realizei a integração através do plugin Cordova Admob que inclusive já possui um módulo AngularJS para integração com aplicativos Ionic. Nesse plugin você pode configurar tanto o AdMob quanto o Tappx e ainda dividir os anúncios exibidos em seu aplicativo entre as duas plataformas.

  1. Para instalar o plugin no Ionic basta rodar o comando:
ionic plugin add cordova-admob
  1. Adicionar o seguinte código em seu index.html (note que você não precisa copiar nenhum arquivo, o plugin copiará os arquivos automaticamente na fase prepare do Cordova):
<script src="lib/angular-admob/angular-admob.js"></script>
  1. Injetar o módulo na definição do seu aplicativo:
var app = angular.module('myApp', ['admobModule']);
  1. Configurar o plugin com as suas informações do AdMob e do Tappx:
app.config(['admobSvcProvider', function (admobSvcProvider) {
    admobSvcProvider.setOptions({
        publisherId:          "ca-app-pub-XXXXXXXXXXXXXXXX/BBBBBBBBBB",  // Obrigatório
        interstitialAdId:     "ca-app-pub-XXXXXXXXXXXXXXXX/IIIIIIIIII",  // Opcional
        tappxIdiOs:           "/XXXXXXXXX/Pub-XXXX-iOS-IIII",            // Opcional
        tappxIdAndroid:       "/XXXXXXXXX/Pub-XXXX-Android-AAAA",        // Opcional
        tappxShare:           0.5                                        // Opcional
    });
}]);
  1. Exibir os anúncios (você pode fazer isso em um controller específico também):
app.run(['admobSvc', function (admobSvc) {
    admobSvc.createBannerView();
}]);

E é isso. Em um próximo post espero poder falar mais sobre ASO (App Store Optimization), ainda estou estudando e aplicando aos poucos os conhecimentos adquiridos. O meu novo aplicativo (Mini Receitas) ainda não tem muitos downloads, mas com uma plataforma de promoção gratuita as chances de eu atingir meus objetivos ao final do ano aumentam e espero que vocês também possam vir a se beneficiar da plataforma.

2015-08-20

Design Patterns em JavaScript – Parte 2 ( Constructor Pattern )

Continuando nossa série de Design Patterns em JavaScript que começou no  artigo passado vamos falar hoje sobre nosso primeiro padrão de projeto.

Em linguagens clássicas orientada a objetos, o constructor é um método especial usado para inicializar um objeto recém criado, uma vez que a memória já foi alocada para ele. Como em JavaScript quase tudo é um objeto, estamos interessados em objetos construtores.

Objetos construtores são usados para criar tipos especificos de objetos, preparando o objeto para uso e aceitar argumentos que um construtor pode usar para definir os valores da propriedade e métodos quando o objeto é criado pela primeira vez

Criando o Objeto

Segue uma das três maneiras comuns de se criar objeto em JavaScript:

// Opções de criar objetos vazios

var novoObjeto = {};

// ou
var novoObjeto = Object.create(Object.prototype);
// ou
var novoObjeto = new Object();

No último exemplo o "Object" constructor cria um objeto para um valor especificou ou quando não é passado nenhum valor ele cria um objeto vazio e retorna o mesmo.

Continuando, existem várias maneiras de atribuir a um objeto valores do tipo chave/valor, vamos ver algumas delas:

// Usando ponto (.)
 
// Setando propriedade
novoObjeto.chave= "Olá";
 
// Obtendo propriedade
var value = novoObjeto.chave;
 
 
// Usando colchetes([])
 
// Setando propriedade
novoObjeto["chave"] = "Olá";
 
// Obtendo propriedade
var value = novoObjeto["chave"];
 

Construtores básicos

Como vimos, JavaScript não suporta o conceito de classes mas ele simula com funções construtoras que trabalham com objetos O prefixo usado para chamar uma função contrutora é o new que cria o objeto com seus membros que foram definidos.

Nos construtores, a palavra this faz referência ao novo objeto que está sendo criado, vejamos:

// A RacionaisMCs "class"
function RacionaisMCs( music ) {
 
  this.music = music;
  this.year = "2014";
  // Adicionando comportamento a classe
  this.getInfo = function () {
    return this.music + " " + this.year;
  };
 
};
// Criando objetos
var primeiraMusica = new RacionaisMCs("Vida Loka");
var segundaMusica = new RacionaisMCs("Jesus Chorou");

console.log(primeiraMusica.getInfo()); 
console.log(segundaMusica.getInfo());

Este exemplo acima, não é a melhor maneira de criar objetos, pois sofre alguns problemas como fazer herança de forma dificil e performance pois o método getInfo é recriado e adicionado na memória toda vez que criamos um objeto do tipo RacionaisMCs

Como resolver isso? Com prototype!

Construtores com Prototype

Para quem não sabe o JavaScript é uma linguagem prototype-based. O que isso quer dizer? Quer dizer que ela é baseada em protótipos : Cada objeto tem um link interno para um outro objeto chamado prototype. Esse objeto prototype também tem um atributo prototype, e assim por diante até que null seja encontrado como prototype (como em uma implementação de fila em C, por exemplo). null, por definição, não tem prototype, e age como um link final em um encadeamento de protótipos (prototype chain).

Quando instanciamos um objeto, este já tem uma referência(__proto__) para o prototype da função que o construiu . Ou seja, só haverá um método na memória: o definido no prototype da função construtora! Assim ganhamos em memória e performance.

Sendo assim daqui pra frente vamos adotar o uso do prototype em nossas funções. Continuando nossa Classe ficaria assim:

// A RacionaisMCs "class"
function RacionaisMCs( music ) {
   this.music = music;
   this.year = "2014";
 };
 // Adicionando comportamento a classe
 RacionaisMCs.prototype.getInfo = function () {
   return this.music + " " + this.year;
 };

Agora temos uma única instância de getInfo que pode ser compartilhado entre todos objetos de RacionaisMCs

É isto pessoal, logo mais eu volto com nosso segundo Padrão.

router

Brincando com o novo Router do AngularJS – Parte 1

O novo Router do AngularJS é um dos pontos chaves dessa fase de transição que o framework tem passado, pois ele será o cerne de uma das estratégias de migração das aplicações já existentes. Sendo uma robusta solução para gerenciamento de rotas de uma aplicação e oferecendo basicamente os mesmos recursos do UI-Router tem como diferencial a possibilidade de controlar as rotas tanto do 1.x quanto do 2.0 simultaneamente, permitindo assim a migração iterativa incremental das aplicações já escritas com o Angular 1.x.

Essa estratégia de migração vem está sendo especificada na sessão "Component Router Interop Strategy" do documento: AngularJS 1 to Angular 2 Upgrade Strategy.

Porém, tendo em vista que o Angular 2.0 ainda está em fase beta e provavelmente ninguém irá migrar suas aplicações agora, vamos dar uma olhada em como esse novo Router irá funcionar no Angular 1.4, que é a versão mais recente do framework que temos no momento.

Primeiros passos

A primeira mudança notada em relação ao antigo Router é o fato de agora termos que fazer nossas configurações de rotas em um controller e não mais no config de nossa aplicação, o que acaba por nos obrigar a ter pelo menos um controller na aplicação.

 

Nesse controller nós devemos injetar a dependência do nosso $router e nela passar as configurações de nossas rotas. Essas configurações são compostas basicamente de uma lista de objetos contendo as informações de cada rota, onde o path é o caminho da rota e o component é o nome do controller que será usado em nossa rota.

function AppController($router){
  $router.config([
    { path: '/', component: 'home' },        // Será linkado para um controller
    { path: '/sobre', component: 'sobre' }   // Será linkado para um template
  ]);
}
AppController.$inject = ['$router'];

E assim como no antigo Router ainda é necessário especificar para nossa aplicações onde o conteúdo das rotas será renderizado e para isso foi introduzida a diretiva ng-viewport.

 

Certo, agora basta incluir os controllers e/ou templates especificados em nossas rotas, o único porém é que o novo Router é mais rígido quanto a localização desses componentes. Nos obrigando a seguir a recomendação de estrutura de aplicações descritas no documento: Best Practice Recommendations for Angular App Structure.

Ok, mas o que isso quer dizer? Isso quer dizer que os seus componentes devem seguir o padrão de diretórios especificado nesse documento, ou seja, em nossa aplicação ficaria algo como:

.
+– sampleapp/
|    +– app.js
|    +– components/
|    |    +– sobre/
|    |    |    +– sobre.html
|    |    |    +– sobre.js
|    |    +– home/
|    |    |    +– home.html
|    |    |    +– home.js
|    +– index.html

Agora que já conhecemos a nova estrutura de diretórios é bom lembrar que por padrão nossos controllers devem começar com uma letra maiúscula e terminar com o sufixo Controller, logo, os controllers dos componentes especificados anteriormente ficariam:

home.js

function HomeController(){}

sobre.js

function SobreController(){}

Nós podemos alterar os padrões descritos anteriormente através da utilização do serviço $componentLoaderProvider.

Mas e para mudar de rota? Bom, para navegar para uma nova rota nós podemos simplesmente fazer da forma antiga com com uma âncora <a href="#/sobre"> ou utilizar a nova diretiva ng-link:

Home
Sobre

Note que em nosso segundo link nós passamos um objeto com a propriedade appNome. As propriedades desse objeto serão recebidas como parâmetros pelo controller de nossa rota sobre.

Para acessar esses parâmetros no controller nós utilizamos o serviço $routeParams:

function SobreController($routeParams){
  this.autor = 'Jean Lucas de Carvalho';
  this.app = $routeParams.appNome;
}
SobreController.$inject = ['$routeParams'];

No novo Router também é possível realizar redirecionamentos de uma rota para outra através da opção redirectTo e atribuir apelidos para as rotas através as como no exemplo a seguir:

function AppController($router){
  $router.config([
    { path: '/', redirectTo: 'home'},
    { path: '/home', component: 'home', as: 'index' },
    { path: '/sobre/:appNome', component: 'sobre' }
  ]);

  this.nome = "Exemplo";
}
AppController.$inject = ['$router'];

Após utilizar um alias através do as nós podemos alterar nosso link para:

Home

Exemplo

 

 

Conclusão

O Novo Router ainda nos trás outras possibilidades como rotas filhas de um componente ou carregamento de múltiplos componentes utilizando diferentes viewports por rota mas eu deixarei para abordá-los no próximo artigo.

ps.: Originalmente esse artigo era gigante, mas devido a uma pane no meu editor markdown eu perdi 3/4 dele. Acabei resolvendo complementar o que eu não tinha perdido e publicar em duas partes, pois assim terei tempo para reescrever todo o conteúdo perdido ;)

sync

Sincronização SQL com persistence.js

Continuando a série sobre o persistence.js nesse artigo iremos entender como ele consegue realizar a sincronização entre dois bancos de dados. O persistence.js vem com um componente de sincronização para o client-side (persistence.sync.js) e um exemplo de implementação em Node.js para o server-side (persistence.sync.server.js). No entanto, nós podemos implementar a sincronização no backend em qualquer linguagem, desde que se obedeça os padrões definidos pelo ORM.

Client-side

No lado do cliente (no navegador ou em nosso aplicativo cordova/ionic), para fazer nossa aplicação sincronizar nós precisamos seguir alguns passos que, no caso de nosso exemplo do artigo anterior, ficaria assim:

var Feed = persistence.define('Feed', function(t){
    t.text('nome');
    t.text('conteudo');
    t.text('url');
    t.boolean('favoritado');
});

Feed.enableSync('/feedChanges');

No exemplo anterior nós definimos uma entidade Feed, que não havíamos definido no outro artigo, e note que para especificar que queremos sincronizá-la nós só precisamos chamar o método enableSync() passando o endpoint onde o servidor irá expor as mudanças daquela entidade.

Para iniciarmos uma sincronização nós chamamos o método syncAll(conflictHandler, successCallback, errorCallback), que recebe 3 callbacks como parâmetros, sendo as duas últimas opcionais. A primeira callback será executada quando ocorrer um conflito em alguma tupla da entidade, a segunda será executada caso a sincronização ocorra com sucesso e a terceira caso ocorra algum erro (como um erro 500 no servidor por exemplo).

Por padrão o persistence.sync.js já vem com duas estratégias de resolução de conflitos pré definidas e nós podemos simplesmente utilizar uma das duas. Mas a biblioteca é flexível o suficiente para nos permitir implementar o tipo de resolução de conflitos que acharmos conveniente.

  1. persistence.sync.preferLocalConflictHandler, em caso de conflito sempre irá escolher as alterações locais;
  2. persistence.sync.preferRemoteConflictHandler, em caso de conflito sempre irá escolher as alterações remotas.
Feed.syncAll(
        persistence.sync.preferLocalConflictHandler,
        function(){
            // Sincronização realizada com sucesso
        },
        function(err){
            // Ocorreu algum erro na sincronização
        });

É importante ressaltarmos que a responsabilidade de manter a base de dados consistente é do desenvolvedor. Se tivermos um relacionamento entre duas entidades e sincronizarmos apenas uma o banco de dados poderá ficar incosistente enquanto não sincronizarmos a outra.

Atualmente essa sincronização não consegue sincronizar relacionamentos muitos-para-muitos (many-to-many). Mas como nós sabemos como esse tipo de relacionamento funciona nos bancos relacionais nós podemos simplesmente fazer na unha.

Server-side

Aqui eu poderia focar na implementação para alguma linguagem específica, mas tendo em vista que já existem implementações (em Node.js, PHP e Java com Slim3 e AppEngine ), resolvi focar em como a sincronização funciona, pois assim poderemos criar a sincronização para qualquer linguagem e framework que quisermos. Basicamente para que nossa sincronização funcione todos os nossos endpoints tem que implementar os métodos GET e POST.

GET

O método GET deve possuir um parâmetro since que receberá o timestamp da última mudança realizada no banco local.

GET /feedChanges??since=1279888110373

A resposta da chamada acima deverá ser algo como:

{
    "now":1279888110421,
    "updates": [
        {
            "id": "F89F99F7B887423FB4B9C961C3883C0A",
            "favoritado": true,
            "_lastChange": 1279888110370
        }
    ]
}

Note que o servidor deverá nos retornar através da propriedade now o timestamp da hora atual do servidor para que possamos passar para o servidor em uma próxima atualização. O servidor também deve retornar um array updates que contenha as atualizações que ocorreram no servidor desde o timestamp que informamos em nossa requisição.

Todos os objetos retornados pelo servidor devem, obrigatoriamente, retornar pelo menos a propriedade id e a propriedade _lastChange que é o timestamp da última atualização daquele objeto.

Em alguns casos é necessário que o cliente sincronize apenas alguns dados do servidor, como na sincronização de dados do usuário. Para efetuarmos esse tipo de sincronização basta filtrarmos as respostas de nosso método GET conforme as nossas necessidades.

POST

O método de sincronização do persistence.sync.js irá efetuar uma requisição POST onde o corpo da requisição (também conhecido como body) será um array no formato JSON contendo todos os novos objetos e os objetos alterados desde a última sincronização. Todos os objetos devem possuir pelo menos a propriedade id.

POST /feedChanges

Corpo da requisição

[
    {
        "id":"F89F99F7B887423FB4B9C961C3883C0A",
        "favoritado": false
    }
]

Após a requisição o servidor deverá persistir as mudanças adicionando a cada objeto a propriedade _lastChange contendo o timestamp atual do servidor. Caso a persistência de dados seja efetuada com sucesso, esse timestamp será retornado para o cliente:

{
    "status": "ok",
    "now": 1279888110797
}

É importante que a propriedade _lastChange de todos os objetos inseridos/atualizados na mesma requisição sejam o mesmo.

Conclusão

A sincronização de dados do persistence.js não cobre todos os pontos que possam ser necessários em algumas aplicações, mas em grande parte dos casos (desde que o desenvolvedor tome as devidas precauções) ela é mais que o suficiente.

Acredito que nesses dois artigos eu cobri as partes mais interessantes da biblioteca, no entanto senti falta de um artigo introdutório sobre o ORM que eu procurarei expor em uma próxima ocasião.

2015-08-20

Design Patterns em JavaScript

Olá pessoal tenho o prazer de apresentar para vocês o começo de uma série de artigos sobre Design Patterns em JavaScript.

Definição

O que é isso? Porque usar?

Design Patterns ou Padrões de projetos são padrões que descrevem problemas que ocorrem frequentemente e então descreve uma solução ao problema, a fim de poder reusar essa solução milhares de vezes.

Ok, mas no que isso me ajuda?

Ajuda atribuindo responsabilidade a objetos, deixando o projeto fácil de compreender, reusar e dar manutenção. E claro dando flexibilidade ao mesmo.

Legal mas quando eu vou usar esses padrões?

Você chegou onde eu queria, como é a estrutura de um padrão? Basicamente ele tem quatro elementos essenciais são eles:

  • Nome
  • Descrição do problema ou contexto para os quais o padrão se aplica
  • Descrição da solução genérica proposta
  • Descrição da aplicação do padrão (custos e beneficios)

Pois bem, o entendimento de como funciona um padrão de projeto pode nos oferecer uma série de benefícios úteis, como por exemplo um raciocínio mais apreciado sobre determinado problema, saber se a solução proposta é ou não um padrão de projeto e até mesmo rever se o padrão utilizado é mesmo necessário.

Escrever padrões de projetos é uma tarefa desafiadora. Padrões não precisam somente de uma quantidade substancial de material para usuários finais, mas também de uma defesa convicente do porque eles são necessários

Depois de sabermos a definição do que é um padrão de projeto, as vezes pensamos que isso é o suficiente para indentificarmos padrões. Mas não nos enganemos, nem sempre um pedaço de código que estamos olhando está usando um padrão, as vezes ele apenas se parece com um padrão.

Quando olhamos um trecho de código que achamos estar usando algum padrão, devemos listar alguns aspectos do código em que acreditamos que não se trata de um padrão ou conjunto de padrões. Em muitos casos de análise olhamos apenas se o código segue boas práticas e princípios de design que podem coincidir com alguns padrões por acidente. Mas o fato é: qualquer solução que não faça interações e nem tenha regras definidas, essas soluções não tem padrões

Anti-Patterns

Se nós consideramos que padrões são uma boa prática, obviamente anti-patterns representa uma má pratica. Isto é descreve uma má solução para um problema particular que desencadou um mal comportamento.

Alguns exemplos de anti-patterns em JavaScript são:

Categorias de Design Patterns

Os padrões de projeto são dividos em dois parâmetros: propósito (criacional, estrutural, compotamental) e escopo (classes e objetos)

categorias design patters

Propósito

  • Criacional : Processo de criação de objetos
  • Estrutural : Composição de classes e objetos
  • Comportamental : Interação e distribuição de responsabilidades entre objetos e classes

Escopo

  • Classe: Relação entre classes e subclasses (herança)
  • Objetos: Relação entre objetos (associação, composição)

Uma breve introdução a Classes

JavaScript não tem classes, no entanto elas são simuladas usando funções.

A maneira mais comum de fazer isso é definir uma function e depois instanciar com o operador new. Nós podemos usar o this para definir novas propriedades e métodos como no exemplo abaixo:

// A Racionais "class"
function RacionaisMCs( music ) {

  this.music = music;
  this.year = "2014";

  this.getInfo = function () {
    return this.music + " " + this.year;
  };

}

Agora podemos instância um objeto usando a função construtora RacionaisMCs que acabamos de definir:

 

var musicaPreferida = new RacionaisMCs("Negro Drama");

musicaPreferida.year = "2002";

console.log(musicaPreferida.getInfo()); // "Negro Drama 2002"

 

Para mais maneiras de definir classes, olhe este link

Veja a parte 2 dessa série!

analytics_and_ionic

Google Analytics e Ionic Framework

O Google Analytics é um serviço gratuito do Google para monitoramento e análise do comportamento dos usuários de sua aplicação, seja ela um site ou um app mobile. Quem já trabalhou com web provavelmente já o utilizou em algum momento, mas no contexto de aplicativos, principalmenete os híbridos, pouco se é dito a respeito dele.

Entender os fluxos que o usuário segue, quais ações ele executa, quanto tempo ele passa na aplicação, etc. são de extrema importância para sabermos onde e o que melhorar em nossas aplicações para atingirmos um maior engajamento e uma maior taxa de conversão.

Para utilizarmos o Google Analytics em nossa aplicação Cordova/Ionic Framework nós precisamos adicioná-lo ao projeto usando o comando:

cordova plugin add https://github.com/danwilson/google-analytics-plugin.git

Após adicionado ao projeto nós devemos setar nosso tracking Id como no código a seguir:

var myApp = angular.module('myApp', ['ionic'])
    .run(function($ionicPlatform, $ionicPopup) {
        $ionicPlatform.ready(function() {
            if(typeof analytics !== undefined) {
                // No lugar de "UA-XXXXXXXX-XX" você deve colocar o seu tracking id
                analytics.startTrackerWithId("UA-XXXXXXXX-XX");
            } else {
                console.log("Google Analytics indisponível");
            }
        });
    });

O plugin do Google Analytics só funciona nas plataformas Android e iOS, por isso, nesse artigo, nós sempre verificamos se o analytics está disponível para que o código não quebre em outras plataformas e/ou no ambiente de desenvolvimento no browser.

Para realizarmos o acompanhamento de nossa aplicação podemos fazer como no exemplo a seguir:

myApp.controller('HomeController', function($scope) {
    if(typeof analytics !== undefined) {
        analytics.trackView("Home Controller");
    }

    $scope.doTheHarlemShake = function() {
        if(typeof analytics !== undefined) {
            analytics.trackEvent("Category", "Action", "Label", 25);
        }
    }
});

No código acima, assim que o HomeController é chamado nós o reportamos como uma view para o Analytics. E quando o usuário executa o método doTheHarlemShake (que pode ser disparado ao clicar em um botão, ao carregar mais itens em um infinite scroll, etc.) nós o reportamos como um evento para o Analytics.

O plugin possui outras opções de tracking que podem ser consultados na documentação e como cada aplicação possui próprias métricas e metas, caberá ao desenvolvedor saber quais dos métodos presentes no Google Analytics serão mais úteis para o contexto de sua aplicação.

pouchdb

Consultas rápidas no PouchDB

Como uma continuidade do último post sobre PouchDB, nesse artigo iremos aprofundar nas formas de realizar queries no banco de dados. Basicamente existem três formas de realizarmos consultas no PouchDB, uma é através da API allDocs() e as outras duas são através da API query(), onde temos as queries temporárias e as queries persistentes.

allDocs()

Utilizando a API allDocs() sem passar parâmetros nós basicamente estamos buscando por todos os registros de nosso banco de dados. Porém, embora pareça simples, essa API é bastante poderosa, pois ela nos fornece algumas opções para realizar nossas consultas. Dentre elas, as que eu mais utilizo são:

  • include_docs: caso essa propriedade não seja especificada todos os documentos serão retornados apenas com _id e _rev;
  • startkey & endkey: busca os documentos cujo IDs estão inclusos no intervalo especificado por essas opções;
  • limit: número máximo de documentos a serem retornados;
  • skip: número de documentos que iremos “pular” em nossa consulta;
  • descending: retorna os documentos em ordem inversa;
  • para outras opções acesse a documentação do allDocs().

Mas como podemos usar essas opções em nosso favor? Bom, para fazer uma paginação é bem claro que podemos usar as opções limit e skip, mas e se eu não quiser que todos os documentos do meu banco de dados sejam retornados, é possível filtrá-los?

Utilizando o startkey e o endkey nós podemos buscar apenas os documentos que estão em um determinado intervalo de IDs, certo? Imagine uma situação onde precisamos cadastrar “pessoas” e “empresas” em nosso banco de dados e queremos poder filtrar por cada um desses dois tipos de documentos.Utilizando as opções startkey e endkey nós podemos fazer isso desde que a gente defina os IDs de nossos documentos ao invés de deixar que o PouchDB faça isso.

var db = new PouchDB('mydb');

db.put({
    _id: 'pessoa-' + new Date().getTime(),
    nome: 'Jean Lucas de Carvalho',
    empresa: 'Futuring'
});

db.put({
    _id: 'pessoa-' + new Date().getTime(),
    nome: 'Michael Villander',
    empresa: 'Front In Brazil'
});

db.put({
    _id: 'empresa-' + new Date().getTime(),
    nome: 'Futuring',
    cidade: 'Goiânia'
});

No exemplo acima nós criamos nosso banco de dados e cadastramos três documentos nele: dois do tipo “pessoa” e um do tipo “empresa”. Agora realizaremos uma consulta utilizando o allDocs() buscando apenas os documentos com o tipo “pessoa”:

db.allDocs({
    include_docs: true,
    startkey: 'pessoa-',
    endkey: 'pessoa-\uffff'
}).then(function (result) {
    console.dir(result);
});

O exemplo acima nos retornará todos os documentos cujo _id começa com pessoa-. Essa consulta funciona graças ao caractere Unicode \uffff, pois é através dele que nós fazemos buscas por prefixo, ou seja, estamos dizendo ao PouchDB "me retorne todos os documentos cujo _id começa com pessoa-".

Essa é uma das formas de definir tipos de documentos no PouchDB, mas as vezes nós não podemos alterar a forma padrão como o banco de dados trata os IDs e pra esses casos uma outra forma bastante usada para definir tipos de documentos é atribuir uma propriedade ao documento especificando seu tipo como veremos na sessão a seguir.

query()

As vezes o allDocs() não é o suficiente para realizar consultas em nossas aplicações e aí nós precisamos de uma outra API que nos dê mais possibilidades, como fazer um Map/Reduce por exemplo. No PouchDB essa API é a query() e ela possui algumas peculiaridades.

Temporary queries

Como o próprio nome diz queries temporárias são temporárias, ou seja, elas são criadas no momento da consulta e depois deixam de existir. Por esse motivo elas não são indexadas o que acaba tornando esse tipo de query bem lenta se compararmos às Persistent Queries ou ao allDocs().

Para implementar o exemplo anterior utilizando a API query() nós não precisamos alterar a forma como o PouchDB trabalha com IDs. Na verdade nós devemos adicionar a todos os documentos uma propriedade que o identifique, como no exemplo a seguir, onde definimos uma propriedade type para cada documento e depois fazemos uma consulta que nos retorna todos os documentos cujo valor da propriedade type é igual a pessoa.

var db = new PouchDB('mydb');

db.post({
    nome: 'Jean Lucas de Carvalho',
    empresa: 'Futuring',
    type: 'pessoa'
});

db.post({
    nome: 'Michael Villander',
    empresa: 'Front In Brazil',
    type: 'pessoa'
});

db.post({
    nome: 'Futuring',
    cidade: 'Goiânia',
    type: 'empresa'
});

db.query(function (doc, emit) {
    emit(doc.type);
}, {key: 'pessoa', include_docs: true}).then(function (result) {
    // retorna todos os documentos cujo valor da propriedade "type" é igual a "pessoa"
});

A função emit faz parte da API de map/reduce do CouchDB. O que essa função basicamente faz é emitir, para cada documento, doc.type como key.

Persistent queries

Persistente queries são a forma recomendada de se usar a API query() segundo a documentação do PouchDB. E a diferença básica desse tipo de queries para as Temporary queries é o fato de que elas são salvas no banco e indexadas. Pelo fato de serem indexadas elas são bem mais performáticas que as queries temporárias. Para criarmos uma query persistente nós devemos seguir 2 passos:

  1. Criar um design document
var ddoc = {
    _id: '_design/typeIndex',
    views: {
        by_type: {
            map: function (doc) { emit(doc.type); }.toString()
        }
    }
};

db.put(ddoc).then(function () {
    // Persistent query criada com sucesso
});

O método .toString() ao final de nossa função map é necessário para transformar o objeto em um JSON válido.

  1. E então nós podemos usá-la de forma semelhante às queries temporárias:
db.query('typeIndex/by_type', {key: 'pessoa'}).then(function (result) {
    // retorna todos os documentos cujo valor da propriedade "type" é igual a "pessoa"
});

É interessante frisar que nossa primeira consulta realizada será relativamente lenta pois os índices só são criados quando nós realizamos uma consulta. A documentação recomenda a realização de uma consulta com limit: 0 para criar os índices sem retornar os documentos.

db.query('typeIndex/by_type', {limit: 0}).then(function (result) {
    // índices criados
});

Extra: Pouchdb-find

Agora que já conhecemos as principais formas de realizarmos consultas no Pouchdb é interessante conhecermos o Pouchdb-find, plugin que busca trazer uma forma de fazer consultas inspiradas na API do MongoDB. O plugin faz a mesma coisa que os métodos anteriormente apresentados, porém com menos código.

Para reproduzirmos o exemplo anterior utilizando o Pouchdb-find, nós precisamos criar um índice utilizando o método createIndex e então podemos utilizar o método find para realizar nossas consultas.

db.createIndex({
    index: {fields: ['type']}
}).then(function () {
    return db.find({
        selector: {type: 'pessoa'}
    });
}).then(function(result){
    // retorna todos os documentos cujo valor da propriedade "type" é igual a "pessoa"
});

O Pouchdb-find ainda não possui todos os métodos que necessitamos em muitas ocasiões (eu mesmo já precisei utilizar o $in), mas já é possível utilizá-lo. Para maiores informações acesse a documentação do plugin.

Conclusão

Existem muitas formas de realizarmos consultas no PouchDB e cabe ao desenvolvedor ter a sensibilidade de saber qual a melhor opção para cada caso, mas para sabermos qual a melhor opção é necessário conhecermos cada uma delas e espero ter conseguido apresentar o tema e esclarecer possíveis dúvidas.

Agora que já conhecemos as Persistent queries no próximo artigo a respeito do PouchDB irei apresentar as formas de sincronizar apenas determinados documentos no nosso banco de dados.

persistencejs

Dicas para trabalhar com o Ionic Framework – Persistence.js Migrations

Mesmo com todas as vantagens de bancos não relacionais como o PouchDB ainda existem aqueles que ainda preferem os bancos relacionais por fatores como familiaridade, curva de aprendizagem, equipe, backend já é relacional, etc. ou simplesmente por que o não relacional não atende ou não é a melhor indicação para o projeto em questão.

Quando essas pessoas vão trabalhar com o Ionic elas normalmente optam pelo plugin de SQLite do Cordova e não tem nada de errado nisso, porém se formos fazer um paralelo com desenvolvimento em Java ou PHP é como se utilizássemos os drives nativos de comunicação com o banco de dados sem utilizar um ORM como o Hibernate ou o Doctrine.

E essa forma de trabalhar sem um ORM pode acarretar alguns problemas quando nossos bancos mudam. Quando projetamos um banco de dados relacional nós esperamos que ele nunca vá mudar, pois estamos criando estruturas para armazenar os dados muito rígidas e altamente acopladas umas as outras. No entanto, conforme a aplicação vai crescendo normalmente é necessário alterar essa estrutura ou schema de dados que definimos e nesse momento iremos precisar de alguma ferramenta para fazer a migração do antigo schema de dados para o novo. E esse é um dos problemas que os ORM’s buscam solucionar.

E durante um dos meus cursos na Futuring sobre Apps híbridos eu fui questionado por meus alunos sobre a melhor forma de trabalhar com dados relacionais no Ionic pensando nessas migrações de dados. E essa pergunta me levou a fazer uma pesquisa para encontrar essa melhor forma para que eu pudesse responder aquela pergunta. Foi então que eu encontrei o persistence.js.

persistence.js

O persistence.js é um ORM escrito em javascript que trabalha de forma assíncrona. Ele pode ser usado tanto no navegador quanto no Node.js. Quem já trabalhou ou trabalha com algum tipo de ORM não terá dificuldades para se adaptar a ele. Mas o que torna essa biblioteca a melhor opção para quem quer trabalhar com bancos de dados relacionais são seus plugins. Basicamente ela possui 3 plugins que me chamaram a atenção:

  • persistence.search.js: adiciona a possibilidade realizar consultas do tipo FullText em nossos registros.
  • persistence.migrations.js: adiciona suporte a migração de dados (alterações no schema do banco de dados).
  • persistence.sync.js: implementa a capacidade de sincronização com servidores remotos desde que o backend respeite algumas regras.

Um outro ponto que vale a pena ressaltar é o fato de que quando o plugin de SQLite do Cordova está instalado em nossa aplicação, automaticamente o pesistence.js irá tentar trabalhar com ele (assim como o PouchDB) e se por algum motivo ele não consiguir, irá fazer o fallback para o WebSQL.

Embora a biblioteca possua inúmeros outros recursos especificamente nesse artigo iremos focar na migração de dados.

Migrations

Agora que já conhecemos a biblioteca e os motivos pelos quais eventualmente precisaremos realizar migrações de dados que tal darmos uma olhada em como fazê-lo com o persistence.js?

A primeira coisa que temos que saber é que o plugin de migrations foi inspirado nas migrations do Ruby on Rails, logo, quem já utiliza o Rails se sentirá ainda mais confortável com a ferramenta.

persistence.defineMigration(1, {
  up: function() {
    this.createTable('Feed', function(t){
      t.text('nome');
      t.text('conteudo');
      t.text('url');
      t.boolean('favoritado');
    });
  },
  down: function() {
    this.dropTable('Feed');
  }
});

De forma simplista uma migration pode ser definida como descrita acima, onde estamos adicionando uma tabela com o nome Feed que possui 3 colunas textuais (nome, conteudo e url respectivamente) e uma coluna booleana chamada favoritado. Uma coluna chamada id que será nossa chave primária também será adicionada, porém, como esse é um comportamento default nós não precisamos descrevê-la.

O primeiro argumento passado para nossa migration é a versão da mesma, logo, quando quisermos voltar para uma migration específica basta passar essa versão como parâmetro.

Como rodar as migrations?

Primeiro é necessário iniciar o plugin de migrations:

persistence.migrations.init(function() {
  // Callback que será executada após a inicialização
});

Depois precisaremos carregar nossas migrations. A documentação recomenda utilizar o RequireJS para isso. Após carregá-las nós devemos executá-las como no exemplo a seguir:

requirejs(['migrations'], function() {
    // Irá executar as migrações para a versão mais recente
    persistence.migrate(function(){
        // Callback que será executada após migração ser concluída
    });
});

Caso seja necessário voltar versões o primeiro parâmetro do método migrate será o número da versão a ser restaurada:

requirejs(['migrations'], function() {
    // Irá executar as migrações para a versão 1
    persistence.migrate(1, function(){
        // Callback que será executada após migração ser concluída
    });
});

Agora vocês conhecem o persistence.js e como realizar migrations com ele. O plugin de migrations também pode ser utilizado para tratar dados inconsistente ou popular novos campos como vocês podem consultar em sua documentação.

Enfim

Escolhi escrever sobre bancos de dados relacionais dessa vez pois notei que é um tópico recorrente na comunidade Ionic Framework Brasil. Nesse artigo vimos que é fácil trabalhar com dados relacionais em projetos Cordova e em meu próximo artigo sobre o persistence.js irei dissecar a forma de realizar sincronização em bancos de dados relacionais.

pouchdb

Dicas para trabalhar com o Ionic Framework – PouchDB

Originalmente essa era pra ser só mais uma dica para se trabalhar com o Ionic, e viria acompanhada de diversas outras, mas eu decidi discorrer sobre o assunto para poder argumentar e expressar os motivos pelos quais eu escolhi trabalhar com o PouchDB em meus apps.

Introdução

Quando pensamos em persistência de dados em dispositivos móveis logo pensamos no SQLite, mas as vezes uma mudança de paradigma pode tornar o desenvolvimento mais fácil.

Quando comecei a desenvolver com o Ionic eu tinha alguns requisitos básicos e um deles era sincronização com servidor sem complicação. Então eu queria ser capaz de persistir os dados no dispositivo móvel e quando necessário sincronizar com o servidor sem esquentar muito a cabeça com estratégias de sincronização.

E foi nesse contexto que conheci o PouchDB. O PouchDB é um SGBD não relacional, o famoso NoSQL, baseado em documentos que busca ser uma implementação em JavaScript do CouchdDB. O motivo dele seguir a mesma API do CouchDB é o fato dele ter sido concebido para sincronizar com o CouchDB como se fosse uma outra instância do CouchDB.

Como o PouchDB faz uso do IndexedDB e/ou do WebSQL para armazenamento dos dados ele possui uma limitação de tamanho máximo determinada por cada browser. Por isso é interessante utilizarmos ele em conjunto com o SQLite, pois o SQLite não possui tais limitações. O PouchDB irá utilizar o SQLite automaticamente caso você tenha instalado o plugin do mesmo através do comando:

cordova plugin add io.litehelpers.cordova.sqlitestorage

E para instalar o PouchDB nós utilizamos o bower:

bower install pouchdb --save

E depois importamos o mesmo em nosso index.html:

<script src="lib/pouchdb/dist/pouchdb.min.js"></script>

Trabalhar com o PouchDB é relativamente fácil e ele possui um guia básico bem completo em seu site.

Quem já trabalhou com o Mongo não terá muitos problemas para se habituar com o PouchDB, as diferenças mais gritantes entre os dois são: o protocolo de comunicação, o CouchDB utiliza o protocolo HTTP para realizar suas operações; e o fato do CouchDB não possuir Collections como o Mongo, ou seja, todos os documentos são armazenados no mesmo “nível” e nós precisamos de uma outra ferramenta para identificar o tipo daquele documento. E é aí que entram as Persistent Queries.

Mas isso é conversa pra um artigo mais aprofundado sobre o assunto. E o foco desse texto é mostrar o quão simples é sincronizar dois bancos de dados utilizando o PouchDB.

Atualmente o Couchbase e o Cloudant são outras plataformas de banco de dados que compartilham o mesmo protocolo de sincronização. Isso significa que o PouchDB também é capaz de sincronizar com essas plataformas.

Sincronização

É possível sincronizar os dados entre dois ou mais bancos através da replicação, ou replication, onde estamos replicando um banco para outro banco, ou seja, criando uma “cópia”.

var localDB = new PouchDB('mylocaldb')
var remoteDB = new PouchDB('http://localhost:5984/myremotedb')

localDB.replicate.to(remoteDB).on('complete', function () {
  // Sincronizado!
}).on('error', function (err) {
  // Ops, tivemos um erro!
});

No exemplo acima estamos replicando os dados do banco local para o banco remoto, porém o contrário (do remoto para o local não acontece). Replicamos do remoto para o local utilizando o replicate.from:

var localDB = new PouchDB('mylocaldb');
var remoteDB = new PouchDB('http://localhost:5984/myremotedb');

localDB.replicate.from(remoteDB).on('complete', function () {
  // Sincronizado!
}).on('error', function (err) {
  // Ops, tivemos um erro!
});

Nesse caso estamos replicando do banco remoto para o local. Mas e se quisermos sincronizar tudo? Enviar as alterações do local para o remoto e as alterações do remoto para o local?

localDB.replicate.to(remoteDB);
localDB.replicate.from(remoteDB);

No entanto, o PouchDB possui um atalho pra tornar ainda mais fácil esse caso específico:

localDB.sync(remoteDB).on('complete', function () {
  // Sincronizado!
}).on('error', function (err) {
  // Ops, tivemos um erro!
});

Mas e se quisermos manter os dois bancos sempre sincronizados?

localDB.sync(remoteDB, {
  live: true
}).on('change', function () {
  // Ocorreu uma alteração!
}).on('error', function (err) {
  // Ops, tivemos um erro!
});

No entanto, quando o usuário ficar offline nós teremos um erro e a sincronização será quebrada. Para resolver esse problema nós precisamos dizer ao PouchDB para tentar novamente:

localDB.sync(remoteDB, {
  live: true,
  retry: true
}).on('change', function (change) {
  // Ocorreu uma alteração!
}).on('paused', function (info) {
  // replicação pausada, normalmente por que o usuário está offline
}).on('active', function (info) {
  // replicação continuada
}).on('error', function (err) {
  // erro desconhecido, normalmente não deveria acontecer
})));

Também é possível sincronizar apenas partes específicas do nosso banco utilizando as Persistent Queries, mas vou deixar para uma próxima oportunidade.

Conclusão

Uma abordagem não relacional pode não encaixar em todos os tipos de aplicações e normalmente essa mudança de paradigma em nossa forma de desenvolver leva certo tempo para assimilação, então temos que saber pesar as vantagens e desvantagens de utilizar de cada tipo de banco de dados para podermos extrair o melhor de cada.

Para entender melhor como funciona essa abordagem dos bancos não relacionais baseados em documentos eu recomendo esses artigos do Jean Carlo Nascimento (@osuissa):

MongoDb – Como mudar seu jeito relacional de pensar – Parte 1

MongoDb – Como mudar seu jeito relacional de pensar – Parte 2

Aprendendo MongoDb a partir do Relacional

I know jQuery

Muito além do jQuery

Por incrível que pareça eu comecei esse artigo no dia 8 de Agosto de 2013 e ele ficou perdido no limbo do meu Google Drive desde então, faltavam alguns exemplos e uma conclusão. Então, eis que o encontrei ao procurar um outro artigo que estava perdido por lá.

Resolvi finalizar e estou postando com mais de 1 ano de atraso mesmo.


Bom galera já tinha um tempo que eu queria falar sobre esse assunto, mas ainda faltava um incentivo para soltar a boiada. E então eis que aconteceu, dispararam o gatilho. E aconteceu em um grupo de desenvolvimento do facebook, o jQuery Brasil. Um iniciante perguntou quais os requisitos necessários de JavaScript para aprender jQuery e um membro respondeu que não era preciso saber JavaScript para aprender. E na hora eu fiquei indignado, mas pensando bem eu acho que ele está certo, muita gente sabe jQuery e não sabe JavaScript. Mas se o jQuery é uma biblioteca JavaScript, logo, quem sabe jQuery sabe JavaScript, certo?

Em teoria era isso que deveria acontecer, mas as bibliotecas e os frameworks existem para criar uma camada de abstração maior e simplificar o trabalho, então em grande parte das vezes as pessoas não aprendem a linguagem e sim a ferramenta, que pode ser uma biblioteca, um framework, uma sdk, etc.

Então esse artigo é dedicado para o pessoal que sabe jQuery e que provavelmente não sofreu para aprender a trabalhar com o DOM (Document Object Model) ou fazer uma requisição ajax usando o XMLHttpRequest.

Primeiramente eu gostaria de lembrá-los que para tudo que o jQuery faz existe algo equivalente em JavaScript, até por que jQuery é JavaScript (sim, vou falar isso várias vezes). Mas eu não vou começar pelo básico, até por que o básico você encontra facilmente no google. Eu recomendo o ótimo artigo do Tárcio Zemel entitulado “Equivalentes nativos de JavaScript para funções jQuery“, onde o autor mostra algumas dessas equivalências.

Quando usar jQuery?

Uma das inspirações para esse artigo foi o artigo I know jQuery. Now what? do Remy Sharp, onde ele levanta alguns pontos sobre quando não usar jQuery e demonstra algumas soluções de problemas sem a utilização de jQuery. Este artigo é mais simples que o do Remy Sharp por isso recomendo a leitura dele, no entanto, é igualmente opinativo.

Bom, esse artigo já está ficando grande e chato e eu nem comecei a parte técnica ainda, então chega de enrolação.

Quando a complexibilidade supera a facilidade

Quando uma grande parte do seu público alvo não utiliza navegadores de última geração fica complicado trabalhar com JavaScript puro já que algumas funções do JavaScript podem não ser compatíveis por terem sido implementadas depois. O pessoal da BBC, no artigo Cutting the mustard, divide os navegadores em dois grandes grupos (Navegadores HTML5 e Navegadores HTML4) para poder exibir uma interface mais rica ao usuário dependendo dos recursos suportados pelo navegador do mesmo.

Por padrão o IE8 não suporta o método addEventListener que é um método que fica “escutando” um evento e é executado quando o evento do objeto é acionado. Está presente em todos os navegadores lançados desde 2007, menos no IE8. E isso, teoricamente, implica que se o navegador do cliente suportar esse recurso então esse navegador possui um melhor suporte aos padrões que o IE8.

Logo, se o seu público alvo utiliza esse navegador você deve utilizar jQuery. O que não quer dizer que todos os projetos que não utilizem jQuery ou outras bibliotecas não irão funcionar no IE8 e sim que você deve começar o projeto de forma simples e sem gambiarras.

Quando é necessário agilidade

Isso é uma das principais causas pelo qual os desenvolvedores utilizam bibliotecas e frameworks, agilizar o desenvolvimento. E o jQuery faz isso muito bem. Então não precisamos ficar reinventando a roda.

Como sobreviver sem jQuery?

Desenvolver sem o jQuery pode soar para alguns como reinventar a roda. Mas existem algumas alternativas para se escrever a aplicação sem ele e sem perder tempo.

document.ready

O document.ready, ou simplesmente $(function), é utilizado para executar o JavaScript após o navegador ter compilado o DOM. Fazemos isso pois o JavaScript bloqueia a renderização, mas como estamos falando de alternativas que tal colocar o seu código JavaScript antes da tag ? Dessa forma o DOM já estará compilado.

Caso essa simples ação ainda não resolva o seu problema, o que eu duvido muito, você ainda poderá utilizar o DOMContentLoaded. O DOMContentLoaded é suportado pela maioria dos navegadores.

document.addEventListener("DOMContentLoaded", function(event) {
  //do work
});

querySelectorAll

Anos atrás, quando o jQuery surgiu, ele trouxe uma revolução ao modo de utilizar JavaScript pelo simples fato dele ter simplificado, de forma significativa, a tarefa de selecionar os elementos com os quais queremos trabalhar. Porém o JavaScript evoluiu e hoje nós temos uma maneira igualmente fácil e nativa. É o querySelectorAll que é usado da seguinte forma:

var matches=document.querySelectorAll("div.nota, div.alerta");

Classes

Frequentemente costumamos trabalhar com classes no JavaScript, tanto para selecionar um elemento, quanto para fazer gets e sets do atributoclass em elementos. Normalmente acabamos usando o jQuery. Mas que tal usar as propriedades className e classList?

Formulários

Antigamente para fazer a validação de formulários no navegador nós sempre utilizávamos JavaScript, não existia outra opção. Porém o HTML também evoluiu e atualmente é possível fazer validação de formulários utilizando apenas atributos nos elementos HTML. É possível validar emails com o input do tipo email, definir campos obrigatórios com o atributo required e fazer validações com expressões regulares com o atributo pattern, nos permitindo assim, validar telefones, CPFs, CNPJs, etc.

Animações

O CSS também evoluiu e hoje podemos fazer animações com ele através da propriedade animate, como visto nesse artigo da MDN, e até mesmo utilizar aceleração hardware utilizando o translate3d da propriedade transform como visto nesse artigo do Paul Irish. 
Eu, particularmente, uso bastante essas duas características do CSS3 para deixar as animações nos dispositivos móveis mais fluídas.

Conclusão

Hoje é possível ser produtivo sem utilizar jQuery que embora seja muito prático muitas vezes não é necessário, pois muitas vezes carregamos uma biblioteca inteira para utilizar apenas alguns métodos que já possuem APIs nativas disponíves no JavaScript ou alternativas melhores, mais simples e mais performáticas no HTML e no CSS.

Então sempre que o jQuery não for necessário, evite-o ;)