Aaron Ullal es un Desarrollador freelance en FactoryMind, amante de la gente y de la vida, vive en beta permanente. Él es un desarrollador full stack con sede en la hermosa ciudad de Trento, Italia.
IT apasionada por más que 7 años, es actualmente centrado en el desarrollo móvil con NativeScript y el diseño de soluciones arquitectónicas para plataformas complejas.
Couchbase es una gran herramienta para persistir datos dentro de nuestra app. Si no has oído hablar de él, es un almacenamiento de objetos de documentos que te permite guardar tus datos.
Si estás usando NativeScript-Angular, ya hay algunos tutoriales geniales sobre cómo empezar y algunas características más avanzadas. En este artículo nos centraremos en NativeScript con TypeScript.
Qué trataremos
En este sencillo artículo vamos a ver cómo guardar y recuperar alguna información del usuario por:
- Instalación de Couchbase
- Creación de una clase TypeScript que proporciona una capa de abstracción sobre Couchbase, haciéndolo mucho más fácil de usar (sin instrucciones complejas para la inserción y recuperación de datos).
- Implementación del patrón singleton en la clase
Al final del tutorial seremos capaces de utilizar instrucciones como
1 2 3 |
userSettings.nombre = "Frank" //¡boom! escrito en nuestra db texto="{{ userSettings.nombre }}" //¡boom! leer de nuestra db |
¡sin tener que escribir extensas consultas para leer y escribir!
Además, todo el código de este artículo está disponible en este repositorio de GitHub. Siéntete libre de clonarlo y probarlo.
Sin más dilación, ¡empecemos!
Instalar el plugin Couchbase
Vamos a crear una nueva aplicación NativeScript utilizando la plantilla TypeScript.
1 |
tns crear ns-couchbase-demo --tsc |
En la carpeta raíz de nuestro recién creado proyecto NativeScript, vamos a dar el siguiente comando para añadir el plugin y guardarlo en nuestra lista de dependencias:
1 |
tns plugin añada nativescript-couchbase |
Una vez que el plugin se ha instalado correctamente, vamos a crear una clase que vamos a utilizar para almacenar y recuperar la información de usuario que necesitamos.
Clase de capa de abstracción
OPCIONAL: Suele ser una buena idea crear una interfaz para definir las propiedades que necesitamos incluir.
Navega a la carpeta app de nuestro proyecto y crea una carpeta models. Aquí definiremos nuestro archivo Models.ts (app/models/Models.ts):
1 2 3 4 5 6 7 8 9 10 11 12 13 |
exportar módulo Modelos{ exportar interfaz IUserSettings { nombre de usuario :cadena; nombre completo :cadena; correo electrónico :cadena; } } |
Una vez definida nuestra interfaz, vamos a crear la clase que la implementa. Llamaremos a la clase UserSettings.
Dentro de esta clase también necesitamos almacenar la información relacionada con nuestra base de datos Couchbase.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
deje CouchBaseModule = requiere("nativescript-couchbase"); importar { Modelos } de '../modelos/Modelos'; exportar clase Configuración de usuario implementa Modelos.IUserSettings{ privado NOMBRE_BASE_DE_DATOS = appname-db; privado USER_SETTINGS_DOC_ID = 'usersettings'; privado Base de datos; privado Documento de configuración de usuario: Modelos.IUserSettings; privado _userSettingsObj: Modelos.IUserSettings; privado _instancia :Configuración de usuario; } |
Vamos a desglosarlo:
En las dos primeras líneas requerimos el plugin de Couchbase que instalamos previamente junto con el módulo Models que utilizaremos para aprovechar el tipado fuerte de TypeScript.
A continuación, definimos algunas otras propiedades privadas que utilizaremos más adelante:
NOMBRE_BASE_DE_DATOS: Es el nombre de la base de datos. Asegúrate de que no contenga mayúsculas (y que tenga menos de 240 caracteres).
USER_SETTINGS_DOC_ID: Nombre del documento Couchbase.
base de datos: Es la instancia del conector de base de datos.
_userSettingsDocument: Couchbase es una base de datos No orientada a documentos. No tiene tablas, en su lugar utiliza una entidad de almacenamiento primaria conocida como Documento. Básicamente un documento es una colección de pares clave-valor, donde el valor puede ser prácticamente cualquier cosa (cadena, números, matrices, etc.). Esto es lo que hace de Couchbase la solución número uno si necesitas almacenar una variedad de datos no relacionales: simplicidad y flexibilidad.
_userSettingsObj: Es el objeto JavaScript que utilizaremos para sincronizar la información con el documento de Couchbase.
Veremos por qué declaramos la variable _instance más adelante, cuando implementemos el patrón singleton.
Ahora que tenemos esto cubierto, veamos cómo funciona el constructor de nuestra clase:
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() { este.Base de datos = nuevo CouchBaseModule.Couchbase(este.NOMBRE_BASE_DE_DATOS); este.Documento de configuración de usuario = este.Base de datos.getDocument(este.USER_SETTINGS_DOC_ID); si (!este.Documento de configuración de usuario) { consola.registro("El documento aún no existe :)"); este._userSettingsObj = { nombre de usuario: "", correo electrónico: "", nombre completo: "", } este.Base de datos.crearDocumento(este._userSettingsObj, este.USER_SETTINGS_DOC_ID); este.Documento de configuración de usuario = este.Base de datos.getDocument(este.USER_SETTINGS_DOC_ID); } } |
Una vez más, vamos a ver en detalle lo que está pasando en nuestro constructor:.
this._database = new CouchBaseModule.Couchbase(this.DATABASE_NAME);
Aquí estamos instanciando Couchbase y diciendo a qué base de datos queremos conectarnos.
this._userSettingsDocument = this._database.getDocument(this.USER_SETTINGS_DOC_ID);
Esta línea le dice a Couchbase que nos consiga nuestro documento (la colección que usamos para almacenar nuestra información).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
si (!este.Documento de configuración de usuario) { consola.registro("El documento aún no existe :)"); este._userSettingsObj = { nombre de usuario: "", correo electrónico: "", nombre completo: "", } este.Base de datos.crearDocumento(este._userSettingsObj, este.USER_SETTINGS_DOC_ID); este.Documento de configuración de usuario = este.Base de datos.getDocument(este.USER_SETTINGS_DOC_ID); } |
Si nuestro documento aún no existe (por ejemplo, la primera vez), creamos un objeto vacío de tipo UserSettings y también creamos un nuevo documento. El segundo parámetro que asignamos a la función createDocument es el id del documento. Si no proporcionamos este parámetro Couchbase asignará al documento un UUID automáticamente.
¡Fantástico! Ahora que nuestro constructor está hecho, veamos cómo podemos establecer y recuperar información sobre nuestro usuario.
Vamos a configurar algunos getters y setters para lograr precisamente eso.
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 |
/*====== NOMBRE DE USUARIO GETTER Y SETTER ======*/ consiga nombre de usuario(): cadena { este._userSettingsObj = este.Base de datos.getDocument(este.USER_SETTINGS_DOC_ID ); deje nombre de usuario = este._userSettingsObj.nombre de usuario; devolver nombre de usuario; } configure nombre de usuario(valor: cadena) { este._userSettingsObj = este.Base de datos.getDocument(este.USER_SETTINGS_DOC_ID ); este._userSettingsObj.nombre de usuario = valor; este.Base de datos.actualizarDocumento(este.USER_SETTINGS_DOC_ID , este._userSettingsObj); } |
Como de costumbre, desglosemos el código.
En el getter hacemos tres cosas:
1) Leemos los datos de nuestro documento en nuestro objeto
2) Obtener el valor deseado (en este caso el nombre de usuario)
3) Devuélvalo
Muy fácil :)
En el setter hacemos lo siguiente:
1) Obtenemos la última versión de nuestro userSettingsObject leyéndolo de la db
2) Establecemos la propiedad username al valor pasado a la función
3) Actualizamos nuestro documento en la base de datos. A diferencia de la función createDocument, el primer parámetro es el ID del documento y el segundo es el propio objeto.
Pero ... ¿por qué necesitamos primero getDocument? ¿Por qué no podemos simplemente actualizar el documento con el objeto?
Me alegro de que preguntes.
Básicamente, cada vez que actualizamos un documento, Couchbase hace un seguimiento del cambio. Cada documento tiene una propiedad _rev; este valor es actualizado por Couchbase cada vez que se realiza una operación de escritura sobre el documento. Si intentamos actualizar un documento con un _rev más antiguo, Couchbase lo rechazará.
Patrón Singleton
¿A quién no le gustan los patrones de diseño? Hoy vamos a implementar uno muy simple, pero efectivo: el singleton. El patrón singleton, cuando se implementa correctamente, "restringe la instanciación de un clase a uno objeto. Esto es útil cuando se necesita exactamente un objeto para coordinar las acciones en todo el sistema", que es exactamente nuestro caso.
Básicamente nos aseguramos de que sólo tenemos una instancia de nuestra clase leyendo y escribiendo en nuestra base de datos.
¿Cómo lo hacemos? Una forma sencilla de conseguirlo es con los dos pasos siguientes:
1) Hacer nuestro constructor privado (Introducido en ts 2.0)
2) Implementar un método getInstance que devuelva la instancia actual si existe, o una nueva si es la primera llamada.
PASO 1:
1 |
privado constructor() {....} |
PASO 2:
Ahora podemos seguir adelante y crear nuestro método getInstance() que se verá algo como esto:
1 2 3 4 5 6 7 8 9 10 11 |
público estático getInstance() :Configuración de usuario{ si(!este._instancia){ este._instancia = nuevo Configuración de usuario(); } devolver este._instancia; } |
Estupendo. Hemos abstraído datos e implementado el patrón singleton, sigamos adelante y veamos cómo podemos utilizar nuestra nueva y brillante clase UserSettings.
Vamos a editar los archivos existentes main-page.xml y main-page.ts.
En primer lugar, definiremos el diseño básico de la interfaz de usuario. Para esta demo usaremos un campo de texto para obtener la entrada del usuario, un botón para guardar el valor en la base de datos, y una etiqueta para mostrar el valor actual en la base de datos. Como puedes ver, si no hay datos almacenados en la base de datos la etiqueta simplemente mostrará "No hay datos almacenados en la base de datos".
Este es el aspecto que debería tener nuestro archivo main-page.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<Página xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navegandoHacia"> <Página.actionBar> <ActionBar título="Mi App" icono="" clase="barra de acción"> </ActionBar> </Página.actionBar> <StackLayout> <Campo de texto pista="nombre de usuario" texto="{{ nombre de usuario }}" /> <Botón texto="GUARDAR MI NOMBRE DE USUARIO" pulse="onTap" clase="btn btn-primary btn-active"/> <Etiqueta texto="{{ dbusername || 'no hay datos almacenados en la db' }}" textWrap="true"/> </StackLayout> </Página> |
De acuerdo. Ahora que tenemos un diseño básico podemos proceder a añadir algo de lógica a la interfaz de usuario.
Este es el aspecto que debería tener nuestro archivo 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 |
importar { Observable } de datos/observables; //vincular datos a nuestra vista importar { Configuración de usuario } de './ConfiguraciónUsuario'; //woohoo nuestra increíble capa de interacción con la base de datos importar { EventData } de datos/observables; //utilizar con typescript para intellisense importar { Página } de ui/page; //utilizar con typescript para intellisense deje userSettings = Configuración de usuario.getInstance(); deje mainPageViewModel; clase MainPageViewModel extiende Observable{ público nombre de usuario; público nombre de usuario constructor(){ super(); este.nombre de usuario = userSettings.nombre de usuario; } } exportar función navigatingTo(args: EventData) { deje página = <Página>args.objeto; mainPageViewModel = nuevo MainPageViewModel(); página.bindingContext = mainPageViewModel; } exportar función onTap(){ userSettings.nombre de usuario = mainPageViewModel.nombre de usuario; mainPageViewModel.configure("dbusername",userSettings.nombre de usuario); } |
¿Qué hemos hecho? En las primeras líneas acabamos de importar un montón de cosas, comprobando los comentarios para ver lo que hacen los módulos. A continuación tenemos:
1 |
deje userSettings = Configuración de usuario.getInstance(); |
Así es como obtenemos una instancia de nuestra clase UserSettings.
Continuando, declaramos otra clase pequeña:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
clase MainPageViewModel extiende Observable{ público nombre de usuario; público nombre de usuario constructor(){ super(); este.nombre de usuario = userSettings.nombre de usuario; } } |
Como puedes ver MainPageViewModel extiende la clase Observable. Si no estás familiarizado con los Observables, debes saber que son básicamente objetos JavaScript que activan una notificación si una de sus propiedades cambia. Puedes encontrar más información en aquí.
Nuestro modelo de vista tiene dos propiedades: username (que vinculamos a nuestro campo de texto) y dbusername (que vinculamos a nuestra etiqueta). Cada vez que creamos una nueva instancia de nuestra clase asignamos a dbusername cualquier valor presente en nuestra instancia userSettings, que a su vez va y lee datos en nuestra base de datos (¿recuerdas nuestro método get username() getter?).
1 2 3 4 5 6 7 8 9 |
exportar función navigatingTo(args: EventData) { deje página = <Página>args.objeto; mainPageViewModel = nuevo MainPageViewModel(); página.bindingContext = mainPageViewModel; } |
Esta función es llamada cada vez que navegamos a nuestra página. Instanciamos nuestra clase MainPageViewModel y la asignamos a la variable mainPageViewModel y la enlazamos a nuestra página. Simple y llanamente.
Por último, pero no menos importante, definimos el comportamiento para el clic del botón:
1 2 3 4 5 6 7 |
exportar función onTap(){ userSettings.nombre de usuario = mainPageViewModel.nombre de usuario; mainPageViewModel.configure("dbusername",userSettings.nombre de usuario); } |
Una vez más, muy simple: Escribimos en la base de datos el valor de nuestro campo de texto (mainPageViewModel.username está vinculado al campo de texto) y luego actualizamos el valor de dbusername para actualizar la etiqueta. Una imagen vale más que mil palabras, ¡así que aquí va un GIF!
Este post forma parte del Programa de Escritura de la Comunidad Couchbase
Gracias Laura por este blog tan detallado.
¿Podemos tener un blog similar para Nativescript-vue couchbase sobre cómo persistir los datos?