Se você está acompanhando, sou um grande defensor do desenvolvimento do Node.js e da pilha de desenvolvimento JavaScript. Anteriormente, escrevi sobre o Pilha Couchbase, Express, AngularJS e Node.js (CEAN) e modernizamos a API de back-end com o Hapi.jsuma alternativa popular ao Express. Sou um grande fã do Angular, mas recentemente tenho explorado a cada vez mais popular estrutura Vue.js.
Veremos como criar um aplicativo de pilha completa usando a pilha JavaScript que consiste em Node.js, Hapi, Vue.js e Couchbase NoSQL. Como todos os caracteres iniciais das tecnologias são consoantes, não tentarei criar um acrônimo para elas.
O aplicativo que criamos terá um modelo de dados semi-simplista. Armazenaremos informações sobre pessoas e endereços e configuraremos como os endereços estão relacionados a determinadas pessoas.
O backend do Node.js com Hapi demonstrará o uso de N1QL e mutações de subdocumentos no banco de dados. O Vue.js nos dará uma saída semi-atrativa para trabalhar com nossa API.
Configuração do Couchbase para consulta e controle de acesso com base em função
Antes de começarmos a desenvolver nossa API RESTful que se comunica com o banco de dados NoSQL do Couchbase, o banco de dados deve ser configurado corretamente. Vamos supor que você já tenha instalado o Servidor Couchbase 5 ou superior.
Com o Couchbase pronto para funcionar, precisamos criar um Bucket para armazenar nossos dados. Este tutorial fará referência a um Bucket chamado exemplomas isso realmente não importa, desde que você seja consistente.
O Bucket não precisa de nenhuma configuração especial para este exemplo.
Com o Bucket disponível, precisamos criar um usuário com permissão para trabalhar com o Bucket. Para obter informações sobre a criação de controle de acesso baseado em função (RBAC), consulte um artigo anterior que escrevi intitulado, Proteja seus dados NoSQL com o controle de acesso baseado em função do Couchbase. A conta precisará de Leitor de dados, Gravador de dadose Seleção de consulta funções. Isso nos permitirá realizar operações CRUD no banco de dados, bem como executar consultas N1QL.
Por fim, precisamos preparar um índice para suporte a consultas N1QL.
Em um ambiente de produção, você deverá criar um índice para cada consulta que deseja executar. Para o nosso exemplo, usaremos um índice primário que é um índice de prototipagem.
Execute a seguinte consulta para criar o índice:
1 |
CRIAR PRIMÁRIO ÍNDICE ON `exemplo`; |
O índice primário nos proporcionará a conveniência de poder executar qualquer consulta em nosso Bucket, mas com o custo do desempenho. Os índices personalizados proporcionarão um desempenho muito melhor.
Nesse momento, podemos iniciar o desenvolvimento.
Desenvolvimento de um back-end da Web com o Node.js e a estrutura Hapi
Com o Couchbase pronto para uso, podemos começar a desenvolver o aplicativo Node.js com Hapi. Se você já viu meu Artigo anteriorNo caso do Node js e do Couchbase, grande parte do material será mantido. No entanto, este exemplo será um pouco mais completo em relação ao que o Node.js e o Couchbase podem fazer.
Supondo que você tenha o Node.js instalado, precisamos criar um novo projeto. Na CLI, execute o seguinte:
1 2 |
npm inicial -y npm instalar hapi joi couchbase uuid --salvar |
Os comandos acima inicializarão um novo projeto e instalarão o hapi
para o Hapi.js, o pacote joi
para validação de dados, o pacote couchbase
para interação com o Couchbase, e o pacote uuid
para gerar cadeias de caracteres exclusivas.
Em seguida, crie um app.js que conterá todo o nosso código Node.js. Para começar, adicione o seguinte ao arquivo app.js file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
const Couchbase = exigir("couchbase"); const Hapi = exigir("hapi"); const Joi = exigir("joi"); const UUID = exigir("uuid"); const servidor = novo Hapi.Servidor(); const agrupamento = novo Couchbase.Aglomerado("couchbase://localhost"); agrupamento.autenticar("demo", "123456"); const balde = agrupamento.openBucket("exemplo"); servidor.conexão({ hospedeiro: "localhost", porto: 3000, rotas: { cors: verdadeiro } }); balde.em("error" (erro), erro => { lançar erro; }); servidor.iniciar(erro => { se(erro) { lançar erro; } console.registro("Ouvindo em " + servidor.informações.uri); }); |
O código acima importará nossas dependências, se conectará à nossa instância do Couchbase usando as informações que especificamos na etapa anterior e configurará nosso servidor Hapi para operar em http://localhost:3000.
Ao definir nossas informações de conexão, optamos por ativar o compartilhamento de recursos entre origens (CORS). Isso permitirá que nosso aplicativo Vue.js se comunique com o aplicativo Node.js, mesmo que eles estejam operando em portas diferentes. Mais informações sobre CORS com o Hapi podem ser encontradas em um Artigo anterior que eu escrevi.
Neste ponto, podemos começar a definir as rotas de endpoint para nossa API.
O objetivo é criar dados para pessoas e endereços no Couchbase. Como atualmente não existem dados para essas duas categorias, faz sentido começar com endpoints que executam a criação de dados.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
servidor.rota({ método: "POST", caminho: "/pessoa", configuração: { validar: { carga útil: { primeiro nome: Joi.string().necessário(), sobrenome: Joi.string().necessário(), tipo: Joi.string().proibido().padrão("pessoa"), carimbo de data/hora: Joi.qualquer().proibido().padrão((novo Data).getTime()) } } }, manipulador: (solicitação, resposta) => { var id = UUID.v4(); balde.inserir(id, solicitação.carga útil, (erro, resultado) => { se(erro) { retorno resposta({ código: erro.código, mensagem: erro.mensagem }).código(500); } solicitação.carga útil.id = id; resposta(solicitação.carga útil); }); } }); |
O código acima criará um endpoint que aceita solicitações POST. Supondo que a carga útil de JSON enviada com a solicitação atenda aos critérios da lógica de validação, o manipulador
será usado. Será gerada uma identificação exclusiva e a carga útil será salva com a identificação criada. Em caso de sucesso, a carga útil com o ID será devolvida ao cliente.
Uma lógica semelhante pode ser usada na criação de endereços:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
servidor.rota({ método: "POST", caminho: "/address", configuração: { validar: { carga útil: { cidade: Joi.string().necessário(), estado: Joi.string().necessário(), tipo: Joi.string().proibido().padrão("endereço"), carimbo de data/hora: Joi.qualquer().proibido().padrão((novo Data).getTime()) } } }, manipulador: (solicitação, resposta) => { var id = UUID.v4(); balde.inserir(id, solicitação.carga útil, (erro, resultado) => { se(erro) { retorno resposta({ código: erro.código, mensagem: erro.mensagem }).código(500); } solicitação.carga útil.id = id; resposta(solicitação.carga útil); }); } }); |
A lógica de validação dos endereços é um pouco diferente, mas todo o resto permanece o mesmo. Essa mesma lógica pode ser transferida para praticamente qualquer endpoint de criação com solicitações POST.
Com os documentos disponíveis no Couchbase, podemos tentar consultá-los. Faz sentido criar uma consulta especializada que encontre pessoa
documentos ou endereço
documentos. Para fazer isso, usaremos o N1QL.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
servidor.rota({ método: "GET", caminho: "/addresses", manipulador: (solicitação, resposta) => { var declaração = "SELECT META(address).id, address.* FROM example AS address WHERE address.type = 'address'"; var consulta = Couchbase.N1qlQuery.fromString(declaração); balde.consulta(consulta, (erro, resultado) => { se(erro) { retorno resposta({ código: erro.código, mensagem: erro.mensagem }).código(500); } resposta(resultado); }); } }); |
A rota acima usará uma consulta N1QL que obtém todos os documentos e suas chaves de documento, desde que contenham uma propriedade chamada tipo
que é igual a endereço
.
Da mesma forma, poderíamos fazer a mesma coisa para pessoa
documentos:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
servidor.rota({ método: "GET", caminho: "/people", manipulador: (solicitação, resposta) => { var declaração = "SELECT META(person).id, person.* FROM example AS person WHERE person.type = 'person'"; var consulta = Couchbase.N1qlQuery.fromString(declaração); balde.consulta(consulta, (erro, resultado) => { se(erro) { retorno resposta({ código: erro.código, mensagem: erro.mensagem }).código(500); } resposta(resultado); }); } }); |
No entanto, isso é enfadonho, apenas consultar documentos com base em suas tipo
propriedade. Provavelmente, devemos estabelecer uma relação entre os dois tipos.
A ideia aqui é criar uma matriz dentro do pessoa
documentos que contêm um ID para cada endereço ao qual estão associados. Em vez de obter um documento inteiro, criar ou atualizar a matriz e salvar novamente, faremos uma mutação de subdocumento diretamente no banco de dados.
Veja o seguinte endpoint, por exemplo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
servidor.rota({ método: "PUT", caminho: "/person/address/{personid}", configuração: { validar: { carga útil: { endereço: Joi.string().necessário() } } }, manipulador: (solicitação, resposta) => { balde.mutateIn(solicitação.parâmetros.personid).arrayAppend("endereços", solicitação.carga útil.endereço, verdadeiro).executar((erro, resultado) => { se(erro) { retorno resposta({ código: erro.código, mensagem: erro.mensagem }).código(500); } balde.obter(solicitação.parâmetros.personid, (erro, resultado) => { se(erro) { retorno resposta({ código: erro.código, mensagem: erro.mensagem }).código(500); } resposta(resultado.valor); }); }); } }); |
O ponto de extremidade acima espera que haja um parâmetro de rota e uma carga útil em cada solicitação. O parâmetro de rota é o ID de um pessoa
e o payload conterá um id de um documento endereço
documento.
Ao usar o mutateIn
podemos fornecer um documento a ser alterado e um caminho para a propriedade que deve ser alterada. Nesse caso, o pessoa
será alterado, e estaremos adicionando valores a um documento endereços
dentro dele. Se o endereços
não existe, não se preocupe, ela será criada.
Depois que a mutação ocorrer, vamos extrair todo o documento que sofreu mutação e devolvê-lo ao cliente.
Agora podemos fazer uma consulta N1QL mais interessante em nossos dados. Confira este endpoint revisado para coletar pessoa
documentos:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
servidor.rota({ método: "GET", caminho: "/people", manipulador: (solicitação, resposta) => { var declaração = "SELECT META(person).id, person.firstname, person.lastname, (SELECT address.city, address.state FROM example AS address USE KEYS person.addresses) AS addresses FROM example AS person WHERE person.type = 'person'"; var consulta = Couchbase.N1qlQuery.fromString(declaração); balde.consulta(consulta, (erro, resultado) => { se(erro) { retorno resposta({ código: erro.código, mensagem: erro.mensagem }).código(500); } resposta(resultado); }); } }); |
Agora estamos fazendo uma subconsulta. Não estamos fazendo apenas uma consulta simples porque uma matriz de valores de identificação de endereço não é muito útil para nós. Em vez disso, a subconsulta carrega esses valores de id para que nossos resultados tenham endereços reais neles.
Legal, não é?
Vamos concluir nossa API RESTful com um último endpoint.
1 2 3 4 5 6 7 8 9 10 11 12 |
servidor.rota({ método: "GET", caminho: "/address/{addressid}", manipulador: (solicitação, resposta) => { balde.obter(solicitação.parâmetros.endereço, (erro, resultado) => { se(erro) { retorno resposta({ código: erro.código, mensagem: erro.mensagem }).código(500); } resposta(resultado.valor); }); } }); |
O ponto de extremidade acima nos permitirá retornar um único endereço específico com base em sua chave de documento.
Com a API criada, você poderia testá-la facilmente com o Postman ou uma ferramenta semelhante. No entanto, vamos criar um frontend para ela com o Vue.js.
Criação de um front-end de cliente com o Vue.js
A ideia por trás do frontend é que estaremos fazendo solicitações HTTP para o backend que acabamos de criar. A maior parte do nosso trabalho será nas solicitações, na vinculação de dados e na atratividade geral da nossa interface do usuário.
Se você for novo no Vue.js, certifique-se de obter a CLI do Vue. Em um novo diretório, execute o seguinte com a CLI do Vue:
1 |
valor inicial webpack front-end |
O comando acima iniciará o processo de scaffolding para o Vue.js. Selecione não para tudo o que for solicitado, pois não usaremos todos esses recursos. Quando se trata de um projeto autônomo (compilador e tempo de execução) versus somente tempo de execução, isso não importa para este exemplo.
Quando o andaime estiver pronto, execute os seguintes comandos:
1 2 |
cd front-end npm instalar |
Os comandos acima farão o download de todas as dependências necessárias para o projeto básico. No entanto, precisamos de uma dependência para fazer solicitações HTTP com o Vue.js. Execute o seguinte na CLI:
1 |
npm instalar áxis --salvar |
Este projeto usará o áxis pacote. Para obter mais informações sobre o axios e fazer solicitações HTTP com o Vue.js, confira um tutorial anterior que escrevi intitulado, Consumir dados de API remota via HTTP em um aplicativo da Web Vue.js.
Em seu projeto, você deve ter um src/App.vue arquivo. Para simplificar, este será um aplicativo de página única e arquivo único. Ignore quaisquer outros componentes que tenham sido criados com o scaffold.
Antes de começarmos a adicionar a marcação HTML e a lógica JavaScript, vamos incluir Bootstrap como nossa estrutura de temas. Abra a seção index.html e faça com que ele se pareça com o seguinte:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<!DOCTYPE html> <html> <cabeça> <meta conjunto de caracteres="utf-8"> <título>valor-projeto</título> <link rel="folha de estilo" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" /> </cabeça> <corpo> <div id="aplicativo"></div> <!-- construído arquivos vontade ser automático injetado --> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> </corpo> </html> |
A maior parte das informações acima foi extraída do Documentação de introdução ao Bootstrap. Agora, quando começarmos a codificar no src/App.vue arquivo, ele parecerá um pouco mais atraente.
Como na maioria dos arquivos de projeto do Vue, há um <template>
, <script>
e <style>
bloco. Nosso <style>
não terá nada de especial adicionado, portanto, abra o bloco src/App.vue e inclua o seguinte:
1 2 3 4 5 6 7 8 9 |
<style> #app { família de fontes: "Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: escala de cinza; cor: #2c3e50; margem superior: 30px; } </style> |
A maior parte do nosso trabalho estará nos outros dois blocos de código. Para simplificar, começaremos com a lógica e depois terminaremos com a interface do usuário.
No âmbito do projeto src/App.vue inclua o seguinte 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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
<script> importação áxis de "axios"; exportação padrão { nome: 'aplicativo', dados() { retorno { entrada: { pessoa: { primeiro nome: "", sobrenome: "" }, endereço: { cidade: "", estado: "" }, endereço: "" }, pessoas: [], endereços: [] } }, montado() { áxis({ método: "GET", url: "http://localhost:3000/people" }).então(resultado => { este.pessoas = resultado.dados; }); áxis({ método: "GET", url: "http://localhost:3000/addresses" }).então(resultado => { este.endereços = resultado.dados; }); }, métodos: { createPerson() { se(este.entrada.pessoa.primeiro nome != "" && este.entrada.pessoa.sobrenome != "") { áxis({ método: "POST", url: "http://localhost:3000/person", dados: este.entrada.pessoa, cabeçalhos: { "content-type": "application/json" }}).então(resultado => { este.pessoas.empurrar(resultado.dados); este.entrada.pessoa.primeiro nome = ""; este.entrada.pessoa.sobrenome = ""; }); } }, createAddress() { se(este.entrada.endereço.cidade != "" && este.entrada.endereço.estado != "") { áxis({ método: "POST", url: "http://localhost:3000/address", dados: este.entrada.endereço, cabeçalhos: { "content-type": "application/json" }}).então(resultado => { este.endereços.empurrar(resultado.dados); este.entrada.endereço.cidade = ""; este.entrada.endereço.estado = ""; }); } }, linkAddress(personid) { se(este.entrada.endereço != indefinido && personid != "") { áxis({ método: "PUT", url: "http://localhost:3000/person/address/" + personid, dados: { endereço: este.entrada.endereço }, cabeçalhos: { "content-type": "application/json" }}).então(resultado => { para(deixar i = 0; i < este.pessoas.comprimento; i++) { se(este.pessoas[i].id == personid) { se(este.pessoas[i].endereços == indefinido) { este.pessoas[i].endereços = []; } áxis({ método: "GET", url: "http://localhost:3000/address/" + este.entrada.endereço }).então(resultado => { este.pessoas[i].endereços.empurrar(resultado.dados); este.entrada.endereço = ""; }); } } }); } } } } </script> |
Há muita coisa acontecendo no texto acima, portanto, vamos detalhá-lo.
A primeira parte central da lógica que adicionamos está relacionada à inicialização de dados. A dados
nos permitirá inicializar as variáveis usadas em todo esse arquivo específico.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
dados() { retorno { entrada: { pessoa: { primeiro nome: "", sobrenome: "" }, endereço: { cidade: "", estado: "" }, endereço: "" }, pessoas: [], endereços: [] } }, |
No dados
método, o entrada
será vinculado a um formulário na interface do usuário. No nosso caso, teremos dois formulários, um para pessoa
e um para endereço
. Por padrão, queremos que os valores fiquem em branco. Também estamos inicializando nossa lista de pessoas
e nossa lista de endereços
que será obtido da API do Node.js.
Após a inicialização das variáveis, precisamos carregar alguns dados do servidor. Isso pode ser feito na seção montado
método.
1 2 3 4 5 6 7 8 |
montado() { áxis({ método: "GET", url: "http://localhost:3000/people" }).então(resultado => { este.pessoas = resultado.dados; }); áxis({ método: "GET", url: "http://localhost:3000/addresses" }).então(resultado => { este.endereços = resultado.dados; }); }, |
Quando o aplicativo é carregado, é feita uma solicitação de pessoas e uma solicitação de endereços.
Isso nos leva à lista de métodos que podem ser chamados a partir do HTML. O método createPerson
pegará os dados do formulário da pessoa e os enviará à API para serem salvos. Da mesma forma, o método createAddress
fará o mesmo, mas com informações de endereço.
O linkAddress
é um pouco diferente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
linkAddress(personid) { se(este.entrada.endereço != indefinido && personid != "") { áxis({ método: "PUT", url: "http://localhost:3000/person/address/" + personid, dados: { endereço: este.entrada.endereço }, cabeçalhos: { "content-type": "application/json" }}).então(resultado => { para(deixar i = 0; i < este.pessoas.comprimento; i++) { se(este.pessoas[i].id == personid) { se(este.pessoas[i].endereços == indefinido) { este.pessoas[i].endereços = []; } áxis({ método: "GET", url: "http://localhost:3000/address/" + este.entrada.endereço }).então(resultado => { este.pessoas[i].endereços.empurrar(resultado.dados); este.entrada.endereço = ""; }); } } }); } } |
O linkAddress
pegará um determinado ID de pessoa e um determinado ID de endereço e os enviará ao nosso endpoint da API de subdocumento. Quando terminar, ele atualizará as informações nas variáveis locais para exibição na interface do usuário.
Isso nos leva à parte HTML do nosso arquivo de aplicativo. Dentro da seção <template>
bloco, deveríamos ter algo como o seguinte:
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 58 |
<modelo> <div id="aplicativo"> <div classe="contêiner"> <div classe="row" (linha)> <div classe="col-md-6"> <div classe="bem"> <formulário> <div classe="form-group" (grupo de formulários)> <rótulo para="firstname">Primeiro Nome</rótulo> <entrada tipo="texto" v-modelo="input.person.firstname" classe="form-control" (controle de formulário) id="firstname" espaço reservado="Primeiro nome"> </div> <div classe="form-group" (grupo de formulários)> <rótulo para="lastname" (sobrenome)>Último Nome</rótulo> <entrada tipo="texto" v-modelo="input.person.lastname" classe="form-control" (controle de formulário) id="lastname" (sobrenome) espaço reservado="Sobrenome"> </div> <botão tipo="botão" v-em:clique="createPerson()" classe="btn btn-default">Salvar</botão> </formulário> </div> </div> <div classe="col-md-6"> <div classe="bem"> <formulário> <div classe="form-group" (grupo de formulários)> <rótulo para="cidade">Cidade</rótulo> <entrada tipo="texto" v-modelo="input.address.city" classe="form-control" (controle de formulário) id="cidade" espaço reservado="Cidade"> </div> <div classe="form-group" (grupo de formulários)> <rótulo para="estado">Estado</rótulo> <entrada tipo="texto" v-modelo="input.address.state" classe="form-control" (controle de formulário) id="estado" espaço reservado="Estado"> </div> <botão tipo="botão" v-em:clique="createAddress()" classe="btn btn-default">Salvar</botão> </formulário> </div> </div> </div> <div classe="row" (linha)> <div classe="col-md-12"> <ul classe="grupo de listas"> <li v-para="(pessoa, índice) em pessoas" classe="list-group-item"> {{ pessoa.primeiro nome }} {{ pessoa.sobrenome }} - <extensão v-para="(endereço, índice) em person.addresses"> {{ endereço.cidade }}, {{ endereço.estado }} / </extensão> <p> <formulário> <div v-para="(endereço, índice) em endereços"> <entrada tipo="rádio" nome="addressid" v-vincular:valor="address.id" v-modelo="input.addressid"> {{ endereço.cidade }}, {{ endereço.estado }} </div> <botão tipo="botão" v-em:clique="linkAddress(person.id)" classe="btn btn-default">Salvar</botão> </formulário> </p> </li> </ul> </div> </div> </div> </div> </modelo> |
A maior parte do HTML acima é um boilerplate do Bootstrap. Se você já trabalhou com o Bootstrap antes, saberá que há muita preparação envolvida.
Vá para a seção a seguir:
1 2 3 4 5 6 7 8 9 10 11 |
<formulário> <div classe="form-group" (grupo de formulários)> <rótulo para="firstname">Primeiro Nome</rótulo> <entrada tipo="texto" v-modelo="input.person.firstname" classe="form-control" (controle de formulário) id="firstname" espaço reservado="Primeiro nome"> </div> <div classe="form-group" (grupo de formulários)> <rótulo para="lastname" (sobrenome)>Último Nome</rótulo> <entrada tipo="texto" v-modelo="input.person.lastname" classe="form-control" (controle de formulário) id="lastname" (sobrenome) espaço reservado="Sobrenome"> </div> <botão tipo="botão" v-em:clique="createPerson()" classe="btn btn-default">Salvar</botão> </formulário> |
Observe que o entrada
que havíamos inicializado, agora está vinculado aos elementos do formulário. Essa é uma vinculação de dados bidirecional.
Quando o botão do formulário é pressionado para esse formulário específico, o botão createPerson
é chamado. O outro elemento do formulário se comporta da mesma forma.
Na parte principal da interface do usuário, temos uma lista de dados recuperados do servidor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<ul classe="grupo de listas"> <li v-para="(pessoa, índice) em pessoas" classe="list-group-item"> {{ pessoa.primeiro nome }} {{ pessoa.sobrenome }} - <extensão v-para="(endereço, índice) em person.addresses"> {{ endereço.cidade }}, {{ endereço.estado }} / </extensão> <p> <formulário> <div v-para="(endereço, índice) em endereços"> <entrada tipo="rádio" nome="addressid" v-vincular:valor="address.id" v-modelo="input.addressid"> {{ endereço.cidade }}, {{ endereço.estado }} </div> <botão tipo="botão" v-em:clique="linkAddress(person.id)" classe="btn btn-default">Salvar</botão> </formulário> </p> </li> </ul> |
Estamos listando as pessoas, as informações de endereço expandidas de nossa consulta N1QL do lado do servidor e a interação do botão de opção para emitir a solicitação de subdocumento.
Tudo isso é feito facilmente com o Vue.js devido à forma como a interface do usuário está vinculada à camada lógica.
Conclusão
Você acabou de ver como criar um aplicativo de pilha completa composto estritamente de tecnologias JavaScript. Usamos o Node.js com a estrutura Hapi.js para a camada de backend da API e a estrutura Vue.js para a camada de frontend do navegador da Web. O frontend consome dados do backend e o backend obtém seus dados do nosso Couchbase Banco de dados NoSQL.
Como tornamos o aplicativo muito modular, cada um dos componentes pode ser trocado por uma tecnologia diferente. Poderíamos trocar o Node.js por Java ou outra coisa e poderíamos trocar o Vue.js por outra coisa, como o Angular. Se quiser ver como usar pilha completa com Golang e Angular, consulte este tutorial que escrevi com o título, Crie um banco de dados de filmes em pilha completa com Golang, Angular e NoSQL.
Para obter mais informações sobre como usar o Couchbase com o Node.js, consulte o Portal do desenvolvedor do Couchbase.