He estado en un papel cuando se trata de JavaScript y Couchbase. En las últimas semanas he estado creando contenido que gira en torno al framework Angular 2, la librería JavaScript PouchDB, y Ionic Framework para móviles. Lo que aún no he explorado es Couchbase en una aplicación de escritorio construida con Angular 2.
Hace dos años escribí sobre el uso de Couchbase en una aplicación de escritorio usando AngularJS 1.0 y Electron...pero con la tecnología bien podría haber habido dinosaurios en esa época. Han cambiado muchas cosas y este post, que en su día fue estupendo, merece una actualización.
Vamos a ver cómo crear una aplicación de escritorio utilizando Electrón que funciona con Angular 2, PouchDB, Ionic 2 y Couchbase.
Requisitos
Hay numerosos requisitos que deben cumplirse para que este proyecto sea un éxito. Son los siguientes:
- Node.js 4.0+
- Marco iónico 2.0
- Pasarela de sincronización Couchbase
El enfoque y punto de este tutorial no es Ionic 2. Sin embargo, Ionic 2 tiene una capa de interfaz de usuario muy fina que nos ahorrará tiempo sobre soluciones alternativas como Bootstrap o Foundation. Ionic 2 también tiene Angular 2 incorporado. Dicho esto, necesitaremos la CLI de Ionic 2 instalada. Node.js es un requisito debido al Node Package Manager (NPM) que utilizaremos para reunir las dependencias.
No incluiremos Couchbase Server en este ejemplo, sino Couchbase Sync Gateway y su base de datos de prototipos en memoria. No es más que una sola línea para cambiar a Couchbase Server desde este ejemplo. PouchDB se comunicará desde nuestra aplicación de escritorio a Sync Gateway y en la otra dirección también.
Configuración de Couchbase Sync Gateway
Sync Gateway gestiona toda la orquestación de datos y debe configurarse para cada aplicación. Para esta aplicación en concreto, vamos a permitir que todos los dispositivos conectados puedan leer y escribir datos.
Una configuración de este tipo tendría el siguiente aspecto:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
{ "log":["CRUD+", "REST+", "Cambios+", "Adjuntar+"], "bases de datos": { "ejemplo": { "servidor":"morsa:", "sync":` función (doc) { canal (doc.canales); } `, "usuarios": { "INVITADO": { "desactivado": falso, "admin_canales": ["*"] } } } } } |
En la configuración de ejemplo anterior, utilizaremos una base de datos denominada ejemplopero en realidad puede llamarse como quieras. La configuración debe guardarse en un archivo, por ejemplo, sync-gateway-config.json.
Con Couchbase Sync Gateway descargado e instalado, la configuración se puede ejecutar de la siguiente manera:
1 |
/ruta/a/sync_gateway /ruta/a/sincronizar-pasarela-config.json |
A continuación, se puede acceder a Sync Gateway desde http://localhost:4985/_admin/.
Creación de un proyecto Ionic 2 destinado al PC
Si has visto mi guía anterior sobre el tema de la Couchbase con Ionic 2 y PouchDBprobablemente se pregunte qué será diferente aquí. La verdad es que nada. Las aplicaciones Ionic 2 que no utilizan funciones nativas de Android e iOS se pueden integrar en aplicaciones Electron sin problemas. Sin embargo, se han realizado algunas optimizaciones en esta aplicación frente a la anterior.

En la animación de arriba puedes ver una simple lista de tareas de una aplicación Electron donde puedes añadir elementos a la lista y sincronizarlos con Couchbase Sync Gateway y eventualmente con otros dispositivos y Couchbase Server.
Empecemos creando ese sencillo ejemplo.
Creación de un nuevo proyecto con las dependencias
Antes de que podamos empezar a desarrollar la aplicación y empaquetarla en Electron, necesitamos crear un proyecto nuevo con cada una de las dependencias.
Desde el Símbolo del sistema o Terminal, ejecute lo siguiente:
1 2 |
iónico iniciar ElectrónEjemplo en blanco --v2 cd ElectrónEjemplo |
Con el proyecto Ionic 2 creado necesitamos reunir algunas dependencias como Electron y PouchDB. Para ello ejecuta lo siguiente:
1 2 3 |
npm instale pouchdb --guardar npm instale uuid @tipos/uuid --guardar npm instale electrón --guardar-dev |
Debido a que esta aplicación Angular 2 utilizará TypeScript, es mejor utilizar bibliotecas JavaScript con definiciones de tipos. Sin embargo, PouchDB no tiene un conjunto oficial de definiciones de tipos, haciéndolas obsoletas. Podemos evitarlo incluyendo la siguiente dependencia:
1 |
npm instale @tipos/nodo --guardar |
La dependencia anterior nos dará acceso a la aplicación requiere
para poder importar bibliotecas JavaScript a nuestro proyecto.
Añadir Electron para soporte de escritorio
Con el proyecto creado necesitamos añadir soporte para Electron. Esto se hace añadiendo un archivo de configuración JavaScript especial que Electron procesa en el arranque.
En la raíz de su proyecto, cree un archivo llamado electron.js con el siguiente código JavaScript:
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 |
const electrón = requiere("electrón) const aplicación = electrón.aplicación const BrowserWindow = electrón.BrowserWindow const ruta = requiere(camino) const url = requiere(url) deje mainWindow función crearVentana () { mainWindow = nuevo BrowserWindow({anchura: 800, altura: 600}) mainWindow.loadURL(url.formato({ ruta: ruta.únase a(__dirname, www/index.html), protocolo: "archivo:, barras: verdadero })) mainWindow.en(cerrado, función () { mainWindow = null }) } aplicación.en(listo, crearVentana) aplicación.en(ventana-todo-cerrado, función () { si (proceso.plataforma !== darwin) { aplicación.abandone() } }) aplicación.en(activar, función () { si (mainWindow === null) { crearVentana() } }) |
La mayor parte del código anterior se ha tomado directamente del archivo Plantilla Electron Startera excepción de esta línea:
1 |
ruta: ruta.únase a(__dirname, www/index.html), |
En la línea anterior le estamos diciendo a Electron qué página de Ionic 2 cargar cuando se inicie la aplicación. Mientras se realiza el bootstrapping de Electron, necesitamos empaquetarlo en nuestros scripts de compilación.
Abra el archivo paquete.json e incluya esta línea:
1 |
"principal": "electron.js", |
La línea anterior le dice a Electron cuál es el archivo de configuración. Es necesario porque tal vez usted no nombró su archivo electron.js como hice yo. También necesitamos añadir un script particular que construya el proyecto y lo lance con Electron:
1 2 3 4 5 |
"scripts": { "ionic:build": "ionic-app-scripts build", "ionic:serve": "ionic-app-scripts serve", "electrón": "ionic-app-scripts build; electron ." }, |
No empaquetará la aplicación para su despliegue en una tienda de aplicaciones, pero nos permitirá probarla correctamente con Electron en nuestro ordenador.
En este punto podemos centrarnos en el código de Angular 2 que puede que hayas visto antes o puede que no.
Desarrollo de un proveedor de PouchDB para Angular 2
Es una buena práctica mantener el código relacionado con los datos separado de la lógica de la página. En Angular 2 podemos lograr esta separación mediante el uso de un proveedor compartido.
Para crear un proveedor en Ionic 2, ejecuta lo siguiente desde la CLI:
1 |
iónico g proveedor pouchdb-proveedor |
Deberías acabar con src/providers/pouchdb-provider.ts o similar. El nombre no es realmente importante siempre que recuerdes lo que es.
Abra el archivo del proveedor e incluya el siguiente TypeScript:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
importar { Inyectable, Emisor de eventos } de @angular/core; var PouchDB = requiere("pouchdb"); @Inyectable() exportar clase PouchDBProvider { privado isInstantiated: booleano; privado base de datos: cualquier; privado oyente: Emisor de eventos = nuevo Emisor de eventos(); público constructor() { } público consiga(id: cadena) { } público poner(documento: cualquier, id: cadena) { } público sincronizar(remoto: cadena) { } público getChangeListener() { } } |
El proveedor se inyectará en varias páginas y emitirá cambios, de ahí que la etiqueta Inyectable
y Emisor de eventos
importaciones. También estamos importando la biblioteca PouchDB JavaScript.
El proveedor actuará como un singleton con una instancia de base de datos abierta mientras nuestra aplicación esté abierta. Esta configuración se puede crear en el archivo constructor
método:
1 2 3 4 5 6 7 8 9 10 11 12 |
público constructor() { si(!este.isInstantiated) { este.base de datos = nuevo PouchDB("nraboy"); este.base de datos.cambia({ en directo: verdadero, incluir_docs: verdadero }).en(cambio, cambiar => { este.oyente.emite(cambiar); }); este.isInstantiated = verdadero; } } |
En el constructor
si la base de datos no está ya instanciada, la instanciaremos, la abriremos y configuraremos los eventos de cambio. Por cada cambio contra la base de datos los emitiremos y eventualmente los recogeremos suscribiéndonos al listener.
Digamos que queremos obtener un documento NoSQL en particular por su id. Podemos crear una función como la siguiente:
1 2 3 |
público consiga(id: cadena) { devolver este.base de datos.consiga(id); } |
Para esta aplicación en particular, el método anterior es más útil para nuestro método de creación de documentos, ya que queremos ver si un documento existe antes de intentar actualizarlo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
público poner(documento: cualquier, id: cadena) { documento._id = id; devolver este.consiga(id).entonces(resultado => { documento._rev = resultado._rev; devolver este.base de datos.poner(documento); }, error => { si(error.estado == "404") { devolver este.base de datos.poner(documento); } si no { devolver nuevo Promesa((resolver, rechace) => { rechace(error); }); } }); } |
Si el documento no existe, se creará en lugar de actualizarse.
Como el objetivo aquí es usar Couchbase en nuestra aplicación Electron de escritorio, necesitamos tener una función de sincronización:
1 2 3 4 5 6 7 |
público sincronizar(remoto: cadena) { deje remoteDatabase = nuevo PouchDB(remoto); este.base de datos.sincronizar(remoteDatabase, { en directo: verdadero, reintentar: verdadero }); } |
En sincronizar
tomará el nombre de host y la base de datos de nuestro Sync Gateway y realizará una sincronización bidireccional entre nuestra base de datos local y la base de datos remota.
1 2 3 |
público getChangeListener() { devolver este.oyente; } |
Por último, proporcionamos una forma de obtener y suscribirse al receptor de cambios.
El proveedor PouchDB está hecho, pero no está listo para ser usado. Para usarlo en cada una de nuestras páginas necesitamos añadirlo a la aplicación @NgModule
que se encuentra en el bloque src/app/app.module.ts archivo. Abra este archivo e incluya lo siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
importar { NgModule, ErrorHandler } de @angular/core; importar { IonicApp, IonicModule, IonicErrorHandler } de iónico-angular; importar { MyApp } de './app.component'; importar { Página de inicio } de '../páginas/hogar/hogar'; importar { PouchDBProvider } de "../proveedores/pouchdb-proveedor"; @NgModule({ declaraciones: [ MyApp, Página de inicio ], importaciones: [ IonicModule.forRoot(MyApp) ], arranque: [IonicApp], entryComponents: [ MyApp, Página de inicio ], proveedores: [{proporcionar: ErrorHandler, useClass: IonicErrorHandler}, PouchDBProvider] }) exportar clase AppModule {} |
Básicamente, sólo importamos el proveedor y lo añadimos a la carpeta proveedores
de la @NgModule
bloque. Ahora el proveedor se puede utilizar en las páginas de nuestra aplicación.
Añadir la lógica de página para una aplicación funcional
Esta aplicación en particular sólo tiene una pantalla y esa pantalla es bastante simple. Muestra una lista de datos y permite introducir nuevos datos.
Empezando por la lógica TypeScript, abra el archivo src/pages/home/home.ts e incluya lo siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
importar { Componente, NgZone } de @angular/core; importar { NavController, AlertController } de iónico-angular; importar { PouchDBProvider } de "../../proveedores/pouchdb-proveedor"; importar * como Uuid de "uuid"; @Componente({ selector: página de inicio, templateUrl: Inicio.html }) exportar clase Página de inicio { público artículos: Matriz; público constructor(público navCtrl: NavController, público alertCtrl: AlertController, privado zona: NgZone, privado base de datos: PouchDBProvider) { este.artículos = []; } público ionViewDidEnter() { } público insertar() { } } |
Hemos importado varios componentes de Angular 2, Ionic 2 y personalizados en la página y hemos inyectado muchos de ellos en el componente constructor
método. En artículos
contendrá los datos sincronizados que se mostrarán en la pantalla. El constructor
sólo inicializa nuestras variables.
Para cargar datos en nuestras variables debemos utilizar el método ionViewDidEnter
método:
1 2 3 4 5 6 7 8 |
público ionViewDidEnter() { este.base de datos.sincronizar("http://192.168.57.1:4984/example"); este.base de datos.getChangeListener().suscríbase a(datos => { este.zona.ejecute(() => { este.artículos.pulse(datos.doc); }); }); } |
En el ionViewDidEnter
estamos iniciando la sincronización con nuestro Sync Gateway y suscribiéndonos a los eventos de cambio. A medida que se produzcan cambios, se añadirán a la base de datos artículos
matriz. Estamos utilizando NgZone
porque los escuchadores de cambios son dudosos en Angular 2 y queremos garantizar que la interfaz de usuario se actualiza correctamente.
En insertar
es prácticamente toda la lógica de Ionic 2:
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 |
público insertar() { deje consulte = este.alertCtrl.crear({ título: Todo, mensaje: "Añadir un nuevo elemento a la lista de tareas pendientes", entradas: [ { nombre: título, marcador de posición: Título }, ], botones: [ { texto: Cancelar, manipulador: datos => {} }, { texto: Guardar, manipulador: datos => { si(datos.título) { este.base de datos.poner({tipo: "lista", título: datos.título}, Uuid.v4()); } } } ] }); consulte.presente(); } |
Cuando se llama al método, se mostrará un mensaje para que el usuario introduzca los datos. Cuando se guarden, los datos se guardarán como un documento en PouchDB y se sincronizarán con Couchbase Server.
Diseño de la interfaz de usuario
La interfaz de usuario detrás de la lógica TypeScript es corta y dulce. Abra el proyecto src/pages/home/home.html e incluya el siguiente código HTML:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<ion-cabecera> <ion-barra de navegación> <ion-título> Electrón w/ Couchbase </ion-título> <ion-botones fin> <botón ion-botón icono-sólo (haga clic en)="insert()"> <ion-icono nombre="añadir"></ion-icono> </botón> </ion-botones> </ion-barra de navegación> </ion-cabecera> <ion-contenido acolchado> <ion-lista> <ion-artículo *ngFor="let item of items"> {{ artículo.título }} </ion-artículo> </ion-lista> </ion-contenido> |
La interfaz de usuario tiene una barra de acción con un botón que ejecutará el programa insertar
cuando se pulsa. El contenido de la pantalla es una lista que muestra cada elemento de la lista artículos
como una fila.
Ver el proyecto en acción
Hicimos muchas cosas en el ejemplo de Electron con Couchbase. He cargado un proyecto de trabajo en GitHub si quieres probarlo.
Descargue el proyecto y ejecute el siguiente comando:
1 |
npm instale |
El comando anterior restaurar todas las dependencias del proyecto. Con Sync Gateway en marcha, ejecute lo siguiente para ejecutar la aplicación con Electron:
1 |
npm ejecute electrón |
Tenga en cuenta que probablemente tendrá que cambiar el host de Sync Gateway en el archivo src/pages/home/home.ts para que coincida con su nombre de host.
Conclusión
Acabas de ver cómo crear una aplicación de escritorio que se sincroniza con Couchbase. Esta aplicación funciona con Electrónpero utiliza Angular 2 y PouchDB. Aunque la capa de interfaz de usuario era Ionic 2, no tenía por qué serlo. Puedes usar tu propio framework de UI como Bootstrap o similar. El objetivo aquí era más bien demostrar Electron con Couchbase y Angular 2. Esta guía fue un paso adelante de mi anterior sobre el tema de AngularJS 1.0 con Couchbase y Electron.
En funcionamiento
npm run electron
El resultado es un error:
[19:46:53] ionic-app-scripts 0.0.47
[19:46:53] tarea ionic-app-script: "build;"
[19:46:53] Error: No se puede encontrar el módulo '../dist/build;'
(Y esto es de su propio ejemplo de github, descargado, hizo npm install, y npm run electron).
¿Alguna idea?
Gracias :)
Hola Martín y Nic,
Estoy recibiendo el mismo error, utilizando Ionic 2.1.12
[15:44:02] ionic-app-scripts 1.1.4
[15:44:02] tarea ionic-app-script: "build;"
[15:44:02] Error: No se puede encontrar el módulo '../dist/build;'
¿Alguno de vosotros lo ha resuelto?
Gracias :)
¿Qué versión de Ionic estás usando? Creo que Ionic tuvo cambios importantes entre las versiones de la corriente y cuando lo publiqué.
El contenido y la lógica deben seguir siendo exactos, pero el comando de compilación puede ser ligeramente diferente.
Hola,
En primer lugar, ¡gracias Nic por este post!
Tengo el mismo error ("Error: Cannot find module '../dist/build;'"), y creo que es porque el script "electron" supone que el módulo electron npm está instalado globalmente.
Con electron instalado localmente, deberías usar algo como esto:
"./node_modules/.bin/electron ."
Véase https://electron.atom.io/docs/tutorial/quick-start/
Por lo tanto, he cambiado la secuencia de comandos de electrones con esto (estoy bajo Windows OS):
"scripts": {
…
"electron": "ionic-app-scripts build && .\node_modules\\.bin\\electron ."
}
Finalmente, ¡ejecutar "npm run electron" me funciona!