Nesta segunda e última parte da série, implementaremos em Angular 7, nosso chatbot.
Para começar, abra o terminal no diretório de sua preferência e crie um projeto. Para mais detalhes na geração do projeto, veja meu artigo.
No arquivo app.module.ts vamos importar dois módulos. O HttClientModule e o FormsModule.
boot.service.ts
// Outros imports omitidos
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from "@angular/common/http";
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
FormsModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
O próximo passo é a criação do serviço que acessa a API do DialogFlow.
No terminal de comando, informe:
> ng g service boot
Comentando o código do BootService:
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class BootService {
private baseURL: string = "https://api.dialogflow.com/v1/query?v=20150910";
private token: string = 'SEU TOKEN'
constructor(private http: HttpClient) { }
public getResponse(query: string) {
let data = {
query: query,
lang: 'en',
sessionId: '12345'
}
return this.http
.post(`${this.baseURL}`, data, { headers: this.getHeaders() })
}
public getHeaders() {
let headers = new HttpHeaders();
headers = headers.set('Authorization', `Bearer ${this.token}`);
return headers;
}
}
Vamos declarar duas propriedades para o endereço e o token (linhas 9 e 10). O token é obtido nas configurações do projeto, no console do DialogFlow.

O método getResponse será responsável pelas interações com o Agente. Realizamos um post da classe HttpClient, injetado no método construtor (linha 12).
Basicamente informamos o endereço, alguns parâmetros na query e o token de autorização no cabeçalho (linha 22), passado no método getHeaders (linha 26).
Finalizado o serviço, a próxima etapa é a codificação do script.
No arquivo app.component.ts, definimos uma interface para mapear os campos do chat. A interface Message possui dois atributos string e um campo Date(linhas 5 a 9). Isso nos ajudará a formatar os campos com base nas mensagens do bot ou do usuário.
import { element } from 'protractor';
import { BootService } from './boot.service';
import { Component, ViewChild, ElementRef } from '@angular/core';
export interface Message {
remetente?: string;
mensagem: string;
data?: Date;
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
@ViewChild('scrollMe') private myScrollContainer: ElementRef;
msg: string;
resultados: Message[]
constructor(private chatBoot: BootService) {
this.initBoot()
}
initBoot() {
this.resultados = []
this.chatBoot.getResponse('oi')
.subscribe((lista: any) => {
lista.result.fulfillment.messages.forEach((element) => {
this.resultados.push({ remetente: 'boot', mensagem: element.speech, data: lista.timestamp })
});
})
}
sendMessage() {
this.resultados.push({ remetente: 'eu', mensagem: this.msg, data: new Date() })
this.chatBoot.getResponse(this.removerAcentos(this.msg))
.subscribe((lista: any) => {
lista.result.fulfillment.messages.forEach((element) => {
this.resultados.push({ remetente: 'boot', mensagem: element.speech, data: lista.timestamp })
});
})
this.msg = '';
}
ngAfterViewChecked() {
this.scrollToBottom();
}
scrollToBottom(): void {
try {
this.myScrollContainer.nativeElement.scrollTop = this.myScrollContainer.nativeElement.scrollHeight;
} catch (err) { }
}
private removerAcentos(s) {
return s.normalize('NFD').replace(/[\u0300-\u036f]/g, "")
}
}
Na linha 18 vamos controlar o elemento DOM para acionar o scroll de uma div, conforme a inclusão de mais mensagens.
A linha 20 é o input do usuário e a linha 21 um array de Message de respostas do boot.
No método initBoot(), iniciamos a primeira chamada ao Agente, passando um ‘oi’ (linha 29). Esse comportamento foi tratado na primeira parte do artigo.
O objeto JSON retornado possui várias propriedades. Daí o fato de no momento do subscribe, acessarmos especificamente a propriedade lista.result.fulfillment.messages, adicionando ao array de messages e identificando o remetente como boot (linha 32).
O método sendMessage adiciona ao array, mas desta vez definindo o remetente como usuário (‘eu’) utilizando uma função acessória para remover acentos (linha 39).
O restante da implementação é semelhante ao método anterior, populando o array com as respostas do boot (linha 42).
Para acionar o scroll, verificamos toda vez que a página sofre atualizações no método ngAfterViewChecked (linha 48).
Vamos finalizar o projeto. No template app.component.html, limpe o código padrão que foi gerado na criação e deixe-o assim:
<div class="container">
<div class="row align-items-center">
<div class="mx-auto">
<h3><i class="fas fa-robot"></i>ChatBoot com DialogFLow </h3>
</div>
</div>
<div class="row align-items-center justify-content-center">
<div class="col-lg-8">
<ul #scrollMe class="messages">
<div *ngFor="let msg of resultados ">
<li class="message appeared" [ngClass]="{'left':msg.remetente === 'boot',
'right':msg.remetente === 'eu' }">
<div class="avatar"></div>
<div class="text_wrapper">
<div class="text">
<p class="time"> ({{msg.data | date:'HH:mm:ss'}})</p>
{{msg.mensagem}}
</div>
</div>
</li>
</div>
</ul>
</div>
</div>
<div class="row align-items-center justify-content-center">
<div class="col-sm-6">
<input type="text" class="form-control " (keyup.enter)="sendMessage()" [(ngModel)]="msg" />
</div>
<div class="col-sm-2">
<button class="btn btn-success sendbtn" (click)="sendMessage()"><i class="far fa-share-square"></i> Enviar</button>
</div>
</div>
Na linha 11 declaramos a variável para manipular o scroll #scrollMe. Na linha 12 utilizamos a diretiva *ngFor para iterarmos sobre o array das respostas do boot.
Note que utilizamos o ngClass (linha 13) para decidir qual estilo será aplicado na formatação da lista de mensagens, criando o efeito de chats.
Todo o código do projeto está no GIT.
Abraços!