Angular Integration Guide

Install IssueCapture in Angular

Add bug reporting to your Angular application in 5 minutes. Works with Angular 15+, TypeScript, and standalone components.

Quick Summary

  • Time: 2-5 minutes (7 simple steps)
  • Prerequisites: Angular 15+, TypeScript, Jira account, IssueCapture account (free)
  • Method: Script tag in index.html or programmatic ngOnInit
  • Works with: Angular 15+, Angular Router, NgRx, RxJS
  • Bundle size: 32KB initial (152KB for screenshot & annotation tools, lazy-loaded)

Prerequisites

  • Angular 15 or higher
  • TypeScript 4.9+
  • Jira Cloud account (Software or JSM)
  • IssueCapture account (sign up free)

Step-by-Step Installation

1

Get Your API Key

Sign up for IssueCapture and retrieve your widget API key from the dashboard

  • Go to https://issuecapture.com/signup and create a free account
  • Connect your Jira instance via OAuth
  • Navigate to the Widgets page and create a new widget
  • Copy your API key (starts with "ic_")
2

Add Environment Variable

Store your API key in your environment configuration

  • Angular uses environment.ts for configuration
  • Store different API keys for dev and production
  • Never commit production keys to version control
  • Angular CLI automatically uses the correct environment file
// src/environments/environment.ts
export const environment = {
  production: false,
  issueCaptureApiKey: 'ic_your_api_key_here'
};

// src/environments/environment.prod.ts
export const environment = {
  production: true,
  issueCaptureApiKey: 'ic_your_production_api_key'
};
3

Install via Script Tag (Recommended)

Add IssueCapture to your src/index.html file

  • Place script tag at the end of <body> for optimal loading
  • type="module" is required for ESM imports
  • For dynamic API keys, use programmatic initialization (Step 4)
  • Widget loads after Angular app bootstraps
<!-- src/index.html -->
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Your Angular App</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
  <app-root></app-root>

  <!-- IssueCapture Widget -->
  <script type="module">
    import IssueCapture from 'https://issuecapture.com/widget.js';

    // Note: Replace with actual API key during build
    // Or use programmatic initialization (see Step 4)
    IssueCapture.init({
      apiKey: 'ic_your_api_key_here', // Replace with your API key
      trigger: 'auto',
      button: {
        position: 'bottom-right',
        text: 'Report Bug'
      }
    });
  </script>
</body>
</html>
4

Alternative: Programmatic Initialization

Initialize IssueCapture from your AppComponent using ngOnInit

  • Loads widget when AppComponent initializes
  • Uses environment.ts for API key management
  • Cleanup in ngOnDestroy prevents memory leaks
  • Useful for SPA with Angular Router
// src/app/app.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { environment } from '../environments/environment';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {
  title = 'your-angular-app';

  ngOnInit(): void {
    // Dynamically load IssueCapture widget
    const script = document.createElement('script');
    script.type = 'module';
    script.innerHTML = `
      import IssueCapture from 'https://issuecapture.com/widget.js';

      IssueCapture.init({
        apiKey: '${environment.issueCaptureApiKey}',
        trigger: 'auto',
        button: {
          position: 'bottom-right',
          text: 'Report Bug'
        }
      });
    `;
    document.body.appendChild(script);
  }

  ngOnDestroy(): void {
    // Cleanup on destroy
    if ((window as any).IssueCapture) {
      (window as any).IssueCapture.destroy();
    }
  }
}
5

Custom Trigger Component

Create an Angular component to trigger the widget

  • Use (window as any).IssueCapture.open() to open programmatically
  • Check if IssueCapture exists before calling
  • Add aria-label for accessibility
  • Integrate into navbar, footer, or error handlers
// src/app/components/bug-report-button/bug-report-button.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-bug-report-button',
  template: `
    <button
      (click)="openWidget()"
      class="bug-report-btn"
      aria-label="Report a bug"
    >
      Report Bug
    </button>
  `,
  styles: [`
    .bug-report-btn {
      padding: 0.5rem 1rem;
      background: #dd0031;
      color: white;
      border: none;
      border-radius: 0.5rem;
      cursor: pointer;
      font-weight: 600;
      font-size: 1rem;
    }

    .bug-report-btn:hover {
      background: #c3002f;
    }
  `]
})
export class BugReportButtonComponent {
  openWidget(): void {
    const issueCapture = (window as any).IssueCapture;
    if (issueCapture) {
      issueCapture.open();
    }
  }
}

// Usage in your template:
// <app-bug-report-button></app-bug-report-button>
6

Set User Context (Optional)

Pre-fill user information for authenticated users

  • Pre-fill reporter name and email for authenticated users
  • Update user context when auth state changes
  • Component field maps to Jira components for routing
  • Improves bug report context for developers
// src/app/app.component.ts
import { Component, OnInit } from '@angular/core';
import { AuthService } from './services/auth.service'; // Your auth service
import { environment } from '../environments/environment';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  constructor(private authService: AuthService) {}

  ngOnInit(): void {
    // Subscribe to auth state changes
    this.authService.user$.subscribe(user => {
      if ((window as any).IssueCapture && user) {
        this.updateUserContext(user);
      }
    });
  }

  private updateUserContext(user: any): void {
    (window as any).IssueCapture.init({
      apiKey: environment.issueCaptureApiKey,
      trigger: 'auto',
      user: {
        name: user.name,
        email: user.email,
        component: 'Frontend' // Optional: Jira component
      }
    });
  }
}
7

Test the Integration

Verify IssueCapture is working correctly

  • Start your dev server: ng serve
  • Open your app in the browser
  • You should see the floating bug report button
  • Click it and submit a test issue
  • Check your Jira project for the created issue
  • Open browser console and type: window.IssueCapture.isOpen()

Troubleshooting

TypeScript: Property 'IssueCapture' does not exist on type 'Window'

Add type declarations to your project:

// src/types/issuecapture.d.ts
interface IssueCaptureAPI {
  init(config: {
    apiKey: string
    trigger?: string | 'auto'
    button?: {
      position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
      text?: string
      icon?: string
    }
    user?: {
      name?: string
      email?: string
      component?: string
    }
  }): void
  open(): void
  close(): void
  isOpen(): boolean
  destroy(): void
}

declare global {
  interface Window {
    IssueCapture?: IssueCaptureAPI
  }
}

export {}

Environment files not being used

  • Ensure environment.ts exists in src/environments/
  • Check angular.json has correct fileReplacements configuration
  • Import from '../environments/environment', not hardcoded paths
  • Restart ng serve after changing environment files

Widget loads multiple times (Angular Router)

Initialize widget once in AppComponent, not in child routes:

// src/app/app.component.ts - Initialize once at root level
private isInitialized = false;

ngOnInit(): void {
  // Initialize IssueCapture only once
  if (!this.isInitialized) {
    // Load script...
    this.isInitialized = true;
  }
}

CORS errors when submitting issues

  • Add localhost:4200 (Angular default) to allowed domains in dashboard
  • Add production domain to allowed domains
  • Check IssueCapture dashboard → Widgets → Domains

Advanced Configuration

Event Listeners

(window as any).IssueCapture.init({
  apiKey: environment.issueCaptureApiKey,
  trigger: 'auto',
  onOpen: () => {
    console.log('Widget opened')
  },
  onClose: () => {
    console.log('Widget closed')
  },
  onSubmit: (data: any) => {
    console.log('Issue submitted:', data)
    // Show success toast, etc.
  }
})

Error Handler Integration

Create a global error handler to open IssueCapture on errors:

// src/app/error-handler.service.ts
import { ErrorHandler, Injectable } from '@angular/core';

@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
  handleError(error: any): void {
    console.error('Angular error:', error);

    // Open IssueCapture for error reporting
    const issueCapture = (window as any).IssueCapture;
    if (issueCapture) {
      issueCapture.open();
    }
  }
}

// Register in app.module.ts:
// providers: [
//   { provide: ErrorHandler, useClass: GlobalErrorHandler }
// ]

Production Best Practices

  • Load widget asynchronously to avoid blocking
  • Use environment files for API keys
  • Whitelist production domains in dashboard
  • Test on staging before deploying

Ready to get started?

Sign up for a free account and start collecting bug reports in your Angular app in minutes.