No ano passado, escrevi um artigo que explicava como sincronizar entre plataformas e o Couchbase apenas com AngularJS e PouchDB. Naquela época, o AngularJS estava em alta e provavelmente era minha estrutura favorita de todos os tempos. Um ano depois, o AngularJS ainda existe, mas está sendo lentamente substituído pelo Angular 2, a próxima versão da popular estrutura. Depois de usar o Angular 2, olho para o AngularJS 1.0 e me pergunto o que estava passando pela minha cabeça, porque o Angular 2 é pura maravilha.
Desta vez, veremos como criar um aplicativo da Web simples que sincroniza usando apenas o Angular 2, o PouchDB e o Couchbase Mobile.
Os requisitos
Os poucos requisitos para ter sucesso com esse projeto são os seguintes:
- Node.js 4.0+
- CLI do Angular 2
- Tipificações
- Gateway de sincronização do Couchbase
A CLI do Angular 2 pode ser instalada usando o NPM (Node Package Manager), que está incluído no Node.js. O NPM também é usado quando se trata de reunir várias dependências de projeto. Não usaremos o Couchbase Server neste exemplo, mas poderíamos. Em vez disso, usaremos apenas o Couchbase Sync Gateway e seus recursos de prototipagem.
Configuração do Couchbase Sync Gateway
O Couchbase Sync Gateway é necessário para lidar com toda a sincronização. Sem ele, temos apenas armazenamento local em nosso aplicativo Web e, honestamente, há soluções muito melhores do que o PouchDB se você quiser apenas armazenamento local.
Baixar o Sync Gateway mais recente e implemente-o com a seguinte configuração:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
{ "log":["CRUD+", "REST+", "Mudanças+", "Anexar+"], "bancos de dados": { "exemplo": { "servidor":"walrus:", "sync":` função (doc) { canal (doc.canais); } `, "usuários": { "CONVIDADO": { "desativado": falso, "admin_channels": ["*"] } } } }, "CORS": { "Origem": ["http://localhost:4200"], "LoginOrigin": ["http://localhost:4200"], "Headers" (Cabeçalhos): ["Content-Type"], "MaxAge": 17280000 } } |
Salve isso em um arquivo chamado sync-gateway-config.json se você quiser. Esta é uma configuração muito básica em que estamos usando um banco de dados na memória chamado exemplo. Não há permissões de leitura e gravação, portanto, vale tudo. Na parte inferior dessa configuração, temos alguns itens relacionados ao compartilhamento de recursos entre origens (CORS). Como estaremos servindo nosso aplicativo localmente, precisamos identificar isso para evitar erros de JavaScript.
Criando um novo projeto Angular 2
Para facilitar a compreensão, vamos criar um projeto com um design incrivelmente simples. Vamos criar um aplicativo simples de gerenciamento de usuários, no qual podemos adicionar pessoas e elas serão sincronizadas para onde você quiser. Por exemplo, veja a animação abaixo:

Na animação acima, temos dois navegadores da Web. Adicione uma pessoa a um deles e ela será sincronizada com o outro. Isso é algo que seria particularmente difícil sem o Couchbase.
Vamos começar criando um novo projeto do Angular 2 usando a CLI do Angular. Execute o seguinte em seu prompt de comando (Windows) ou terminal (Mac e Linux):
1 |
ng novo Projeto PouchDB |
Pode demorar um pouco, mas quando o projeto for criado, queremos instalar o PouchDB e todas as dependências necessárias. Execute o seguinte para instalar o PouchDB mais recente:
1 |
npm instalar bolsadb --salvar |
Os mantenedores do PouchDB têm emoções contraditórias quando se trata de TypeScript, uma tecnologia essencial para o desenvolvimento do Angular 2. Por esse motivo, não há definições de tipo confiáveis disponíveis. No entanto, isso não é um grande problema.
Usando o Typings, vamos instalar o seguinte:
1 |
tipificações instalar dt~exigir --salvar --global |
As definições de tipo acima nos permitirão usar o exigir
no TypeScript. Com ela, podemos importar o PouchDB baixado para o nosso projeto.
Nesse ponto, podemos começar a desenvolver nosso projeto!
Desenvolvimento de um provedor Angular 2 para manter os dados
Ao trabalhar com dados, é sempre uma boa ideia criar um serviço do Angular 2, também conhecido como provedor. Isso nos permite ter uma instância singleton e uma classe separada do restante do nosso código. Isso é ótimo do ponto de vista da manutenção.
Você pode criar o provedor manualmente ou usando a CLI. Na CLI, execute o seguinte:
1 |
ng g serviço bolsadb |
Dois arquivos devem ser criados no diretório src/app diretório. Você deve ter um diretório pouchdb.service.ts e um pouchdb.service.spec.ts arquivo. Eles podem ter um nome semelhante, mas o nome não é realmente importante.
O especificação é um arquivo de teste de unidade, algo com que não nos preocuparemos neste exemplo específico. Em vez disso, vamos começar a desenvolver no arquivo pouchdb.service.ts arquivo.
Abra o arquivo e inclua o seguinte código TypeScript:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
importação { Injetável, Emissor de eventos } de '@angular/core'; var PouchDB = exigir("pouchdb"); @Injetável() exportação classe Serviço PouchDBService { privado isInstantiated: booleano; privado banco de dados: qualquer; privado ouvinte: Emissor de eventos = novo Emissor de eventos(); público construtor() { } público buscar() { } público obter(id: string) { } público colocar(id: string, documento: qualquer) { } público sincronização(remoto: string) { } público getChangeListener() { } } |
Antes de começarmos a preencher cada um dos métodos, vamos descobrir o que estamos tentando fazer. Queremos apenas uma instância de banco de dados enquanto o aplicativo estiver em execução. Isso pode ser feito por meio do método construtor
método:
1 2 3 4 5 6 |
público construtor() { se(!este.isInstantiated) { este.banco de dados = novo PouchDB("nraboy"); este.isInstantiated = verdadeiro; } } |
Nesse caso, o banco de dados local é chamado de garoto. Agora, o PouchDB tem uma API realmente excelente. Ela só não é otimizada para o Angular 2, o que significa que trabalhar com o PouchDB em seu estado original em nosso projeto pode ter alguns problemas.
No cenário em que queremos buscar todos os documentos locais, podemos fazer algo assim:
1 2 3 |
público buscar() { retorno este.banco de dados.todos os documentos({incluir_docs: verdadeiro}); } |
Usamos o incluir_docs
para que os documentos sejam incluídos nos resultados em vez de apenas seus valores de id. Então, talvez queiramos obter apenas um único documento. Faríamos algo como o seguinte:
1 2 3 |
público obter(id: string) { retorno este.banco de dados.obter(id); } |
Até agora, usamos a API do PouchDB exatamente como a documentação recomenda. Que tal mudar um pouco? A API oferece uma maneira de criar ou atualizar documentos, mas vamos combinar isso em um único método?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
público colocar(id: string, documento: qualquer) { documento._id = id; retorno este.obter(id).então(resultado => { documento._rev = resultado._rev; retorno este.banco de dados.colocar(documento); }, erro => { se(erro.status == "404") { retorno este.banco de dados.colocar(documento); } mais { retorno novo Promessa((resolver, rejeitar) => { rejeitar(erro); }); } }); } |
O acima colocar
verificará se um documento existe com base em seu ID. Se existir, copie a revisão para os novos dados e salve-a novamente. Se o documento não existir, crie-o sem uma revisão.
Você provavelmente notou o Emissor de eventos
que foi incluído próximo ao topo. Isso é necessário para a assinatura de eventos de alteração nas páginas do Angular 2. Veja o seguinte sincronização
por exemplo:
1 2 3 4 5 6 7 8 |
público sincronização(remoto: string) { deixar banco de dados remoto = novo PouchDB(remoto); este.banco de dados.sincronização(banco de dados remoto, { ao vivo: verdadeiro }).em("mudança, mudança => { este.ouvinte.emitir(mudança); }); } |
Aqui estamos definindo uma fonte de dados remota, que será o Couchbase Sync Gateway, optando por fazer uma sincronização bidirecional em tempo real e emitindo as alterações sempre que elas forem descobertas.
O ouvinte de alterações pode ser acessado em todas as páginas, acessando o seguinte:
1 2 3 |
público getChangeListener() { retorno este.ouvinte; } |
Com isso, acabaríamos assinando o ouvinte.
Embora o provedor tenha sido criado, ele ainda não está disponível em todas as páginas. Precisamos importá-lo para a seção @NgModule
encontrado no src/app/app.module.ts arquivo. Esse arquivo será parecido com o seguinte:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
importação { Módulo do navegador } de '@angular/platform-browser'; importação { NgModule } de '@angular/core'; importação { FormsModule } de '@angular/forms'; importação { HttpModule } de '@angular/http'; importação { Componente de aplicativo } de './app.component'; importação { Serviço PouchDBService } de "./pouchdb.service"; @NgModule({ declarações: [ Componente de aplicativo ], importações: [ Módulo do navegador, FormsModule, HttpModule ], provedores: [Serviço PouchDBService], bootstrap: [Componente de aplicativo] }) exportação classe AppModule { } |
Observe que importamos a classe do provedor e a adicionamos à classe provedores
matriz do @NgModule
bloco. Agora podemos usar o provedor em todo o aplicativo.
Aplicação do provedor de banco de dados no aplicativo Angular 2
Para manter as coisas simples, este será um aplicativo de página única. Passaremos o resto de nosso tempo na seção src/app/app.component.ts e src/app/app.component.html arquivos.
Abra o arquivo src/app/app.component.ts e inclua o seguinte código TypeScript:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
importação { Componente, OnInit, NgZone } de '@angular/core'; importação { Serviço PouchDBService } de "./pouchdb.service"; @Componente({ seletor: 'raiz do aplicativo', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) exportação classe Componente de aplicativo implementa OnInit { público pessoas: Matriz; público formulário: qualquer; público construtor(privado banco de dados: Serviço PouchDBService, privado zona: NgZone) { este.pessoas = []; este.formulário = { "nome de usuário": "", "firstname": "", "lastname" (sobrenome): "" } } público ngOnInit() { } público inserir() { } } |
Você notará que estamos importando o Serviço PouchDBService
além de alguns outros aspectos. Vamos explorá-los à medida que os encontrarmos.
Dentro do Componente de aplicativo
temos uma matriz pública chamada pessoas
que será vinculado à nossa interface do usuário. Ele conterá todos os dados que estão sendo salvos e sincronizados. O formulário
representará um objeto que contém cada um dos nossos elementos de formulário. Poderíamos dividi-lo em cadeias de caracteres, mas um objeto é mais conveniente porque acabaremos salvando o objeto inteiro como um documento no Couchbase.
No construtor
injetamos nosso serviço PouchDB e o método NgZone
. Usamos NgZone
para atualizar a zona do Angular 2, que, às vezes, fica fora de forma ao trabalhar com eventos e outros tipos de ouvintes. Não é um problema muito grande, como você verá em breve. Por fim, a seção construtor
inicializa nossas duas variáveis.
É uma prática ruim carregar dados no construtor
portanto, em vez disso, usamos o método ngOnInit
método:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
público ngOnInit() { este.banco de dados.sincronização("http://localhost:4984/example"); este.banco de dados.getChangeListener().assinar(dados => { para(deixar i = 0; i { este.pessoas.empurrar(dados.mudança.documentos[i]); }); } }); este.banco de dados.buscar().então(resultado => { este.pessoas = []; para(deixar i = 0; i { console.erro(erro); }); } |
A maior parte do trabalho do aplicativo grunt acontece acima. No código acima, estamos definindo o host remoto do Sync Gateway e o nome do banco de dados. Ele não precisa ser igual ao do nosso banco de dados local. Em seguida, nos inscrevemos em nosso ouvinte e percorremos as alterações à medida que elas chegam. Todas as alterações são adicionadas à matriz pública dentro de um NgZone
para que sejam atualizados na tela.
Como os eventos de alteração são acionados apenas quando ocorrem, precisamos buscar todos os documentos quando inicializamos o aplicativo pela primeira vez.
Isso nos leva ao inserir
método:
1 2 3 4 5 6 7 8 9 10 |
público inserir() { se(este.formulário.nome de usuário &lificador;&lificador; este.formulário.primeiro nome &lificador;&lificador; este.formulário.sobrenome) { este.banco de dados.colocar(este.formulário.nome de usuário, este.formulário); este.formulário = { "nome de usuário": "", "firstname": "", "lastname" (sobrenome): "" } } } |
Se os elementos do formulário não estiverem em branco, poderemos salvar os dados como um documento no banco de dados. Depois de salvos, o evento de alteração será acionado e os dados serão adicionados à matriz pública e exibidos na tela.
Tudo isso é feito sem interrupções entre o aplicativo da Web e o Couchbase Sync Gateway.
Levando-o para um test drive
Há muito o que aprender neste guia sobre o Angular 2 e o PouchDB. I fez o upload de um projeto funcional para o GitHub se você quiser dar uma olhada nele.
Faça o download do projeto e execute o seguinte em seu Terminal ou Prompt de Comando:
1 |
npm instalar |
O comando acima obterá todas as dependências do projeto. Certifique-se de atualizar o src/app/app.component.ts para refletir o nome de host correto do Sync Gateway e você estará pronto para começar.
O projeto pode ser executado com a execução:
1 |
ng servir |
Enquanto estiver em funcionamento, o projeto pode ser acessado em http://localhost:4200.
Conclusão
Você acabou de ver como criar um aplicativo da Web simples que sincroniza dados usando o PouchDB e o Couchbase. Este é um aplicativo Angular 2 que é um passo à frente do aplicativo guia que escrevi no ano passado que usava o AngularJS 1.0.
Olá, amigo! Obrigado pelo tutorial, estou muito agradecido. Só uma pergunta rápida: estou tentando instalar o "require" de acordo com o comando sugerido, mas não estou tendo muita sorte. Estou executando o Angular 5.
typings install dt~require -save -global
Em "ng serve", obtenho o seguinte:
"ERRO em src/app/services/pouchdb.service.ts(3,17): erro TS2304: não é possível encontrar o nome 'require'."
Alguma ideia?
Estou tendo o mesmo problema! Consegui encontrar alguns pacotes npm desatualizados, incluindo o antigo angular-cli que precisou ser removido e substituído por @angular/cli. Estou me perguntando se é algo semelhante porque, quando instalei as tipagens, recebi um aviso de que elas foram descontinuadas em favor do uso de @types. Vou tentar isso...
O PouchDB agora funciona com TypeScript, sem usar require.js. Consulte a documentação do PouchDB: https://pouchdb.com/guides/setup-pouchdb.html#typescript
Passos:
1. Instalar o PoutchDB
> npm install pouchdb -save
2. Instalar os tipos de PouchDB
> npm install pouchdb @types/pouchdb
3. Edite o tsconfig.json e ative as importações padrão sintéticas
{
"compilerOptions": {
"allowSyntheticDefaultImports": true
}
}
4. Importe o PouchDB em sua classe Typescript: pouchdb.service.ts.
importar PouchDB de 'pouchdb';