Angular Integration
Add analytics to your Angular Figma plugin with the TodeService. This guide shows you how to track user actions in Angular-based Figma plugins and widgets.
Installation
Install the core SDK and Angular adapter:
Terminal
npm install @tode-sdk/core @tode-sdk/figma-angularSetup
Initialize in Plugin Controller
Set up Tode in your plugin's main code file:
code.js
import { tode } from '@tode-sdk/core';
// Show your Angular UI
figma.showUI(__html__, { width: 400, height: 600 });
// Initialize Tode for Figma plugin analytics
await tode.init({ apiKey: 'your-api-key' });
// Optional: Track plugin open from controller
tode.trackAction('plugin_opened');Configure Network Access
Add Tode's URLs to your plugin's manifest.json to enable analytics requests. Learn more about network access.
manifest.json
{
"name": "Your Plugin",
"id": "your-plugin-id",
"api": "1.0.0",
"main": "code.js",
"ui": "index.html",
"networkAccess": {
"allowedDomains": [
"https://mvp-events-processing-production.up.railway.app",
"wss://mvp-events-processing-production.up.railway.app"
]
}
}Provide the Service in Your Angular App
Add TodeService to your app's providers:
app.config.ts
import { ApplicationConfig } from '@angular/core';
import { TodeService } from '@tode-sdk/figma-angular';
export const appConfig: ApplicationConfig = {
providers: [TodeService]
};Use the Service in Your Components
Inject TodeService to track user actions:
app.component.js
import { Component } from '@angular/core';
import { TodeService } from '@tode-sdk/figma-angular';
@Component({
selector: 'app-root',
template: `
<button (click)="handleExport()" [disabled]="!isReady">
Export Design
</button>
<p>Analytics: {{ isReady ? 'Connected' : 'Connecting...' }}</p>
`
})
export class AppComponent {
isReady = false;
subscription;
constructor(todeService) {
this.todeService = todeService;
this.subscription = this.todeService.isReady$.subscribe(
(ready) => this.isReady = ready
);
}
handleExport() {
if (this.isReady) {
this.todeService.trackAction('export_clicked');
}
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}Service Reference
TodeService
| Property/Method | Type | Description |
|---|---|---|
tode | Tode | The Tode instance for direct access |
isReady$ | Observable<boolean> | Observable of ready state (tode.isReady) |
trackAction(name) | method | Track a simple action (API) |
trackActionWithMetric(name, value) | method | Track action with numeric value (API) |
destroy() | method | Clean up resources |
Examples
Basic Tracking
tool-button.component.js
import { Component, Input } from '@angular/core';
import { TodeService } from '@tode-sdk/figma-angular';
@Component({
selector: 'app-tool-button',
template: `<button (click)="handleClick()">{{ tool }}</button>`
})
export class ToolButtonComponent {
@Input() tool;
isReady = false;
subscription;
constructor(todeService) {
this.todeService = todeService;
this.subscription = this.todeService.isReady$.subscribe(
(ready) => this.isReady = ready
);
}
handleClick() {
// Track which tool was selected in your Figma plugin
if (this.isReady) {
this.todeService.trackAction(`tool_selected_${this.tool}`);
}
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}Track with Metrics
export-panel.component.js
import { Component } from '@angular/core';
import { TodeService } from '@tode-sdk/figma-angular';
@Component({
selector: 'app-export-panel',
template: `
<button (click)="handleExport()">
Export {{ selectedItems.length }} Items
</button>
`
})
export class ExportPanelComponent {
selectedItems = [];
isReady = false;
subscription;
constructor(todeService) {
this.todeService = todeService;
this.subscription = this.todeService.isReady$.subscribe(
(ready) => this.isReady = ready
);
}
async handleExport() {
// Perform export logic...
// Track export with item count for Figma widget analytics
if (this.isReady) {
this.todeService.trackActionWithMetric('items_exported', this.selectedItems.length);
}
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}Track Feature Views
feature-view.component.js
import { Component } from '@angular/core';
import { TodeService } from '@tode-sdk/figma-angular';
import { filter, take } from 'rxjs';
@Component({
selector: 'app-feature-view',
template: `<div>Feature Content</div>`
})
export class FeatureViewComponent {
subscription;
constructor(todeService) {
this.todeService = todeService;
}
ngOnInit() {
// Track feature view once when Tode is ready
this.subscription = this.todeService.isReady$.pipe(
filter((ready) => ready),
take(1)
).subscribe(() => {
this.todeService.trackAction('feature_viewed');
});
}
ngOnDestroy() {
this.subscription?.unsubscribe();
}
}Using AsyncPipe
async-example.component.ts
import { Component } from '@angular/core';
import { TodeService } from '@tode-sdk/figma-angular';
import { CommonModule, AsyncPipe } from '@angular/common';
@Component({
selector: 'app-async-example',
standalone: true,
imports: [CommonModule, AsyncPipe],
template: `
<button
(click)="track()"
[disabled]="!(todeService.isReady$ | async)">
Track Action
</button>
<p>Status: {{ (todeService.isReady$ | async) ? 'Ready' : 'Connecting...' }}</p>
`
})
export class AsyncExampleComponent {
constructor(public todeService: TodeService) {}
track(): void {
this.todeService.trackAction('button_clicked');
}
}Standalone Components
standalone.component.ts
import { Component, OnDestroy } from '@angular/core';
import { TodeService } from '@tode-sdk/figma-angular';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-standalone',
standalone: true,
providers: [TodeService],
template: `
<button (click)="track()" [disabled]="!isReady">
Track
</button>
`
})
export class StandaloneComponent implements OnDestroy {
isReady = false;
private subscription: Subscription;
constructor(private todeService: TodeService) {
this.subscription = this.todeService.isReady$.subscribe(
ready => this.isReady = ready
);
}
track(): void {
if (this.isReady) {
this.todeService.trackAction('standalone_action');
}
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
this.todeService.destroy();
}
}Best Practices
1. Always Unsubscribe
TypeScript
import { Subscription } from 'rxjs';
export class MyComponent implements OnDestroy {
private subscription: Subscription;
constructor(private todeService: TodeService) {
this.subscription = this.todeService.isReady$.subscribe(...);
}
ngOnDestroy(): void {
// Always clean up subscriptions
this.subscription.unsubscribe();
}
}2. Use takeUntilDestroyed (Angular 16+)
TypeScript
import { Component, DestroyRef, inject } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({...})
export class ModernComponent {
private destroyRef = inject(DestroyRef);
private todeService = inject(TodeService);
isReady = false;
constructor() {
this.todeService.isReady$
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(ready => this.isReady = ready);
}
}3. Use Descriptive Action Names
TypeScript
// Good - clear and descriptive
this.todeService.trackAction('export_to_png');
this.todeService.trackAction('layer_renamed');
// Bad - too vague
this.todeService.trackAction('click');
this.todeService.trackAction('action1');4. Prefer AsyncPipe for Templates
TypeScript
// Good - automatic subscription management
template: `<button [disabled]="!(todeService.isReady$ | async)">`
// Also good but requires manual unsubscribe
isReady = false;
this.todeService.isReady$.subscribe(r => this.isReady = r);
template: `<button [disabled]="!isReady">`Next Steps
- Learn about the API Reference for all available methods
- See other framework guides: React, Vue, Svelte