DEV Community

Alkademy
Alkademy

Posted on • Originally published at munonye.com

Build a Streaming Chat UI in Angular 19 (2026)

Canonical URL: Republished from munonye.com. Full code on GitHub.

This Angular OpenAI chat tutorial builds a chat UI on Angular 19 that talks to the Spring AI backend from M7-A. Part of the AI Developer Tutorials hub.

Prerequisites: M7-A backend running on port 8080, Node 18+, Angular CLI 19.

Time: ~50 minutes.

Step 1 — Create the Angular app

ng new chat-ui --routing --style=scss --ssr=false
cd chat-ui
ng serve --port 4200
Enter fullscreen mode Exit fullscreen mode

Configure HttpClient in main.ts:

import { bootstrapApplication } from '@angular/platform-browser';
import { provideHttpClient } from '@angular/common/http';
import { AppComponent } from './app/app.component';

bootstrapApplication(AppComponent, {
  providers: [provideHttpClient()],
});
Enter fullscreen mode Exit fullscreen mode

Step 2 — Chat service

// chat.service.ts
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

export interface ChatMessage {
  role: 'user' | 'assistant';
  content: string;
}

@Injectable({ providedIn: 'root' })
export class ChatService {
  private http = inject(HttpClient);
  private apiUrl = 'http://localhost:8080/api/chat';

  send(message: string): Observable<{ reply: string }> {
    return this.http.post<{ reply: string }>(this.apiUrl, { message });
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 3 — Chat component (standalone)

import { Component, inject, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ChatService, ChatMessage } from './chat.service';

@Component({
  selector: 'app-chat',
  standalone: true,
  imports: [FormsModule],
  template: `
    <div class="chat">
      @for (msg of messages(); track $index) {
        <div [class]="msg.role">{{ msg.content }}</div>
      }
      @if (loading()) { <p>Thinking…</p> }
      <form (ngSubmit)="send()">
        <input [(ngModel)]="input" name="input" [disabled]="loading()" />
        <button type="submit" [disabled]="loading() || !input.trim()">Send</button>
      </form>
    </div>
  `,
  styles: [`
    .user { text-align: right; color: #1565c0; margin: 8px 0; }
    .assistant { text-align: left; color: #2e7d32; margin: 8px 0; }
  `],
})
export class ChatComponent {
  private chat = inject(ChatService);
  messages = signal<ChatMessage[]>([]);
  loading = signal(false);
  input = '';

  send(): void {
    const text = this.input.trim();
    if (!text) return;
    this.messages.update(m => [...m, { role: 'user', content: text }]);
    this.input = '';
    this.loading.set(true);
    this.chat.send(text).subscribe({
      next: res => {
        this.messages.update(m => [...m, { role: 'assistant', content: res.reply }]);
        this.loading.set(false);
      },
      error: () => {
        this.messages.update(m => [...m, { role: 'assistant', content: 'Error contacting AI service.' }]);
        this.loading.set(false);
      },
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

Wire ChatComponent in AppComponent imports.

Step 4 — Connect to reactive forms hub

For production forms validation patterns, cross-link Angular reactive forms validation guide.

Next steps


Read the full tutorial with all code on munonye.com →


Full tutorial: Build a Streaming Chat UI in Angular 19 (2026)

Kindson MunonyeGitHub · LinkedIn · About

Top comments (0)