¿Has utilizado alguna vez una extensión de Chrome en tu navegador web Google Chrome? Yo personalmente uso un montón de ellas y algunas de mis favoritas son ColorZilla y Google Hangouts. ¿Alguna vez has querido crear tu propia extensión de Chrome o, lo que es más importante, una extensión de Chrome que sincronice datos con tu fuente de datos remota?
Las extensiones de Chrome no son más que aplicaciones JavaScript y como desarrolladores de JavaScript tenemos todas las habilidades necesarias para crear algo impresionante.

Vamos a echar un vistazo a la creación de una extensión de Google Chrome que sincroniza los datos de la lista de la compra entre navegadores e incluso dispositivos utilizando Angular 2, PouchDBy Couchbase.
Requisitos
Hay algunos requisitos cuando se trata de hacer la extensión popup prevista. Son los siguientes:
- Node.js 4.0+
- Angular CLI
- Pasarela de sincronización Couchbase 1.3+
Como estamos construyendo la extensión usando Angular 2, necesitaremos el Angular CLI que está disponible a través del Node Package Manager (NPM) que se encuentra en Node.js. Aunque no vamos a utilizar Couchbase Server en este ejemplo, se puede añadir fácilmente a nuestro archivo de configuración de Couchbase Sync Gateway. En su lugar vamos a utilizar una solución de almacenamiento en memoria que viene con Sync Gateway y vamos a utilizar Sync Gateway para sincronizar nuestros datos entre navegadores, dispositivos, plataformas y Couchbase Server.
Preparación de Couchbase Sync Gateway para la replicación de datos
Hay varias maneras de crear una extensión de Chrome que utilice Couchbase. Para este ejemplo, y en un esfuerzo por hacer sólo una aplicación, vamos a mantenerla orientada al cliente con JavaScript y Couchbase Sync Gateway. Otros ejemplos podrían incluir un servicio back-end con el que nuestra aplicación cliente se comunica y ese servicio back-end se comunicaría con Couchbase.
Dado que la extensión de Chrome es una aplicación, necesitamos una configuración para Sync Gateway para que sepa cómo orquestar los datos. Una configuración en una de sus formas más simples podría parecerse a la siguiente:
|
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+", "Cambios+", "Adjuntar+"], "bases de datos": { "ejemplo": { "servidor":"morsa:", "sync":` función (doc) { canal (doc.canales); } `, "usuarios": { "INVITADO": { "desactivado": falso, "admin_canales": ["*"] } } } }, "CORS": { "Origen": ["http://localhost:4200", "chrome-extension://ecfgpjabjmlpkedejekaipjipddddohj"], "LoginOrigin": ["http://localhost:4200", "chrome-extension://ecfgpjabjmlpkedejekaipjipddddohj"], "Cabeceras": ["Tipo de contenido"], "MaxAge": 17280000 } } |
La configuración anterior se guardaría en un archivo llamado sync-gateway-config.json y se carga al iniciar Sync Gateway.
En la configuración estamos utilizando una base de datos llamada ejemplo sin permisos específicos de lectura o escritura. La sección CORS es para compartir recursos entre orígenes, un requisito cuando se trata de aplicaciones JavaScript. Lo que estamos diciendo es que nos gustaría permitir conexiones desde el puerto 4200 que es nuestra aplicación Angular 2 durante las pruebas, y la dirección de nuestra extensión de Chrome.
Descargar Sync Gateway y lánzalo ejecutando lo siguiente:
|
1 |
/ruta/a/sync_gateway /ruta/a/sincronizar-pasarela-config.json |
Lo anterior comenzará a servir Sync Gateway en http://localhost:4985/_admin/.
Creación de una nueva aplicación con Bootstrap y Angular 2
Para simplificar, vamos a iniciar un nuevo proyecto de Angular 2 y posteriormente lo empaquetaremos en una extensión de Chrome. Desde el CLI de Angular, ejecuta el siguiente comando:
|
1 |
ng nuevo AngularProject |
El comando anterior creará un proyecto Angular 2 listo para usar. En un esfuerzo por salvarnos de tener una interfaz de usuario horrible, vamos a incluir el popular comando Bootstrap framework. Hay muchos otros frameworks, y en uno de mis ejemplos anteriores incluso utilicé Ionic como framework.
Esto es un poco de un proceso manual desde Angular 2 no necesariamente jugar bien con nuestras dependencias, pero tenemos que descargar Bootstrap, jQueryy Bootbox.
Coloque los archivos JavaScript en el directorio src/assets/js/ los archivos CSS en el directorio src/assets/css/ y los archivos de fuentes en el directorio src/assets/fonts/ directorio.
Bootstrap será nuestro framework CSS, pero tiene una dependencia de jQuery. Bootbox nos dará una manera conveniente de crear avisos emergentes para nuestra aplicación. Está construido sobre Bootstrap y jQuery.
Tenemos que añadir Bootstrap a nuestro proyecto a la antigua usanza. Abra el proyecto src/index.html y que tenga el siguiente aspecto:
|
1 2 3 4 5 6 7 8 9 10 11 |
<!doctype html> Couchbase Compras Cargando... |
Note que estamos importando los archivos CSS y JavaScript que fueron descargados.
En este momento podemos empezar a codificar, pero antes de hacerlo, es una buena idea obtener algunas dependencias más que se utilizarán a lo largo de este proyecto.
Utilizando el Símbolo del sistema (Windows) o el Terminal (Mac y Linux) ejecute lo siguiente:
|
1 2 3 |
npm instale pouchdb --guardar npm instale uuid @tipos/uuid --guardar npm instale @tipos/nodo --guardar |
Los comandos anteriores instalarán PouchDB que usaremos para replicar datos hacia y desde Couchbase Sync Gateway. Usaremos la dependencia UUID para generar valores de id únicos que usaremos como claves de documento y usaremos la dependencia Node para poder importar la librería PouchDB a nuestro proyecto. Necesitamos la dependencia Node ya que PouchDB no tiene definiciones de tipos TypeScript.
Empecemos ahora a desarrollar la aplicación que hay detrás de nuestra extensión.
Desarrollo de un proveedor compartido con Angular 2 y PouchDB
Si has visto mis últimos tutoriales, esto puede parecerte familiar. Cuando se trabaja con datos en una aplicación Angular 2, es una buena idea segregar la lógica del resto de la aplicación. Podemos hacer esto creando un proveedor compartido de Angular 2 que envuelva PouchDB y pueda ser usado en toda la aplicación.
Crea un archivo, src/app/pouchdb.service.ts e incluir la siguiente lógica 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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
importar { Inyectable, Emisor de eventos } de @angular/core; var PouchDB = requiere("pouchdb"); @Inyectable() exportar clase Servicio de valija { privado isInstantiated: booleano; privado base de datos: cualquier; privado oyente: Emisor de eventos = nuevo Emisor de eventos(); 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; } } público consiga(id: cadena) { devolver este.base de datos.consiga(id); } 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); }); } }); } público sincronizar(remoto: cadena) { deje remoteDatabase = nuevo PouchDB(remoto); este.base de datos.sincronizar(remoteDatabase, { en directo: verdadero, reintentar: verdadero }); } público getChangeListener() { devolver este.oyente; } } |
Están pasando muchas cosas, así que deberíamos dedicar un momento a desglosarlas.
|
1 2 |
importar { Inyectable, Emisor de eventos } de @angular/core; var PouchDB = requiere("pouchdb"); |
Planeamos inyectar este proveedor en cada una de nuestras páginas de extensión. Dado que PouchDB tiene un oyente de cambios, queremos emitir esos cambios para que las páginas puedan suscribirse a ellos.
|
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; } } |
El objetivo es tener una única instancia de la base de datos local. Después de abrir la base de datos y configurar los emisores de cambios, queremos almacenar el registro de que la base de datos está abierta para que no podamos volver a abrirla. Esto se hace mejor en el constructor método.
Guardar en PouchDB no es más que una sola línea, pero si queremos upsert, se necesita un poco más:
|
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); }); } }); } |
La reinserción consiste en comprobar si el documento ya existe. Si existe, capturar la revisión y actualizarla. Si el documento no existe, hay que crearlo.
|
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 }); } |
Deseamos sincronizar por lo que crearemos un sincronizar que toma una base de datos remota. La base de datos remota es nuestro Couchbase Sync Gateway. Cuando se activa sincronizaremos continuamente en ambas direcciones.
|
1 2 3 |
público getChangeListener() { devolver este.oyente; } |
Finalmente tenemos un getChangeListener que nos permitirá recuperar y suscribirnos al emisor de eventos de cambio.
Antes de empezar a utilizar el proveedor, hay que importarlo al directorio @NgModule bloque. Abra el archivo src/app/app.module.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 |
importar { BrowserModule } de @angular/platform-browser; importar { NgModule } de @angular/core; importar { Módulo de formularios } de @angular/forms; importar { HttpModule } de @angular/http; importar { AppComponent } de './app.component'; importar { Servicio de valija } de "./pouchdb.service"; @NgModule({ declaraciones: [ AppComponent ], importaciones: [ BrowserModule, Módulo de formularios, HttpModule ], proveedores: [Servicio de valija], arranque: [AppComponent] }) exportar clase AppModule { } |
Obsérvese que el Servicio de valija se importó y se añadió al proveedores de la @NgModule bloque. En este punto, el proveedor puede utilizarse en cualquier página de la extensión.
Creación de la aplicación y adición de los datos de la extensión de Chrome
Hay algunas cosas que se deben hacer dentro de este proyecto. La funcionalidad de Angular 2 para la ventana emergente de la extensión debe ser añadida y debe ser empaquetada en una extensión real de Google Chrome, en lugar de dejarla como una aplicación web.
Creación de la página de la aplicación Angular 2
La extensión que estamos construyendo consta de una sola página. Esta página tendrá una interfaz HTML y CSS personalizada, así como la lógica para controlarla.
Comenzando con TypeScript, abra el archivo src/app/app.component.ts e incluir el siguiente 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 28 29 30 31 32 33 34 35 36 37 |
importar { Componente, OnInit, NgZone } de @angular/core; importar * como Uuid de "uuid"; importar { Servicio de valija } de "./pouchdb.service"; declara var caja de arranque: cualquier; @Componente({ selector: app-root, templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) exportar clase AppComponent implementa OnInit { público artículos: Matriz; público constructor(privado base de datos: Servicio de valija, privado zona: NgZone) { este.artículos = []; } público ngOnInit() { este.base de datos.sincronizar("http://localhost:4984/example"); este.base de datos.getChangeListener().suscríbase a(datos => { este.zona.ejecute(() => { este.artículos.pulse(datos.doc); }); }); } público insertar() { caja de arranque.consulte("¿Qué quieres añadir?", resultado => { si(resultado) { este.base de datos.poner({tipo: "lista", título: resultado}, Uuid.v4()); } }); } } |
En el TypeScript anterior estamos importando algunos componentes de Angular 2, el proveedor de PouchDB que habíamos creado, así como la biblioteca UUID para generar claves de documento únicas.
Basándonos en cómo hemos decidido incluir Bootboxdebe declararse como cualquier para evitar errores de TypeScript. Puede encontrar más información sobre la inclusión de bibliotecas JavaScript basadas en navegador en una aplicación TypeScript aquí.
Cada artículo de la compra se almacenará en el artículos array. Es pública, por lo que se puede acceder a ella desde el código HTML. La dirección constructor no sólo inicializa esta matriz, sino que también inyecta el servicio PouchDB en la página, así como NgZone. Utilizaremos NgZone cuando actualicemos la interfaz de usuario desde un receptor de eventos.
Porque es una mala práctica cargar datos dentro del constructor lo cargamos dentro del método ngOnInit método. Los datos se cargan suscribiéndose al oyente de eventos y sincronizando los cambios desde el servidor remoto.
Por último, podemos utilizar Bootbox en nuestro insertar para solicitar datos y guardarlos en la base de datos. Si se sincroniza, los datos se guardarán y luego se replicarán en Couchbase.
La interfaz de usuario HTML que subyace a esta lógica se encuentra en el archivo src/app/app.component.html file:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<div> <nav clase="navbar navbar-default navbar-fixed-top" estilo="color de fondo: #CC2A2E;"> <div clase="navbar-header"> <span clase="navbar-brand" estilo="color: #FFFFFF">Couchbase Compras</span> </div> </nav> <ul clase="lista-grupo" estilo="margin-top: 52px"> <li clase="lista-grupo-elemento" *ngFor="let item of items">{{ artículo.título }}</li> </ul> <a (haga clic en)="insert()" clase="btn btn-circle btn-primary" estilo="position: fixed; bottom: 10px; derecha: 10px"> <span clase="glyphicon glyphicon-plus"></span> </a> </div> |
Utilizando Bootstrap y CSS personalizado, la interfaz de usuario tiene una barra de acción con una lista de datos. La lista de datos se rellena desde el artículos y se añade mediante un botón flotante. El botón flotante tiene el siguiente CSS, que puede añadirse al archivo src/app/app.component.css file:
|
1 2 3 4 5 6 7 8 9 10 |
/* Tomado de http://bootsnipp.com/snippets/8deZ */ .btn-círculo { anchura: 35px; altura: 35px; texto-alinear: centro; acolchado: 2px 0; fuente-talla: 20px; línea-altura: 1.65; frontera-radio: 30px; } |
Al hacer clic en el botón, se activará la función insertar método. En este punto tenemos una aplicación web Angular 2 completamente funcional que se replica con Couchbase. Sin embargo, nuestro objetivo es convertirlo en una extensión para Google Chrome.
Preparación y empaquetado para la implantación de Google Chrome
Google Chrome requiere al menos dos cosas cuando se crea una extensión de Chrome. Usted debe tener un icono de extensión y un archivo de manifiesto. Para que sea de calidad deberías tener iconos de numerosos tamaños, pero por simplicidad solo vamos a usar uno.
Crea un nuevo proyecto, fuera de tu proyecto Angular 2. Tal vez llámalo MyChromeExtension. Lo que quieres hacer es construir tu proyecto Angular 2 en este nuevo proyecto de extensión de Chrome. Por ejemplo, harías algo como esto:
|
1 |
ng construya --salida-ruta=/ruta/a/MyChromeExtension/ |
Si tiene éxito, todos los archivos de proyecto de Angular 2 empaquetados terminarán en su archivo MyChromeExtension directorio. Dentro de ese mismo directorio, cree un directorio manifiesto.json con lo siguiente:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
{ "manifest_version": 2, "nombre": "Compras Couchbase", "descripción": "Sincronización con Couchbase en una extensión de Google Chrome", "version": "1.0", "browser_action": { "icono_por_defecto": "icon.png", "default_popup": "index.html" }, "permisos": [], "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", "recursos_accesibles_en_la_web": [ "assets/css/*", "assets/js/*", "assets/fonts/*" ] } |
Hay algunas partes importantes en el archivo de manifiesto anterior. Decimos que el icono de extensión será icono.png que se encuentra en la raíz del proyecto. Si aún no ha creado uno, cree un icono de 128×128 en formato PNG. La página que se abrirá al hacer clic en el icono de la barra de herramientas de Chrome es la siguiente index.html que debería haberse generado al compilar su proyecto Angular 2.
Por defecto, no podemos utilizar archivos a menos que los especifiquemos. Definiendo una lista de recursos_accesibles_en_la_web estamos diciendo que nuestra extensión puede usar el JavaScript, CSS y fuentes que Bootstrap nos dejó.
Necesitamos política_seguridad_contenido porque el JavaScript de nuestro proyecto se comunica con servicios externos a nuestro paquete. Más específicamente, el Couchbase Sync Gateway.
Para instalar nuestra extensión en un navegador Chrome, abra Chrome y vaya al gestor de extensiones.

En la parte superior, active la extensión para desarrolladores y vaya a su MyChromeExtension descomprimido. Al cargarlo, la extensión se colocará en la barra de herramientas de Chrome como cualquier otra extensión.
Ver el paquete completo
Había mucho que asimilar cuando se trata de esta extensión Angular 2 para Google Chrome. He cargado el proyecto a GitHub por si quieres probarlo.
Descargue el proyecto y navegue hasta AngularExtension directorio. Desde ese directorio ejecute lo siguiente:
|
1 2 |
npm instale ng construya --salida-ruta=../público |
Una vez que el público copia los archivos de manifiesto e icono en el directorio público. A continuación, puede cargarlo en Google Chrome.
Conclusión
Acabas de ver cómo crear una extensión de Google Chrome con Angular 2 que utiliza Couchbase para replicar datos. Este proyecto era más o menos una aplicación de Angular 2 con empaquetado de Google Chrome, pero demostró lo fácil que era hacer estas extensiones.
Como ya se ha dicho, hay muchas formas diferentes de utilizarlo. Couchbase con extensiones de Google Chrome. Podrías usar PouchDB y Sync Gateway, podrías usar peticiones HTTP y Sync Gateway, podrías crear tu propia API web con los SDK de Couchbase, o alguna otra cosa.