Recentemente, escrevi um tutorial intitulado, Criação de um armazenamento de perfil de usuário com Node.js e um banco de dados NoSQLpois esse é um assunto e um caso de uso muito popular quando se trata de bancos de dados NoSQL. Nesse tutorial, criamos uma API usando Node.js e Couchbase para armazenar usuários, informações associadas a determinados usuários e sessões expiradas. No entanto, ele era estritamente relacionado ao back-end, sem interface com o usuário.
Queríamos usar a API de armazenamento de perfil de usuário e entender como criar, usando TypeScript e Angular, uma página de perfil que se comunica com cada um dos pontos de extremidade da API.
Este tutorial foi atualizado em 7 de janeiro de 2021 por Eric Bishard para trabalhar com o Angular 11!
Antes de continuar, presumimos que você tenha seguido o tutorial anterior. Também vamos supor que você tenha o CLI angular instalado.
Criar um projeto Angular com os componentes necessários
O front-end do cliente será rudimentar, mas o criaremos do zero. Do ponto de vista do design, ele não terá uma ótima aparência, pois este tutorial trata da funcionalidade e do trabalho com a nossa API. Você é quem decide se quer deixá-lo bonito e oferecer uma experiência do usuário melhor.
Na CLI do Angular, execute o seguinte comando:
|
1 |
ng new couchbase-angular-blog-app |
O comando acima criará um novo projeto no caminho da CLI. Com o projeto criado, precisaremos criar um componente para cada uma das páginas esperadas do aplicativo.
Execute o seguinte com a CLI do Angular:
|
1 2 3 4 |
ng g component login ng g component register ng g component blogs ng g component blog |
Serão criados quatro componentes de perfil de usuário do Angular, cada um com arquivos TypeScript e HTML apropriados. Antes de começarmos a adicionar lógica a eles, precisamos uni-los para fins de navegação usando o modelo de perfil de usuário do Angular abaixo.
Abra o arquivo src/app/app.module.ts e inclua 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 |
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { HttpClientModule } from '@angular/common/http'; import { AppComponent } from './app.component'; import { LoginComponent } from './login/login.component'; import { RegisterComponent } from './register/register.component'; import { BlogsComponent } from './blogs/blogs.component'; import { BlogComponent } from './blog/blog.component'; import { RouterModule } from '@angular/router'; let routes = [ { path: '', redirectTo: '/login', pathMatch: 'full' }, { path: 'login', component: LoginComponent }, { path: 'register', component: RegisterComponent }, { path: 'blogs', component: BlogsComponent }, { path: 'blog', component: BlogComponent } ]; @NgModule({ declarations: [ AppComponent, LoginComponent, RegisterComponent, BlogsComponent, BlogComponent ], imports: [ BrowserModule, FormsModule, HttpClientModule, RouterModule, RouterModule.forRoot(routes) ], providers: [], bootstrap: [AppComponent] }) export class AppModule { } |
O código acima foi criado pela CLI do Angular, no entanto, adicionamos algumas importações de módulos principais e personalizados e os importamos junto com o rotas no @NgModule bloco.
Para ativar nossa navegação, precisaremos atualizar um arquivo. Abra o arquivo src/app/app.component.html e substitua todo o conteúdo pelo seguinte:
|
1 |
<router-outlet></router-outlet> |
Neste ponto, o projeto de perfil de usuário tem uma configuração básica com o Angular.
Vamos dar uma olhada no tratamento da criação de perfis de usuário e no login com as informações da conta.
Manipulação de login e registro
A primeira tela que o usuário verá é a tela de login. O objetivo aqui é coletar um nome de usuário e uma senha, enviá-los à API, obter um ID de sessão como resposta e usá-lo em todas as páginas futuras do aplicativo.
Abra o arquivo src/app/login/login.component.ts e inclua o seguinte 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 |
import { Component, OnInit } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Router } from '@angular/router'; @Component({ selector: 'app-login', templateUrl: './login.component.html', styleUrls: ['./login.component.css'] }) export class LoginComponent implements OnInit { public input: any; constructor(private http: HttpClient, private router: Router) { this.input = { 'email': '', 'password': '' }; } ngOnInit(): void { } public login() { if (this.input.email && this.input.password) { let headers = new HttpHeaders({ 'content-type': 'application/json' }); this.http.post('https://localhost:3000/login', JSON.stringify(this.input), { headers: headers }) .subscribe(result => this.router.navigate(['/blogs'], { 'queryParams': result }) ); } } } |
Há algumas coisas importantes acontecendo no código acima, portanto, vamos detalhá-lo.
Nossa intenção é vincular um objeto a um formulário na marcação HTML. Esse objeto será o entrada variável. Na construtor definimos cada propriedade possível como um valor vazio que será refletido na interface do usuário.
Quando o usuário decide fazer login, as informações de cabeçalho apropriadas são definidas para a solicitação e o objeto é enviado ao servidor. Se for bem-sucedida, a resposta será um objeto com o ID da sessão. Vamos passá-lo para a próxima página como um parâmetro de consulta.
A marcação HTML que combina com esse TypeScript é encontrada no arquivo src/app/login/login.component.html e ele se parece com o seguinte:
Entrar
|
1 |
Uma observação que devo mencionar aqui é que, se você digitar a senha errada, não criamos nenhuma forma de informar ao usuário que o nome de usuário ou a senha usada não estava correta.
O que é importante aqui é o uso do [(ngModel)] que são usados para vinculação de dados. Também oferecemos navegação para a página de registro por meio do atributo [routerLink] atributo.
Então, como é o componente de registro?
Abra o arquivo src/app/register/register.component.ts e inclua o seguinte 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 |
import { Component, OnInit } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Router } from '@angular/router'; @Component({ selector: 'app-register', templateUrl: './register.component.html', styleUrls: ['./register.component.css'] }) export class RegisterComponent implements OnInit { public input: any; public constructor(private http: HttpClient, private router: Router) { this.input = { 'firstname': '', 'lastname': '', 'email': '', 'password': '' }; } ngOnInit(): void { } public register() { if (this.input.email && this.input.password) { let headers = new HttpHeaders({ 'content-type': 'application/json' }); this.http.post('https://localhost:3000/account', JSON.stringify(this.input), { headers: headers }) .subscribe(() => this.router.navigate(['/login']) ); } } } |
Você perceberá que o código de registro é semelhante ao código de login. Estamos apenas coletando dados de formulário e enviando-os para um endpoint de API diferente.
O HTML correspondente encontrado no arquivo src/app/register/register.component.html tem a seguinte aparência:
Registro
|
1 |
O HTML contém elementos de formulário vinculados ao [(ngModel)] bem como um link para a página de login.
Lembre-se de que, depois de fazermos o login, estamos passando a sessão para as páginas específicas do usuário. Isso nos permitirá obter as informações do usuário em nosso armazenamento de perfis.
Criação e exibição de artigos de blog para um usuário específico no Profile Store
Depois de fazer login, o usuário é levado a uma página em que pode visualizar uma lista de artigos de blog que escreveu. Lembre-se de que nosso backend está conectando documentos NoSQL por meio de um ID de perfil que estamos definindo.
Para visualizar os artigos do blog, o ID da sessão passado da página anterior precisa ser definido como um cabeçalho em uma solicitação. O resultado da solicitação pode ser imediatamente renderizado na tela.
Abra o arquivo src/app/blogs/blogs.component.ts e inclua o seguinte 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 |
import { Component, OnInit } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Router, ActivatedRoute } from '@angular/router'; @Component({ selector: 'app-blogs', templateUrl: './blogs.component.html', styleUrls: ['./blogs.component.css'] }) export class BlogsComponent implements OnInit { private sid: string; public entries: any; public constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute ) { this.entries = []; } public ngOnInit() { this.route.queryParams.subscribe(params => { this.sid = params['sid']; let headers = new HttpHeaders({ 'authorization': 'Bearer ' + params['sid'] }); this.http.get('https://localhost:3000/blogs', { headers: headers }) .subscribe(result => { this.entries = result; console.log(result) }); }); } public create() { this.router.navigate(['/blog'], { 'queryParams': { 'sid': this.sid } }); } } |
Esse arquivo segue uma estratégia semelhante à forma como lidamos com o login e o registro.
Em termos de variáveis, criamos uma variável privada chamada lado que manterá a sessão passada da página anterior. O entradas será uma matriz de entradas de blog para um indivíduo retornado do endpoint da API.
Quando a página é carregada, devemos exibir as entradas do blog. Nunca é uma boa ideia carregar ou solicitar dados de dentro do construtor portanto, em vez disso, usamos o método ngOnInit método.
|
1 2 3 4 5 6 7 8 9 10 11 |
public ngOnInit() { this.route.queryParams.subscribe(params => { this.sid = params['sid']; let headers = new HttpHeaders({ 'authorization': 'Bearer ' + params['sid'] }); this.http.get('https://localhost:3000/blogs', { headers: headers }) .subscribe(result => { this.entries = result; console.log(result) }); }); } |
No ngOnInit podemos pegar os parâmetros passados e construir um cabeçalho de autorização. Em seguida, fazemos uma solicitação ao nosso endpoint que contém o cabeçalho.
Para criar uma nova entrada de blog, podemos passar o parâmetro lado para a próxima rota:
|
1 2 3 |
public create() { this.router.navigate(['/blog'], { 'queryParams': { 'sid': this.sid } }); } |
Isso é exatamente como o que vimos na tela de login.
A marcação HTML que alimenta a interface do usuário não será mais complexa do que a lógica TypeScript que a alimenta. O HTML pode ser visto na seção src/app/blogs/blogs.component.html assim:
Blogs que você escreveu
|
{{ entry.blog.title }}
{{ entry.blog.content }}
A criação de uma entrada de blog terá uma lógica semelhante à que já vimos. Abra a seção src/app/blog/blog.component.ts e inclua o seguinte 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 |
import { Component, OnInit } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { ActivatedRoute } from '@angular/router'; import { Location } from '@angular/common'; @Component({ selector: 'app-blog', templateUrl: './blog.component.html', styleUrls: ['./blog.component.css'] }) export class BlogComponent implements OnInit { private sid: string; public input: any; public constructor(private http: HttpClient, private route: ActivatedRoute, private location: Location) { this.input = { 'title': '', 'content': '' }; } public ngOnInit() { this.route.queryParams.subscribe(params => { this.sid = params['sid']; }); } public save() { if (this.input.title && this.input.content) { let headers = new HttpHeaders({ 'content-type': 'application/json', 'authorization': 'Bearer ' + this.sid }); this.http.post('https://localhost:3000/blog', JSON.stringify(this.input), { headers: headers }) .subscribe(() => this.location.back() ); } } } |
No código acima, estamos inicializando os dados do formulário e recuperando o lado que foi passado da página anterior.
Quando tentamos salvar a entrada, construímos um cabeçalho apropriado que contém o ID da sessão e o enviamos para a API com os dados do formulário. Uma vez concluído, podemos navegar para trás na pilha.
O HTML para a interface do usuário dessa página está no arquivo src/app/blog/blog.component.html assim:
Nova entrada no blog
Manter a consistência em mente
Por padrão, o Couchbase tentará ser rápido, o que significa que ele pode retornar dados mais rapidamente do que a atualização dos índices. Quando isso acontece, você pode ter dados recém-criados ausentes em seus resultados.
Expliquei como corrigir isso em um tutorial anteriormas uma rápida recapitulação está abaixo.
|
1 2 3 4 5 6 7 8 9 10 11 |
const query = `SELECT * FROM `blog` WHERE type = 'blog' AND pid = $PID;` const options = { scanConsistency: couchbase.QueryScanConsistency.RequestPlus, parameters: { PID: request.pid } } await cluster.query(query, options) .then((result) => { console.log(result.rows) response.send(result.rows) }) .catch((e) => response.status(500).send(e)) |
Em nosso blogs podemos definir a consistência da varredura e, nesse caso, estamos dizendo a ele para esperar até que o índice seja atualizado antes de retornar os dados. Essa é uma operação rápida, portanto, não pense que ela será rastreada quando você fizer isso. Apenas não será tão rápido quanto o padrão.
Nesse ponto, o front-end deve funcionar perfeitamente com o back-end!
Para executar os dois projetos juntos.
Primeiro, verifique se o Couchbase está instalado e se você tem um bucket chamado blog criado e o índice que criamos no tutorial da API.
Em seguida, execute a API REST do Node:
|
1 |
node server |
Em seguida, execute o projeto Angular:
|
1 |
ng serve |
Registre-se, faça login, crie uma ou duas postagens de blog e verifique se a rota de blogs está mostrando cada um dos blogs que você criou.
Como você pode melhorar este projeto
Como mencionamos anteriormente, o projeto poderia usar CSS e layout personalizados, bem como validação de formulário e alertas ou notificações, caso o usuário tenha tentado usar a senha errada ou o título do blog exceda um determinado número de caracteres, ou se um erro tiver sido lançado porque a API não está sendo executada corretamente.
Criar um Edição do blog página e rota e um link do título das postagens do blog na página Rota/página de blogs.
Criar navegação e estrutura sofisticadas para o aplicativo.
Reordenar a lista de blogs na seção Rota/página de blogs ou configurar a capacidade de classificar blogs por título em ordem crescente ou decrescente.
Permita que os usuários façam login com logins sociais ou use este projeto que criamos e adicione uma rota de portfólio e outras páginas de suporte como um ponto de partida para seu próprio portfólio de desenvolvedor.
Crie um serviço Angular para lidar com o ID da sessão, de modo que não seja necessário transmiti-lo a cada evento de navegação.
Conclusão
Você acabou de ver como criar um front-end de cliente simples usando o Angular 11 e o TypeScript para o armazenamento de perfil de usuário que vimos em um exemplo anterior. Cada modelo de perfil de usuário do Angular é diferente, é claro. Esse front-end de cliente é um dos muitos exemplos possíveis de página de perfil de usuário de material angular para um front-end, porque esse é um aplicativo modular de pilha completa. Isso significa que o back-end pode estar em qualquer idioma e o front-end pode estar em qualquer idioma.
O código finalizado está disponível no GitHub em couchbaselabs / couchbase-angular-blog-app no GitHub.
Para obter mais informações sobre como usar o Couchbase com o Node.js, consulte o Portal do desenvolvedor do Couchbase.