Official Metigan SDK for Angular - Email, Forms, Contacts, and Audiences management
npm install @metigan/angularbash
npm install @metigan/angular
`
Or via yarn:
`bash
yarn add @metigan/angular
`
๐ Getting Your API Key
Get your API key from the Metigan Dashboard.
1. Sign in to your Metigan account
2. Navigate to Settings โ API Keys
3. Create a new API key or use an existing one
4. Copy the API key and use it in your application
> โ ๏ธ Security Note: Never expose your API key in client-side code. For client-side usage, consider using environment-specific keys or implementing a proxy server.
๐ Quick Start
$3
#### 1. Import the Module
In your app.module.ts:
`typescript
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { MetiganModule } from '@metigan/angular';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
MetiganModule.forRoot({
apiKey: 'your-api-key'
})
],
bootstrap: [AppComponent]
})
export class AppModule { }
`
#### 2. Use in Components
`typescript
import { Component } from '@angular/core';
import { MetiganService } from '@metigan/angular';
@Component({
selector: 'app-my-component',
template: ''
})
export class MyComponent {
constructor(private metigan: MetiganService) {}
sendEmail() {
this.metigan.email.sendEmail({
from: 'Your Company ',
recipients: ['customer@email.com'],
subject: 'Welcome!',
content: 'Hello!
Thank you for signing up.
'
}).subscribe({
next: (response) => {
console.log('Email sent:', response.message);
console.log('Emails remaining:', response.emailsRemaining);
},
error: (error) => {
console.error('Error sending email:', error);
}
});
}
}
`
$3
For Standalone Components, use manual initialization:
For Standalone Components, you can initialize manually:
`typescript
import { Component, OnInit } from '@angular/core';
import { MetiganService } from '@metigan/angular';
@Component({
selector: 'app-root',
standalone: true,
template: ''
})
export class AppComponent implements OnInit {
constructor(private metigan: MetiganService) {}
ngOnInit() {
// Initialize with your API key
this.metigan.initialize({
apiKey: 'your-api-key',
timeout: 30000,
retryCount: 3
});
}
sendEmail() {
this.metigan.email.sendEmail({
from: 'sender@example.com',
recipients: ['recipient@example.com'],
subject: 'Hello!',
content: 'Welcome
Thank you for signing up.
'
}).subscribe({
next: (response) => console.log('Sent:', response),
error: (error) => console.error('Error:', error)
});
}
}
`
๐ง Email Module
$3
`typescript
this.metigan.email.sendEmail({
from: 'sender@example.com',
recipients: ['recipient@example.com'],
subject: 'Email Subject',
content: 'HTML Content
'
}).subscribe({
next: (response) => console.log('Sent:', response),
error: (error) => console.error('Error:', error)
});
`
$3
`typescript
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
const files = Array.from(fileInput.files || []);
this.metigan.email.sendEmail({
from: 'company@email.com',
recipients: ['customer@email.com'],
subject: 'Important Document',
content: 'Please find the document attached.',
attachments: files
}).subscribe({
next: (response) => console.log('Sent with attachments'),
error: (error) => console.error('Error:', error)
});
`
$3
`typescript
this.metigan.email.sendEmail({
from: 'company@email.com',
recipients: ['main@email.com'],
subject: 'Meeting',
content: 'Email content',
cc: ['copy@email.com'],
bcc: ['hidden-copy@email.com'],
replyTo: 'reply-here@email.com'
}).subscribe({
next: (response) => console.log('Sent'),
error: (error) => console.error('Error:', error)
});
`
$3
`typescript
this.metigan.email.sendEmail({
from: 'sender@example.com',
recipients: ['recipient@example.com'],
subject: 'Welcome Email',
templateId: 'template-123',
variables: {
name: 'John Doe',
company: 'Acme Inc'
}
}).subscribe({
next: (response) => console.log('Sent'),
error: (error) => console.error('Error:', error)
});
`
๐ Forms Module
$3
`typescript
this.metigan.forms.submit({
formId: 'form-123', // or form slug
data: {
'field-email': 'user@email.com',
'field-name': 'John Doe',
'field-message': 'Hello, I would like more information.'
}
}).subscribe({
next: (response) => {
console.log(response.message); // "Thank you for your submission!"
},
error: (error) => console.error('Error:', error)
});
`
$3
`typescript
// By slug (for public display, no API key required)
this.metigan.forms.getPublicForm('my-form').subscribe({
next: (form) => {
console.log(form.title);
console.log(form.fields);
},
error: (error) => console.error('Error:', error)
});
`
$3
`typescript
this.metigan.forms.listForms({
page: 1,
limit: 10
}).subscribe({
next: (response) => {
response.forms.forEach(form => {
console.log(${form.title} - ${form.analytics?.submissions || 0} responses);
});
},
error: (error) => console.error('Error:', error)
});
`
$3
`typescript
this.metigan.forms.createForm({
title: 'Contact Form',
description: 'Get in touch with us',
fields: [
{
id: 'field-email',
type: 'email',
label: 'Your Email',
required: true
},
{
id: 'field-name',
type: 'text',
label: 'Your Name',
required: true
},
{
id: 'field-message',
type: 'textarea',
label: 'Message',
required: true
}
],
settings: {
successMessage: 'Thank you! We will get back to you soon.',
notifyEmail: 'contact@company.com'
}
}).subscribe({
next: (form) => console.log('Form created:', form),
error: (error) => console.error('Error:', error)
});
`
๐ฅ Contacts Module
$3
`typescript
this.metigan.contacts.create({
email: 'new@email.com',
firstName: 'Jane',
lastName: 'Doe',
audienceId: 'audience-123',
tags: ['customer', 'newsletter']
}).subscribe({
next: (contact) => console.log('Contact created:', contact),
error: (error) => console.error('Error:', error)
});
`
$3
`typescript
// By ID
this.metigan.contacts.get('contact-456').subscribe({
next: (contact) => console.log('Contact:', contact),
error: (error) => console.error('Error:', error)
});
// By email
this.metigan.contacts.getByEmail('jane@email.com', 'audience-123').subscribe({
next: (contact) => console.log('Contact:', contact),
error: (error) => console.error('Error:', error)
});
`
$3
`typescript
this.metigan.contacts.update('contact-456', {
firstName: 'Jane Marie',
tags: ['customer', 'vip']
}).subscribe({
next: (updated) => console.log('Contact updated:', updated),
error: (error) => console.error('Error:', error)
});
`
$3
`typescript
this.metigan.contacts.list({
audienceId: 'audience-123',
status: 'subscribed',
tag: 'customer',
page: 1,
limit: 50
}).subscribe({
next: (response) => {
response.contacts.forEach(contact => {
console.log(${contact.email}: ${contact.firstName});
});
},
error: (error) => console.error('Error:', error)
});
`
$3
`typescript
// Unsubscribe
this.metigan.contacts.unsubscribe('contact-456').subscribe({
next: (contact) => console.log('Unsubscribed:', contact),
error: (error) => console.error('Error:', error)
});
// Resubscribe
this.metigan.contacts.subscribe('contact-456').subscribe({
next: (contact) => console.log('Subscribed:', contact),
error: (error) => console.error('Error:', error)
});
`
$3
`typescript
// Add tags
this.metigan.contacts.addTags('contact-456', ['vip', 'black-friday']).subscribe({
next: (contact) => console.log('Tags added:', contact),
error: (error) => console.error('Error:', error)
});
// Remove tags
this.metigan.contacts.removeTags('contact-456', ['test']).subscribe({
next: (contact) => console.log('Tags removed:', contact),
error: (error) => console.error('Error:', error)
});
`
$3
`typescript
this.metigan.contacts.bulkImport(
[
{ email: 'john@email.com', firstName: 'John', audienceId: 'audience-123' },
{ email: 'jane@email.com', firstName: 'Jane', audienceId: 'audience-123' },
{ email: 'peter@email.com', firstName: 'Peter', audienceId: 'audience-123', tags: ['vip'] }
],
'audience-123'
).subscribe({
next: (result) => {
console.log(Imported: ${result.imported});
console.log(Failed: ${result.failed});
},
error: (error) => console.error('Error:', error)
});
`
๐ Audiences Module
$3
`typescript
this.metigan.audiences.create({
name: 'Main Newsletter',
description: 'Main subscriber list'
}).subscribe({
next: (audience) => console.log('Audience created:', audience),
error: (error) => console.error('Error:', error)
});
`
$3
`typescript
this.metigan.audiences.list({
page: 1,
limit: 10
}).subscribe({
next: (response) => {
response.audiences.forEach(audience => {
console.log(${audience.name}: ${audience.count} contacts);
});
},
error: (error) => console.error('Error:', error)
});
`
$3
`typescript
this.metigan.audiences.getStats('audience-123').subscribe({
next: (stats) => {
console.log(Total: ${stats.total});
console.log(Subscribed: ${stats.subscribed});
console.log(Unsubscribed: ${stats.unsubscribed});
},
error: (error) => console.error('Error:', error)
});
`
$3
`typescript
// Remove bounced and unsubscribed contacts
this.metigan.audiences.clean('audience-123').subscribe({
next: (result) => console.log(${result.removed} contacts removed),
error: (error) => console.error('Error:', error)
});
`
$3
`typescript
// Merge source into target (source is deleted)
this.metigan.audiences.merge(
'source-audience-id',
'target-audience-id'
).subscribe({
next: (merged) => console.log('Audiences merged:', merged),
error: (error) => console.error('Error:', error)
});
`
๐จ Templates Module
$3
`typescript
this.metigan.templates.list({
page: 1,
limit: 10
}).subscribe({
next: (response) => {
response.templates.forEach(template => {
console.log(${template.name}: ${template.subject});
});
},
error: (error) => console.error('Error:', error)
});
`
$3
`typescript
this.metigan.templates.get('template-123').subscribe({
next: (template) => console.log('Template:', template),
error: (error) => console.error('Error:', error)
});
`
โ๏ธ Configuration
$3
`typescript
MetiganModule.forRoot({
apiKey: 'your-api-key',
timeout: 60000, // Optional: Request timeout in ms (default: 30000)
retryCount: 5, // Optional: Number of retries (default: 3)
retryDelay: 2000, // Optional: Delay between retries in ms (default: 2000)
apiUrl: 'https://api.metigan.com' // Optional: Custom API URL
})
`
$3
`typescript
this.metigan.initialize({
apiKey: 'your-api-key',
timeout: 60000,
retryCount: 5,
retryDelay: 2000,
apiUrl: 'https://api.metigan.com'
});
`
$3
For better security, use environment variables:
environment.ts:
`typescript
export const environment = {
production: false,
metiganApiKey: 'your-api-key'
};
`
app.module.ts:
`typescript
import { environment } from './environments/environment';
@NgModule({
imports: [
MetiganModule.forRoot({
apiKey: environment.metiganApiKey
})
]
})
export class AppModule { }
`
๐ RxJS Observables
All service methods return RxJS Observables. Make sure to:
1. Subscribe to the observable
2. Handle both success and error cases
3. Unsubscribe if needed (or use async pipe in templates)
$3
`typescript
// In component
templates$ = this.metigan.templates.list();
// In template
{{ template.name }}
`
$3
`typescript
import { Component, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
export class MyComponent implements OnDestroy {
private subscription = new Subscription();
sendEmail() {
const sub = this.metigan.email.sendEmail({
from: 'sender@example.com',
recipients: ['recipient@example.com'],
subject: 'Test',
content: 'Hello
'
}).subscribe({
next: (response) => console.log('Sent'),
error: (error) => console.error('Error:', error)
});
this.subscription.add(sub);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
`
$3
`typescript
import { Component, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
export class MyComponent implements OnDestroy {
private destroy$ = new Subject();
loadTemplates() {
this.metigan.templates.list()
.pipe(takeUntil(this.destroy$))
.subscribe({
next: (response) => console.log('Templates:', response),
error: (error) => console.error('Error:', error)
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
`
๐ก๏ธ Error Handling
All services return RxJS Observables, so use standard error handling:
`typescript
import { MetiganError, ValidationError, ApiError } from '@metigan/angular';
this.metigan.email.sendEmail(options).subscribe({
next: (response) => {
// Success
console.log('Email sent successfully');
},
error: (error) => {
if (error instanceof ValidationError) {
console.error('Invalid data:', error.message);
// Handle validation errors
} else if (error instanceof ApiError) {
console.error(API error (${error.statusCode}):, error.message);
// Handle API errors (4xx, 5xx)
if (error.statusCode === 401) {
// Unauthorized - check API key
} else if (error.statusCode === 429) {
// Rate limited - retry after delay
}
} else if (error instanceof MetiganError) {
console.error('Metigan error:', error.message);
// Handle general Metigan errors
} else {
console.error('Unknown error:', error);
// Handle unexpected errors
}
}
});
`
๐ Using Individual Services
If you only need a specific service, you can inject it directly:
`typescript
import { MetiganEmailService } from '@metigan/angular';
@Component({
selector: 'app-email',
template: ''
})
export class EmailComponent {
constructor(private emailService: MetiganEmailService) {}
ngOnInit() {
// Initialize the service
this.emailService.initialize('your-api-key');
// Use the service
this.emailService.sendEmail({
from: 'sender@example.com',
recipients: ['recipient@example.com'],
subject: 'Test',
content: 'Hello
'
}).subscribe({
next: (response) => console.log('Sent'),
error: (error) => console.error('Error:', error)
});
}
}
`
Available services:
- MetiganEmailService
- MetiganFormsService
- MetiganContactsService
- MetiganAudiencesService
- MetiganTemplatesService
๐งช Testing
When testing components that use Metigan services, you can mock the services:
`typescript
import { TestBed } from '@angular/core/testing';
import { of, throwError } from 'rxjs';
import { MetiganService } from '@metigan/angular';
describe('MyComponent', () => {
let mockMetiganService: jasmine.SpyObj;
beforeEach(() => {
mockMetiganService = jasmine.createSpyObj('MetiganService', ['email']);
mockMetiganService.email = jasmine.createSpyObj('email', ['sendEmail']);
TestBed.configureTestingModule({
providers: [
{ provide: MetiganService, useValue: mockMetiganService }
]
});
});
it('should send email', () => {
mockMetiganService.email.sendEmail.and.returnValue(
of({ success: true, message: 'Email sent' })
);
// Test your component
});
});
`
๐ Troubleshooting
$3
Make sure you've initialized the service with an API key:
`typescript
// For NgModules
MetiganModule.forRoot({ apiKey: 'your-api-key' })
// For manual initialization
this.metigan.initialize({ apiKey: 'your-api-key' })
`
$3
Make sure you're subscribing to the Observable:
`typescript
// โ Wrong - nothing happens
this.metigan.email.sendEmail(options);
// โ
Correct - subscribes to the Observable
this.metigan.email.sendEmail(options).subscribe({
next: (response) => console.log('Sent'),
error: (error) => console.error('Error:', error)
});
`
$3
Always unsubscribe from Observables to prevent memory leaks:
`typescript
// Using async pipe (recommended)
templates$ = this.metigan.templates.list();
// Using takeUntil pattern
private destroy$ = new Subject();
this.metigan.templates.list()
.pipe(takeUntil(this.destroy$))
.subscribe(...);
// Using Subscription
private subscription = new Subscription();
this.subscription.add(
this.metigan.email.sendEmail(...).subscribe(...)
);
`
$3
Make sure you have TypeScript 4.9.0 or higher and Angular 15+ installed:
`bash
npm install typescript@^4.9.0 --save-dev
``