Aaron Ullal é um Desenvolvedor freelancer na FactoryMind, amante das pessoas e da vida, vivendo em beta permanente. Ele é um desenvolvedor full stack baseado na bela cidade de Trento, na Itália.
TI apaixonada por mais do que 7 anos, ele está atualmente focado no desenvolvimento móvel usando o NativeSe projetar soluções arquitetônicas para plataformas complexas.

O Couchbase é uma ótima ferramenta para manter os dados dentro do nosso aplicativo. Se você nunca ouviu falar dele, trata-se de um armazenamento de objetos de documentos que permite salvar seus dados.
Se você estiver usando NativeScript-Angular, já existem alguns tutoriais excelentes sobre como começar e alguns recursos mais avançados. Neste artigo, vamos nos concentrar no NativeScript com TypeScript.
O que abordaremos
Neste artigo simples, veremos como salvar e recuperar algumas informações do usuário:
- Instalação do Couchbase
- Criação de uma classe TypeScript que fornece uma camada de abstração sobre o Couchbase, tornando-o muito mais fácil de usar (sem instruções complexas para inserção e recuperação de dados)
- Implementação do padrão singleton na classe
Ao final do tutorial, seremos capazes de usar instruções como
|
1 2 3 |
userSettings.name = "Frank" //boom! written in our db text=”{{ userSettings.name }}” //boom! read from our db |
sem ter que escrever consultas extensas para ler e escrever!
Além disso, todo o código deste artigo está disponível em este repositório do GitHub. Sinta-se à vontade para fazer o clone e brincar com ele.
Sem mais delongas, vamos começar!
Instalar o plug-in do Couchbase
Vamos criar um novo aplicativo NativeScript usando o modelo TypeScript.
|
1 |
tns create ns-couchbase-demo --tsc |
Na pasta raiz de nosso projeto NativeScript recém-criado, vamos dar o seguinte comando para adicionar o plug-in e salvá-lo em nossa lista de dependências:
|
1 |
tns plugin add nativescript-couchbase |
Depois que o plug-in for instalado com êxito, vamos criar uma classe que será usada para armazenar e recuperar as informações do usuário de que precisamos.
Classe de camada de abstração
OPCIONAL: Normalmente, é uma boa ideia criar uma interface para definir as propriedades que precisamos incluir.
Navegue até a pasta app do nosso projeto e crie uma pasta models. Aqui, definiremos nosso arquivo Models.ts (app/models/Models.ts):
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
export module Models{ export interface IUserSettings { username :string; fullname :string; email :string; } } |
Depois de definir nossa interface, vamos criar a classe que de fato a implementa. Chamaremos a classe de UserSettings.
Dentro dessa classe, também precisamos armazenar as informações relacionadas ao nosso banco de dados Couchbase.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
let CouchBaseModule = require("nativescript-couchbase"); import { Models } from '../models/Models'; export class UserSettings implements Models.IUserSettings{ private DATABASE_NAME = 'appname-db'; private USER_SETTINGS_DOC_ID = 'usersettings'; private _database; private _userSettingsDocument: Models.IUserSettings; private _userSettingsObj: Models.IUserSettings; private _instance :UserSettings; } |
Vamos detalhar isso:
Nas duas primeiras linhas, solicitamos o plug-in do Couchbase que instalamos anteriormente, juntamente com o módulo Models que usaremos para aproveitar a tipagem forte do TypeScript.
Em seguida, definimos algumas outras propriedades privadas que usaremos posteriormente:
DATABASE_NAME: Esse é o nome do banco de dados. Certifique-se de que ele não contenha letras maiúsculas (e que tenha menos de 240 caracteres).
USER_SETTINGS_DOC_ID: nome do documento do Couchbase.
banco de dados: Essa é a instância do conector de banco de dados.
_userSettingsDocument: O Couchbase é um banco de dados não orientado a documentos. Ele não tem tabelas, mas usa uma entidade de armazenamento primário conhecida como Document. Basicamente, um documento é uma coleção de pares chave-valor, em que o valor pode ser praticamente qualquer coisa (cadeia de caracteres, números, matrizes etc.). É isso que torna o Couchbase a solução número um se você precisar armazenar uma variedade de dados não relacionais: simplicidade e flexibilidade.
_userSettingsObj: este é o objeto JavaScript que usaremos para sincronizar as informações com o documento do Couchbase.
Abordaremos o motivo pelo qual declaramos a variável _instance mais tarde, quando implementarmos o padrão singleton.
Agora que já cobrimos isso, vamos ver como funciona o construtor da nossa classe:
|
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 |
constructor() { this._database = new CouchBaseModule.Couchbase(this.DATABASE_NAME); this._userSettingsDocument = this._database.getDocument(this.USER_SETTINGS_DOC_ID); if (!this._userSettingsDocument) { console.log("Document does not exist yet :)"); this._userSettingsObj = { username: "", email: "", fullname: "", } this._database.createDocument(this._userSettingsObj, this.USER_SETTINGS_DOC_ID); this._userSettingsDocument = this._database.getDocument(this.USER_SETTINGS_DOC_ID); } } |
Mais uma vez, vamos ver em detalhes o que está acontecendo em nosso construtor:.
this._database = new CouchBaseModule.Couchbase(this.DATABASE_NAME);
Aqui estamos instanciando o Couchbase e informando a qual banco de dados queremos nos conectar.
this._userSettingsDocument = this._database.getDocument(this.USER_SETTINGS_DOC_ID);
Essa linha diz ao Couchbase para obter nosso documento (a coleção que usamos para armazenar nossas informações).
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
if (!this._userSettingsDocument) { console.log("Document does not exist yet :)"); this._userSettingsObj = { username: "", email: "", fullname: "", } this._database.createDocument(this._userSettingsObj, this.USER_SETTINGS_DOC_ID); this._userSettingsDocument = this._database.getDocument(this.USER_SETTINGS_DOC_ID); } |
Se o nosso documento ainda não existir (por exemplo, pela primeira vez), criaremos um objeto vazio do tipo UserSettings e também criaremos um novo documento. O segundo parâmetro que atribuímos à função createDocument é o id do documento. Se não fornecermos esse parâmetro, o Couchbase atribuirá ao documento um UUID automaticamente.
Fantástico! Agora que nosso construtor está pronto, vamos ver como podemos definir e recuperar informações sobre nosso usuário.
Vamos configurar alguns getters e setters para conseguir exatamente isso.
|
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 |
/*====== USERNAME GETTER AND SETTER ======*/ get username(): string { this._userSettingsObj = this._database.getDocument(this.USER_SETTINGS_DOC_ID ); let username = this._userSettingsObj.username; return username; } set username(value: string) { this._userSettingsObj = this._database.getDocument(this.USER_SETTINGS_DOC_ID ); this._userSettingsObj.username = value; this._database.updateDocument(this.USER_SETTINGS_DOC_ID , this._userSettingsObj); } |
Como de costume, vamos detalhar o código.
No getter, fazemos três coisas:
1) Lemos os dados de nosso documento em nosso objeto
2) Obtenha o valor desejado (nesse caso, o nome de usuário)
3) Devolva-o
Fácil de fazer :)
No setter, fazemos o seguinte:
1) Obtemos a versão mais recente de nosso userSettingsObject, lendo-a no banco de dados
2) Definimos a propriedade username como o valor passado para a função
3) Atualizamos nosso documento no banco de dados. Ao contrário da função createDocument, o primeiro parâmetro é o ID do documento e o segundo é o próprio objeto.
Mas... por que precisamos obter o getDocument primeiro? Por que não podemos simplesmente atualizar o documento com o objeto?
Fico feliz que você tenha perguntado!
Basicamente, sempre que atualizamos um documento, o Couchbase mantém o registro da alteração. Cada documento tem uma propriedade _rev; esse valor é atualizado pelo Couchbase sempre que ocorre uma operação de gravação no documento. Se tentarmos atualizar um documento com um _rev mais antigo, o Couchbase o rejeitará.
Padrão Singleton
Quem não gosta de padrões de design? Hoje vamos implementar um padrão muito simples, porém eficaz: o singleton. O padrão singleton, quando implementado corretamente, "restringe a instanciação de um classe para um objeto. Isso é útil quando é necessário exatamente um objeto para coordenar as ações em todo o sistema", que é exatamente o nosso caso.
Basicamente, garantimos que tenhamos apenas uma instância da nossa classe lendo e gravando no nosso banco de dados.
Como podemos fazer isso? Uma maneira simples de conseguir isso é com as duas etapas a seguir:
1) Tornar nosso construtor privado (Introduzido no ts 2.0)
2) Implementar um método getInstance que retorna a instância atual, se ela existir, ou uma nova instância, se for a primeira chamada
ETAPA 1:
|
1 |
private constructor() {....} |
ETAPA 2:
Agora podemos ir em frente e criar nosso método getInstance(), que terá a seguinte aparência:
|
1 2 3 4 5 6 7 8 9 10 11 |
public static getInstance() :UserSettings{ if(!this._instance){ this._instance = new UserSettings(); } return this._instance; } |
Ótimo! Abstraímos os dados e implementamos o padrão singleton. Vamos ver como podemos usar nossa nova e brilhante classe UserSettings.
Editaremos os arquivos main-page.xml e main-page.ts existentes.
Primeiro, definiremos um layout básico da interface do usuário. Para esta demonstração, usaremos um campo de texto para obter a entrada do usuário, um botão para salvar o valor no banco de dados e um rótulo para exibir o valor atual no banco de dados. Como você pode ver, se nenhum dado estiver armazenado no banco de dados, o rótulo exibirá simplesmente "Nenhum dado armazenado no banco de dados".
Esta é a aparência do nosso arquivo main-page.xml:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<Page xmlns="https://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo"> <Page.actionBar> <ActionBar title="My App" icon="" class="action-bar"> </ActionBar> </Page.actionBar> <StackLayout> <TextField hint="username" text="{{ username }}" /> <Button text="SAVE MY USERNAME" tap="onTap" class="btn btn-primary btn-active"/> <Label text="{{ dbusername || 'no data stored in the db' }}" textWrap="true"/> </StackLayout> </Page> |
Muito bem! Agora que temos um layout básico, podemos continuar adicionando alguma lógica à interface do usuário.
Esta é a aparência do nosso arquivo main-page.ts:
|
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
import { Observable } from 'data/observable'; //bind data to our view import { UserSettings } from './UserSettings'; //woohoo our amazing database interaction layer import { EventData } from 'data/observable'; //use with typescript for intellisense import { Page } from 'ui/page'; //use with typescript for intellisense let userSettings = UserSettings.getInstance(); let mainPageViewModel; class MainPageViewModel extends Observable{ public username; public dbusername constructor(){ super(); this.dbusername = userSettings.username; } } export function navigatingTo(args: EventData) { let page = <Page>args.object; mainPageViewModel = new MainPageViewModel(); page.bindingContext = mainPageViewModel; } export function onTap(){ userSettings.username = mainPageViewModel.username; mainPageViewModel.set("dbusername",userSettings.username); } |
O que fizemos? Nas primeiras linhas, apenas importamos um monte de coisas, verificando os comentários para ver o que os módulos fazem. Em seguida, temos:
|
1 |
let userSettings = UserSettings.getInstance(); |
É assim que obtemos a instância da nossa classe UserSettings.
Continuando, declaramos outra classe pequena:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class MainPageViewModel extends Observable{ public username; public dbusername constructor(){ super(); this.dbusername = userSettings.username; } } |
Como você pode ver, o MainPageViewModel estende a classe Observable. Se você não estiver familiarizado com Observables, saiba que eles são basicamente objetos JavaScript que acionam uma notificação se uma das propriedades for alterada. Você pode saber mais aqui.
Nosso modelo de visualização tem duas propriedades: nome de usuário (que vinculamos ao nosso campo de texto) e nome de usuário (que vinculamos ao nosso rótulo). Sempre que criamos uma nova instância de nossa classe, atribuímos ao dbusername qualquer valor presente em nossa instância userSettings, que, por sua vez, vai e lê os dados em nosso banco de dados (lembra-se do método get username() getter?).
|
1 2 3 4 5 6 7 8 9 |
export function navigatingTo(args: EventData) { let page = <Page>args.object; mainPageViewModel = new MainPageViewModel(); page.bindingContext = mainPageViewModel; } |
Essa função é chamada sempre que navegamos em nossa página. Instanciamos nossa classe MainPageViewModel e a atribuímos à variável mainPageViewModel e a vinculamos à nossa página. De forma simples e direta.
Por último, mas não menos importante, definimos o comportamento para o clique do botão:
|
1 2 3 4 5 6 7 |
export function onTap(){ userSettings.username = mainPageViewModel.username; mainPageViewModel.set("dbusername",userSettings.username); } |
Mais uma vez, é muito simples: Gravamos no banco de dados o valor que o campo de texto contém (mainPageViewModel.username está vinculado ao campo de texto) e, em seguida, atualizamos o valor de dbusername para atualizar o rótulo. Uma imagem vale mais que mil palavras, portanto, aqui vai um GIF!

Esta postagem faz parte do Programa de Redação da Comunidade Couchbase
Obrigado, Laura, pelo blog detalhado.
Podemos ter um blog semelhante para o Nativescript-vue couchbase sobre como persistir os dados?