Angular Integration

Integrate BotHarbor chat widget into your Angular applications with comprehensive examples and best practices.

Quick Start

The easiest way to add BotHarbor to your Angular app is by loading the script in your component and initializing it in the ngOnInit lifecycle hook.

Basic Component Integration

import { Component, OnInit, OnDestroy } from '@angular/core';

declare global {
  interface Window {
    BOTHARBOR_CONFIG?: any;
    BotHarbor?: {
      open: () => void;
      close: () => void;
      sendMessage: (message: string) => void;
    };
  }
}

@Component({
  selector: 'app-root',
  template: `
    <div class="app">
      <header>
        <h1>My Angular App</h1>
        <button (click)="openChat()" [disabled]="!chatReady" class="chat-button">
          {{ chatReady ? 'Open Chat' : 'Loading Chat...' }}
        </button>
      </header>
      <main>
        <p>Your app content here...</p>
      </main>
    </div>
  `,
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {
  chatReady = false;

  ngOnInit() {
    this.initializeBotHarbor();
  }

  ngOnDestroy() {
    // Cleanup when component is destroyed
    const existingScript = document.querySelector('script[src="https://botharbor.ai/embed.js"]');
    if (existingScript) {
      existingScript.remove();
    }
    delete window.BOTHARBOR_CONFIG;
  }

  private async initializeBotHarbor() {
    try {
      // Set global configuration
      window.BOTHARBOR_CONFIG = {
        botId: 'your-bot-id',
        theme: 'light',
        position: 'bottom-right',
        primaryColor: '#14B8A6',
        greeting: 'Hello! How can I help you?'
      };

      // Load BotHarbor script
      await this.loadScript('https://botharbor.ai/embed.js');
      this.chatReady = true;
      console.log('BotHarbor initialized successfully');
    } catch (error) {
      console.error('Failed to initialize BotHarbor:', error);
    }
  }

  private loadScript(src: string): Promise<void> {
    return new Promise((resolve, reject) => {
      // Check if script already exists
      const existingScript = document.querySelector(`script[src="${src}"]`);
      if (existingScript) {
        resolve();
        return;
      }

      const script = document.createElement('script');
      script.src = src;
      script.async = true;
      script.onload = () => resolve();
      script.onerror = () => reject(new Error(`Failed to load script: ${src}`));
      document.head.appendChild(script);
    });
  }

  openChat() {
    if (window.BotHarbor && this.chatReady) {
      window.BotHarbor.open();
    }
  }

  closeChat() {
    if (window.BotHarbor) {
      window.BotHarbor.close();
    }
  }
}

Service-Based Approach

Create a dedicated service for better organization and reusability across your Angular application.

BotHarbor Service

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';

export interface BotHarborConfig {
  botId: string;
  theme?: 'light' | 'dark' | 'auto';
  position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
  primaryColor?: string;
  greeting?: string;
}

@Injectable({
  providedIn: 'root'
})
export class BotHarborService {
  private readySubject = new BehaviorSubject<boolean>(false);
  private openSubject = new BehaviorSubject<boolean>(false);
  
  public isReady$: Observable<boolean> = this.readySubject.asObservable();
  public isOpen$: Observable<boolean> = this.openSubject.asObservable();

  async initialize(config: BotHarborConfig): Promise<void> {
    try {
      // Set global configuration
      window.BOTHARBOR_CONFIG = config;

      // Load script if not already loaded
      await this.loadScript('https://botharbor.ai/embed.js');
      
      // Set up event listeners
      this.setupEventListeners();
      
      this.readySubject.next(true);
      console.log('BotHarbor service initialized');
    } catch (error) {
      console.error('Failed to initialize BotHarbor service:', error);
      throw error;
    }
  }

  private loadScript(src: string): Promise<void> {
    return new Promise((resolve, reject) => {
      const existingScript = document.querySelector(`script[src="${src}"]`);
      if (existingScript) {
        resolve();
        return;
      }

      const script = document.createElement('script');
      script.src = src;
      script.async = true;
      script.onload = () => resolve();
      script.onerror = () => reject(new Error(`Failed to load script: ${src}`));
      document.head.appendChild(script);
    });
  }

  private setupEventListeners(): void {
    // Listen for BotHarbor events
    window.addEventListener('botharbor:open', () => {
      this.openSubject.next(true);
    });

    window.addEventListener('botharbor:close', () => {
      this.openSubject.next(false);
    });
  }

  open(): void {
    if (window.BotHarbor && this.readySubject.value) {
      window.BotHarbor.open();
    }
  }

  close(): void {
    if (window.BotHarbor) {
      window.BotHarbor.close();
    }
  }

  sendMessage(message: string): void {
    if (window.BotHarbor && this.readySubject.value) {
      window.BotHarbor.sendMessage(message);
    }
  }

  destroy(): void {
    const existingScript = document.querySelector('script[src="https://botharbor.ai/embed.js"]');
    if (existingScript) {
      existingScript.remove();
    }
    delete window.BOTHARBOR_CONFIG;
    this.readySubject.next(false);
    this.openSubject.next(false);
  }
}

Using the Service

import { Component, OnInit, OnDestroy } from '@angular/core';
import { BotHarborService, BotHarborConfig } from './services/bot-harbor.service';
import { Subscription } from 'rxjs';

@Component({
  selector: 'app-root',
  template: `
    <div class="app">
      <header>
        <h1>My Angular App</h1>
        <div class="chat-controls">
          <button 
            (click)="openChat()" 
            [disabled]="!isReady"
            class="btn btn-primary"
          >
            {{ isReady ? 'Open Chat' : 'Loading...' }}
          </button>
          <button 
            (click)="closeChat()" 
            [disabled]="!isOpen"
            class="btn btn-secondary"
          >
            Close Chat
          </button>
        </div>
        <p>Chat Status: {{ isOpen ? 'Open' : 'Closed' }}</p>
      </header>
      <main>
        <p>Your app content here...</p>
      </main>
    </div>
  `
})
export class AppComponent implements OnInit, OnDestroy {
  isReady = false;
  isOpen = false;
  private subscriptions: Subscription[] = [];

  constructor(private botHarborService: BotHarborService) {}

  ngOnInit() {
    // Subscribe to service observables
    this.subscriptions.push(
      this.botHarborService.isReady$.subscribe(ready => {
        this.isReady = ready;
      })
    );

    this.subscriptions.push(
      this.botHarborService.isOpen$.subscribe(open => {
        this.isOpen = open;
      })
    );

    // Initialize BotHarbor
    const config: BotHarborConfig = {
      botId: 'your-bot-id',
      theme: 'auto',
      position: 'bottom-right',
      primaryColor: '#14B8A6'
    };

    this.botHarborService.initialize(config);
  }

  ngOnDestroy() {
    // Clean up subscriptions
    this.subscriptions.forEach(sub => sub.unsubscribe());
    
    // Destroy BotHarbor
    this.botHarborService.destroy();
  }

  openChat() {
    this.botHarborService.open();
  }

  closeChat() {
    this.botHarborService.close();
  }
}

Environment Configuration

Configure your Angular application with environment variables for different deployment environments.

Environment Files

// src/environments/environment.ts
export const environment = {
  production: false,
  botHarbor: {
    botId: 'your-dev-bot-id',
    theme: 'light',
    position: 'bottom-right',
    primaryColor: '#14B8A6'
  }
};

// src/environments/environment.prod.ts
export const environment = {
  production: true,
  botHarbor: {
    botId: 'your-prod-bot-id',
    theme: 'auto',
    position: 'bottom-right',
    primaryColor: '#14B8A6'
  }
};

Using Environment Variables

import { Component, OnInit } from '@angular/core';
import { BotHarborService } from './services/bot-harbor.service';
import { environment } from '../environments/environment';

@Component({
  selector: 'app-root',
  template: `
    <div class="app">
      <h1>My Angular App</h1>
      <button (click)="openChat()" [disabled]="!isReady">
        Open Chat
      </button>
    </div>
  `
})
export class AppComponent implements OnInit {
  isReady = false;

  constructor(private botHarborService: BotHarborService) {}

  ngOnInit() {
    this.botHarborService.isReady$.subscribe(ready => {
      this.isReady = ready;
    });

    // Use environment configuration
    this.botHarborService.initialize(environment.botHarbor);
  }

  openChat() {
    this.botHarborService.open();
  }
}

Best Practices

Angular-Specific Tips

  • Use services for better dependency injection and testing
  • Leverage RxJS observables for reactive state management
  • Use environment files for configuration management
  • Implement proper cleanup in ngOnDestroy

Performance & Error Handling

  • Load scripts asynchronously to avoid blocking
  • Use try-catch blocks for error handling
  • Implement loading states for better UX
  • Unsubscribe from observables to prevent memory leaks