Recientemente estuve en dos grupos Meetup diferentes en el sur de California presentando lo que yo llamo, la pila OCEAN, que se compone de Ottoman.js, Servidor CouchbaseExpress Framework, Angular y Node.js.
Tuvimos una gran afluencia de desarrolladores ávidos de aprender sobre una pila de JavaScript con NoSQL.

Según mi promesa a los grupos, he hecho una redacción tutorial del contenido que compartí para que todos puedan beneficiarse. Vamos a ver cómo desarrollar una aplicación web funcional utilizando varias tecnologías JavaScript y NoSQL.
Antes de saltar al código, se asume que tienes Couchbase Server instalado y configurado, así como Node.js. Aquí nos centraremos en el desarrollo, no en la configuración del entorno.
Vamos a construir una API usando Ottoman.js y luego con N1QL en lugar de Ottoman.js. Ambas APIs serán consumidas por un front-end desarrollado en Angular.
Desarrollo de una API con Ottoman.js, Express Framework y Couchbase NoSQL
El primer paso en cualquier proyecto Node.js es configurar un proyecto y descargar todas las dependencias. La creación de un proyecto fresco y la descarga de las dependencias se puede manejar con los siguientes comandos:
|
1 2 |
npm init --y npm instale otomana couchbase express cuerpo-analizador cors --guardar |
El primer comando creará el proyecto paquete.json y el segundo comando obtendrá nuestras dependencias, entre las que se incluye el archivo body-parser para aceptar cuerpos JSON en la solicitud y la directiva cors para los problemas relacionados con el origen cruzado que puedan surgir al utilizar Angular en un dominio o puerto diferente.
Para arrancar nuestra aplicación, vamos a crear un archivo llamado app.js que contiene 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 |
var Couchbase = requiere("couchbase"); var Express = requiere("express"); var Cors = requiere("cors"); var BodyParser = requiere("body-parser"); var Otomano = requiere("otomana"); var aplicación = Express(); aplicación.utilice(BodyParser.json()); aplicación.utilice(BodyParser.urlencoded({ ampliado: verdadero })); aplicación.utilice(Cors()); var grupo = nuevo Couchbase.Grupo("couchbase://localhost"); var cubo = grupo.openBucket("por defecto", ""); Otomano.tienda = nuevo Otomano.CbStoreAdapter(cubo, Couchbase); aplicación.consiga("/gente", función(solicitar, respuesta) { }); aplicación.consiga("/persona/:id", función(solicitar, respuesta) { }); aplicación.Correo electrónico:("/persona/:id?", función(solicitar, respuesta) { }); aplicación.borrar("/persona/:id", función(solicitar, respuesta) { }); var servidor = aplicación.escuche(3000, función() { consola.registro("Escuchando en el puerto " + servidor.dirección().puerto + "..."); }); |
Esencialmente acabamos de importar nuestras dependencias descargadas, definimos nuestros puntos finales de API, establecimos una conexión con Couchbase Server, y comenzamos a servir la aplicación.
Los puntos finales de la API serán para operaciones CRUD básicas contra nuestra base de datos. Como estamos usando Ottoman.js que es un modelador de documentos de objetos (ODM), necesitamos definir nuestro modelo. Encima de las definiciones de los endpoints, añade lo siguiente:
|
1 2 3 4 5 6 7 8 |
var Persona = Otomano.modelo("Persona", { nombre: "cadena", apellido: "cadena", medios_sociales: { sitio web: "cadena", twitter: "cadena" } }); |
Este modelo puede ser infinitamente complejo, pero el nuestro sólo representará un simple perfil de usuario. Este es el modelo que manipularemos en nuestro código, así como la base de datos.
Debido a que nuestra base de datos está actualmente vacía, tiene sentido trabajar en nuestro punto final para la creación de datos. Edite el siguiente fragmento de JavaScript dentro de su proyecto:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
aplicación.Correo electrónico:("/persona/:id?", función(solicitar, respuesta) { si(!solicitar.cuerpo) { devolver respuesta.estado(401).enviar({ "mensaje": "¡Se necesita un cuerpo POST!" }); } Persona.getById(solicitar.parámetros.id, función(error, resultado) { si(error) { resultado = nuevo Persona(solicitar.cuerpo); } si no { Objeto.asignar(resultado, solicitar.cuerpo); } resultado.guardar(función(error) { si(error) { devolver respuesta.estado(500).enviar(error); } respuesta.enviar(solicitar.cuerpo); }); }); }); |
El endpoint anterior es probablemente la parte más compleja de nuestro proyecto. Estamos diciendo que estamos requiriendo un cuerpo POST. No importa lo que es, siempre y cuando exista.
Con este punto final hemos matado dos pájaros de un tiro. Estamos realizando tanto una creación como una actualización. Primero hacemos una búsqueda por el id pasado. No importa si un id fue pasado o no. Si hubo un error por razones de tal vez un id no existe, un nuevo modelo ODM se creará sobre la base de nuestra definición, de lo contrario se fusionará con lo que obtuvimos de nuestra búsqueda basada en id.
Una vez que tenemos nuestro modelo completo, podemos guardar y devolver los resultados al usuario.
El siguiente paso lógico es completar el endpoint para encontrar a una persona concreta por su valor id. Añade el siguiente JavaScript a tu proyecto:
|
1 2 3 4 5 6 7 8 9 10 11 |
aplicación.consiga("/persona/:id", función(solicitar, respuesta) { si(!solicitar.parámetros.id) { devolver respuesta.estado(401).enviar({ "mensaje": "¡Se requiere un `id`!" }); } Persona.getById(solicitar.parámetros.id, función(error, resultado) { si(error) { devolver respuesta.estado(500).enviar(error); } respuesta.enviar(resultado); }); }); |
Usando un valor de id pasado podemos obtener a alguien de la base de datos y devolverlo al cliente que había emitido la petición.
Puede haber ocasiones en las que queramos obtener todos los registros que coincidan con el modelo. Incluso puede haber ocasiones en las que deseemos realizar una consulta basada en propiedades distintas del valor de id. En este caso podemos utilizar un encontrar mando:
|
1 2 3 4 5 6 7 8 |
aplicación.consiga("/gente", función(solicitar, respuesta) { Persona.encontrar({}, { coherencia: Otomano.Coherencia.LOCAL }, función(error, resultado) { si(error) { devolver respuesta.estado(500).enviar(error); } respuesta.enviar(resultado); }); }); |
Al proporcionar un objeto vacío en nuestro comando, estamos solicitando todos los documentos que coinciden con el modelo. Esto se podría haber ampliado fácilmente a determinadas propiedades coincidentes. También queremos asegurarnos de que nuestro índice está actualizado antes de completar la consulta. Por defecto, Couchbase se centra en el rendimiento, lo que significa que si tu índice no se ha actualizado todavía, tus resultados podrían perder algunos datos. Dependiendo de las circunstancias, puedes cambiar la consistencia del escaneo como en el ejemplo anterior.
Por último, podemos construir nuestro endpoint para eliminar documentos de la base de datos:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
aplicación.borrar("/persona/:id", función(solicitar, respuesta) { si(!solicitar.parámetros.id) { devolver respuesta.estado(401).enviar({ "mensaje": "¡Se requiere un `id`!" }); } Persona.getById(solicitar.parámetros.id, función(error, resultado) { si(error) { devolver respuesta.estado(500).enviar(error); } resultado.eliminar(función(error) { respuesta.enviar({ "mensaje": "Borrado `" + solicitar.parámetros.id + "`"}); }); }); }); |
Primero obtenemos un documento por el id pasado, y si existe, podemos emitir un eliminar para borrarlo.
Si ejecuta esta aplicación y utiliza una herramienta como Carteropuede probar su API básica, pero totalmente funcional.
Desarrollo de una API con Express Framework, N1QL y Couchbase NoSQL
Digamos que usar un ODM como Ottoman.js no es lo tuyo y prefieres fabricar tus propias consultas. Quitar Ottoman.js nos da la pila CEAN y es posible con N1QL.
Aunque la estructura de nuestro proyecto será bastante similar, vamos a crear un nuevo proyecto por nuestra propia cordura.
|
1 2 |
npm init --y npm instale couchbase express cuerpo-analizador cors uuid --guardar |
Tenemos dependencias similares a las vistas en el proyecto anterior, pero esta vez estamos obteniendo un paquete para generar valores UUID. Ottoman.js creó valores id para nosotros, pero con N1QL, depende del desarrollador. Una de las muchas formas de crear un id es a través de un UUID.
Dentro de un nuevo app.js podría tener el siguiente código repetitivo:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
var Express = requiere("express"); var Couchbase = requiere("couchbase"); var BodyParser = requiere("body-parser"); var UUID = requiere("uuid"); var Cors = requiere("cors"); var aplicación = Express(); var N1qlQuery = Couchbase.N1qlQuery; aplicación.utilice(BodyParser.json()); aplicación.utilice(BodyParser.urlencoded({ ampliado: verdadero })); aplicación.utilice(Cors()); var grupo = nuevo Couchbase.Grupo("couchbase://localhost"); var cubo = grupo.openBucket("por defecto", ""); aplicación.consiga("/gente", función(solicitar, respuesta) { }); aplicación.consiga("/persona/:id?", función(solicitar, respuesta) { }); aplicación.Correo electrónico:("/persona/:id?", función(solicitar, respuesta) { }); aplicación.borrar("/persona/:id?", función(solicitar, respuesta) { }); var servidor = aplicación.escuche(3000, función() { consola.registro("Escuchando en el puerto " + servidor.dirección().puerto + "..."); }); |
Debería resultarte bastante familiar, pero esta vez hemos cambiado Ottoman.js por la preparación N1QL.
Al igual que en el ejemplo anterior, vamos a preocuparnos de guardar los datos antes de intentar leerlos de la base de datos. Echa un vistazo al siguiente código de endpoint:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
aplicación.Correo electrónico:("/persona/:id?", función(solicitar, respuesta) { si(!solicitar.cuerpo) { devolver respuesta.estado(401).enviar({ "mensaje": "¡Se necesita un cuerpo POST!" }); } var id = solicitar.parámetros.id ? solicitar.parámetros.id : UUID.v4(); solicitar.cuerpo._id = id; var declaración = N1qlQuery.fromString("UPSERT INTO `" + cubo.Nombre + "` (KEY, VALUE) VALUES ($id, $data) RETURNING `" + cubo.Nombre + "`.*"); cubo.consulta(declaración, { "id": id, "datos": solicitar.cuerpo }, función(error, resultado) { si(error) { devolver respuesta.estado(500).enviar(error); } respuesta.enviar(resultado); }); }); |
Si recibimos un id con nuestra solicitud, vamos a usarlo y asumir que estamos haciendo una actualización, de lo contrario vamos a generar un nuevo id y hacer una creación. Ambas operaciones se pueden realizar con un solo UPSERT consulta. Para evitar ataques de inyección SQL, vamos a utilizar una consulta parametrizada para parametrizar nuestros datos definidos por el usuario.
Si conocemos el id y deseamos encontrar un documento concreto, podemos utilizar un SELECCIONE consulta para el trabajo:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
aplicación.consiga("/persona/:id?", función(solicitar, respuesta) { si(!solicitar.parámetros.id) { devolver respuesta.estado(401).enviar({ "mensaje": "¡Se requiere un `id`!" }); } var declaración = N1qlQuery.fromString("SELECT `" + cubo.Nombre + "`.* FROM `" + cubo.Nombre + "WHERE META().id = $id"); cubo.consulta(declaración, { "id": solicitar.parámetros.id }, función(error, resultado) { si(error) { devolver respuesta.estado(500).enviar(error); } respuesta.enviar(resultado); }); }); |
En ambas consultas devolvemos el error o los resultados al cliente que realizó la solicitud.
Si deseamos consultar todos nuestros documentos, podemos eliminar la opción DONDE y definimos nuestra consistencia de escaneo así:
|
1 2 3 4 5 6 7 8 9 |
aplicación.consiga("/gente", función(solicitar, respuesta) { var declaración = N1qlQuery.fromString("SELECT META().id, `" + cubo.Nombre + "`.* FROM `" + cubo.Nombre + "`").coherencia(N1qlQuery.Coherencia.SOLICITUD_PLUS); cubo.consulta(declaración, función(error, resultado) { si(error) { devolver respuesta.estado(500).enviar(error); } respuesta.enviar(resultado); }); }); |
Siendo que N1QL es sólo otro sabor de SQL, nuestras posibilidades de consulta son bastante grandes. Este ejemplo de API ni siquiera cubre una fracción de lo que es posible.
Finalmente si quisiéramos eliminar un documento por su valor id, podríamos hacer algo como lo siguiente:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
aplicación.borrar("/persona/:id?", función(solicitar, respuesta) { si(!solicitar.parámetros.id) { devolver respuesta.estado(401).enviar({ "mensaje": "¡Se requiere un `id`!" }); } var declaración = N1qlQuery.fromString("BORRAR DE `" + cubo.Nombre + "WHERE META().id = $id"); cubo.consulta(declaración, { "id": solicitar.parámetros.id }, función(error, resultado) { si(error) { devolver respuesta.estado(500).enviar(error); } respuesta.enviar(resultado); }); }); |
Ninguno de estos puntos finales basados en N1QL fue difícil. No hay elección equivocada cuando se trata de Ottoman.js o N1QL, realmente sólo se reduce a la preferencia.
Creación de una interfaz de cliente con Angular y TypeScript
Con la API backend fuera del camino, podemos centrarnos en un frontend cliente muy simple, pero funcional. Este frontend funcionará tanto con nuestra pila OCEAN como con nuestro ejemplo de pila CEAN. Siempre es bueno tener algo modular.
Suponiendo que tenga el Angular CLI instalado y configurado, necesitamos tener un proyecto fresco:
|
1 |
ng nuevo couchbase-proyecto |
El comando anterior creará un nuevo proyecto con todas nuestras dependencias Angular necesarias.
En este punto puedes usar el CLI para generar cada una de nuestras páginas, o hacerlas manualmente. Ambos le pondrá en el mismo lugar.
Crea los siguientes archivos y directorios en tu proyecto como quieras:
|
1 2 3 4 5 6 |
mkdir -p src/aplicación/lista mkdir -p src/aplicación/altera toque src/aplicación/lista/lista.componente.ts toque src/aplicación/lista/lista.componente.html toque src/aplicación/altera/altera.componente.ts toque src/aplicación/altera/altera.componente.html |
Empezando por lo que será nuestro AlterComponentabra el archivo src/app/alter/alter.component.ts e incluya el siguiente 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 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 57 |
importar { Componente, OnInit } de "@angular/core"; importar { Http, Cabeceras, RequestOptions } de "@angular/http"; importar { Ubicación } de "@angular/common"; importar { Ruta activada } de "@angular/router"; importar "rxjs/Rx"; @Componente({ moduleId: módulo.id, templateUrl: "alter.component.html" }) exportar clase AlterComponent implementa OnInit { público id: cadena; público entrada: cualquier; público constructor(privado http: Http, privado ubicación: Ubicación, privado ruta: Ruta activada) { este.id = ""; este.entrada = { "nombre": "", "apellido": "", "social_media": { "sitio web": "", "twitter": "" } }; } público ngOnInit() { este.ruta.parámetros.suscríbase a(parámetros => { este.id = parámetros["id"]; si(este.id) { este.http.consiga("https://localhost:3000/person/" + este.id) .mapa(resultado => resultado.json()) .suscríbase a(resultado => { este.entrada = resultado instanceof Matriz ? resultado[0] : resultado; }); } }); } público guardar() { deje cabeceras = nuevo Cabeceras({ "Tipo de contenido": "application/json" }); deje opciones = nuevo RequestOptions({ cabeceras: cabeceras }); deje url = "https://localhost:3000/person/"; si(este.id) { url += este.id; } si(este.entrada.nombre && este.entrada.apellido) { este.http.Correo electrónico:(url, JSON.stringify(este.entrada), opciones) .mapa(resultado => resultado.json()) .suscríbase a(resultado => { este.ubicación.volver(); }); } } } |
En el código anterior ocurren muchas cosas, así que vamos a desglosarlo.
Dentro de la constructor tenemos lo siguiente:
|
1 2 3 4 5 6 7 8 9 10 11 |
público constructor(privado http: Http, privado ubicación: Ubicación, privado ruta: Ruta activada) { este.id = ""; este.entrada = { "nombre": "", "apellido": "", "social_media": { "sitio web": "", "twitter": "" } }; } |
Estamos inicializando nuestras variables públicas, una que representa los datos de nuestro formulario y la otra que representa un posible id para un documento existente. Además de esto también estamos inyectando varios servicios de Angular para su uso en el componente.
De nuevo, vamos a matar dos pájaros de un tiro. Este componente representará una pantalla para crear y actualizar documentos.
Dentro del ngOnInit tomamos un id de documento potencial y hacemos una búsqueda de los datos que residen en él:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
público ngOnInit() { este.ruta.parámetros.suscríbase a(parámetros => { este.id = parámetros["id"]; si(este.id) { este.http.consiga("https://localhost:3000/person/" + este.id) .mapa(resultado => resultado.json()) .suscríbase a(resultado => { este.entrada = resultado instanceof Matriz ? resultado[0] : resultado; }); } }); } |
Si no hay id presente, no ocurrirá nada y nuestro entrada no se rellenará con nada más que cadenas vacías. La búsqueda será una petición HTTP contra el punto final de la API que habíamos creado previamente con Ottoman.js o N1QL.
Lo último que hay que hacer para este componente es guardar:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
público guardar() { deje cabeceras = nuevo Cabeceras({ "Tipo de contenido": "application/json" }); deje opciones = nuevo RequestOptions({ cabeceras: cabeceras }); deje url = "https://localhost:3000/person/"; si(este.id) { url += este.id; } si(este.entrada.nombre && este.entrada.apellido) { este.http.Correo electrónico:(url, JSON.stringify(este.entrada), opciones) .mapa(resultado => resultado.json()) .suscríbase a(resultado => { este.ubicación.volver(); }); } } |
Si hay un id presente, lo pasaremos con nuestra petición POST. La petición POST tendrá los datos serializados del formulario. Siempre y cuando la solicitud fue exitosa, podemos navegar a la pantalla anterior.
El HTML que acompaña a esta lógica TypeScript se puede encontrar en el proyecto src/app/alter/alter.component.html y tiene el siguiente aspecto:
|
1 2 3 4 5 6 7 |
<formulario> <entrada tipo="texto" nombre="nombre" marcador de posición="Nombre" [(ngModel)]="entrada.nombre" /> <entrada tipo="texto" nombre="apellido" marcador de posición="Apellido" [(ngModel)]="input.lastname" /> <entrada tipo="texto" nombre="sitio web" marcador de posición="Página web" [(ngModel)]="input.social_media.website" /> <entrada tipo="texto" nombre="twitter" marcador de posición="Twitter" [(ngModel)]="entrada.medios_sociales.twitter" /> <botón tipo="botón" (haga clic en)="save()">Guardar</botón> </formulario> |
No soy muy artista, así que este HTML será muy básico y no tendrá ningún estilo. Observe que cada elemento del formulario está vinculado a nuestro público entrada objeto.
El segundo y último componente del proyecto sirve para listar los documentos guardados. Abra el archivo src/app/list/list.component.ts e incluya el siguiente 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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
importar { Componente, OnInit } de "@angular/core"; importar { Http } de "@angular/http"; importar { Ubicación } de "@angular/common"; importar "rxjs/Rx"; @Componente({ moduleId: módulo.id, templateUrl: "lista.componente.html" }) exportar clase ListComponent implementa OnInit { público gente: Matriz<cualquier>; público constructor(privado http: Http, privado ubicación: Ubicación) { este.gente = []; } público ngOnInit() { este.ubicación.suscríbase a(() => { este.getPeople(); }); este.getPeople(); } privado getPeople() { este.http.consiga("https://localhost:3000/people") .mapa(resultado => resultado.json()) .suscríbase a(resultado => { este.gente = resultado; }); } privado borrar(id: cadena) { este.http.borrar("https://localhost:3000/person/" + id) .mapa(resultado => resultado.json()) .suscríbase a(resultado => { para(deje i = 0; i < este.gente.longitud; i++) { si(este.gente[i]._id == id) { este.gente.empalme(i, 1); romper; } } }); } } |
De nuevo, lo desglosaremos.
Dentro del constructor inicializamos nuestras variables públicas:
|
1 2 3 |
público constructor(privado http: Http, privado ubicación: Ubicación) { este.gente = []; } |
En este caso, la variable pública es nuestra lista de documentos que aparecerán en pantalla. El siguiente paso es consultar por cualquiera que pueda existir y poblar el array ahora vacío:
|
1 2 3 4 5 6 7 |
privado getPeople() { este.http.consiga("https://localhost:3000/people") .mapa(resultado => resultado.json()) .suscríbase a(resultado => { este.gente = resultado; }); } |
A estas alturas ya te habrás dado cuenta de que el frontend no es más que un montón de peticiones HTTP a nuestra API. El backend hace la mayor parte del trabajo pesado.
Necesitamos consultar documentos tanto cuando se carga la aplicación como cuando navegamos hacia atrás en la pila de navegación de Angular. Esto se puede ver en el ngOnInit método:
|
1 2 3 4 5 6 |
público ngOnInit() { este.ubicación.suscríbase a(() => { este.getPeople(); }); este.getPeople(); } |
Necesitamos suscribirnos al oyente de ubicación para poder determinar cuándo hemos navegado hacia atrás. Hacemos esto porque el constructor y ngOnInit sólo se activan cuando se navega hacia ellos, no cuando se vuelve a ellos.
Esto nos lleva a la eliminación de documentos:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
privado borrar(id: cadena) { este.http.borrar("https://localhost:3000/person/" + id) .mapa(resultado => resultado.json()) .suscríbase a(resultado => { para(deje i = 0; i < este.gente.longitud; i++) { si(este.gente[i]._id == id) { este.gente.empalme(i, 1); romper; } } }); } |
En el HTML tendremos un botón junto a cada elemento de la lista. Este botón nos permitirá eliminar documentos llamando a la función borrar e introduciendo el identificador del documento en cuestión. Si tiene éxito, el elemento será eliminado de la base de datos y localmente en la matriz.
El HTML que subyace a esta lógica se encuentra en el proyecto src/app/list/list.component.html y se parece a esto:
|
1 2 3 4 5 6 |
<a [routerLink]="['/alter']">Nuevo</a> <ul> <li *ngFor="dejar persona de personas"> <a [routerLink]="['/alter', persona._id]">{{ persona.nombre }}</a> - <a estilo="cursor: puntero" (haga clic en)="delete(persona._id)">Borrar</a> </li> </ul> |
En routerLink nos llevará a la página de creación o actualización de datos. Entonces hacemos un bucle a través de nuestro array, presentando cada documento en la pantalla. Si se hace clic en el elemento, navegamos a la página de actualización y pasamos el id del documento en el que se hizo clic. Esto nos permite rellenar previamente el formulario. De lo contrario, si queremos eliminar un documento, hacemos clic en el otro botón.
A pesar de que el contenido central se hace con el lado Angular de las cosas, tenemos que unirlo a través del Router Angular.
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 22 23 24 25 26 27 28 29 30 31 32 33 |
importar { BrowserModule } de @angular/platform-browser; importar { NgModule } de @angular/core; importar { MóduloRouter } de "@angular/router"; importar { HttpModule } de "@angular/http"; importar { Módulo de formularios } de "@angular/forms"; importar { AppComponent } de './app.component'; importar { ListComponent } de "./list/list.component"; importar { AlterComponent } de "./alter/alter.component"; const rutas = [ { ruta: "", componente: ListComponent }, { ruta: "alterar", componente: AlterComponent }, { ruta: "alter/:id", componente: AlterComponent } ]; @NgModule({ declaraciones: [ AppComponent, ListComponent, AlterComponent ], importaciones: [ BrowserModule, HttpModule, MóduloRouter, Módulo de formularios, MóduloRouter.forRoot(rutas) ], proveedores: [], arranque: [AppComponent] }) exportar clase AppModule { } |
Fíjate en que acabamos de importar nuestros componentes y de definir una ruta para cada uno de ellos. No hay nada demasiado sofisticado.
Dependiendo de su versión de la CLI de Angular, puede que tenga que modificar el archivo src/app/app.component.html para que contenga lo siguiente:
|
1 |
<enrutador-salida></enrutador-salida> |
Esto permite que la navegación funcione en el proyecto.
Conclusión
Acaban de ver el material sobre la pila OCEAN y la pila CEAN que presenté en el Angular Orange County y el grupo Couchbase Los Ángeles grupo.
Si eres un desarrollador de MongoDB y estás mirando Couchbase por primera vez, puedes echar un vistazo a un libro de cocina que escribí que se sumerge más profundamente en Ottoman.js y N1QL mientras explica cómo pasar de MongoDB.
Para obtener más información sobre Couchbase y Node.js, consulte la página Portal para desarrolladores de Couchbase.