Pular para o conteúdo principal
11 min de leitura

Exemplo de Integração Angular

Este guia fornece um exemplo completo e pronto para produção de integração de análises Zenovay em uma aplicação Angular usando padrões Angular modernos, serviços e injeção de dependência.

Início Rápido

Adicione o script de rastreamento Zenovay ao seu index.html:

<!-- src/index.html -->
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>My Angular App</title>
</head>
<body>
  <app-root></app-root>
  <script defer data-tracking-code="YOUR_TRACKING_CODE" src="https://api.zenovay.com/z.js"></script>
</body>
</html>

É isto para rastreamento básico de exibições de página. O script rastreia automaticamente as exibições de página no carregamento.

Cartão de script de rastreamento mostrando a tag de script com abas HTML, React, Next.js e First-Party e um botão Verificar Instalação
Encontre seu código de rastreamento em Domains → seu site → General.

Configuração Básica

1. Criar um Serviço de Análises

Crie um serviço para envolver o objeto global zenovay com injeção de dependência Angular:

// services/analytics.service.ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class AnalyticsService {
  private get zenovay(): any {
    return (window as any).zenovay;
  }

  track(eventName: string, data?: Record<string, any>): void {
    if (this.zenovay) {
      this.zenovay('track', eventName, data);
    }
  }

  identify(userId: string, traits?: Record<string, any>): void {
    if (this.zenovay) {
      this.zenovay('identify', userId, traits);
    }
  }

  trackGoal(goalName: string, data?: Record<string, any>): void {
    if (this.zenovay) {
      this.zenovay('goal', goalName, data);
    }
  }

  trackPurchase(data: { amount: number; currency: string; product: string }): void {
    if (this.zenovay) {
      this.zenovay('revenue', data.amount, data.currency);
    }
  }
}

2. Rastrear Exibições de Página (Angular Router)

Crie um serviço para rastrear mudanças de rota:

// services/page-tracking.service.ts
import { Injectable } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { AnalyticsService } from './analytics.service';
import { filter } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class PageTrackingService {
  constructor(
    private router: Router,
    private analytics: AnalyticsService
  ) {
    this.initPageTracking();
  }

  private initPageTracking(): void {
    this.router.events.pipe(
      filter(event => event instanceof NavigationEnd)
    ).subscribe((event: NavigationEnd) => {
      this.analytics.track('pageview', {
        path: event.urlAfterRedirects,
        referrer: document.referrer,
        title: document.title,
      });
    });
  }
}

Inicialize no seu componente de aplicação:

// app.component.ts
import { Component, OnInit } from '@angular/core';
import { PageTrackingService } from './services/page-tracking.service';

@Component({
  selector: 'app-root',
  template: '<router-outlet></router-outlet>',
})
export class AppComponent implements OnInit {
  constructor(private pageTracking: PageTrackingService) {}

  ngOnInit(): void {
    // Service automatically starts tracking on init
  }
}

3. Rastrear Eventos Personalizados

Use o serviço de análises em seus componentes:

// components/signup-button/signup-button.component.ts
import { Component } from '@angular/core';
import { AnalyticsService } from '../../services/analytics.service';

@Component({
  selector: 'app-signup-button',
  template: `
    <button (click)="handleSignup()">
      Start Free Trial
    </button>
  `
})
export class SignupButtonComponent {
  constructor(private analytics: AnalyticsService) {}

  handleSignup(): void {
    this.analytics.track('signup_started', {
      plan: 'professional',
      source: 'pricing_page'
    });

    // Continue with signup logic
  }
}

Exemplo Completo

Aqui está uma implementação completa com todos os casos de uso comuns:

Serviço de Análises com Configuração

// services/zenovay-analytics.service.ts
import { Injectable } from '@angular/core';
import { environment } from '../environments/environment';

@Injectable({
  providedIn: 'root'
})
export class ZenovayAnalyticsService {
  private get zenovay(): any {
    return (window as any).zenovay;
  }

  // Page tracking
  trackPageView(path: string, title?: string): void {
    if (this.zenovay) {
      this.zenovay('track', 'pageview', {
        path,
        title: title || document.title,
        referrer: document.referrer,
      });
    }
  }

  // Custom events
  trackEvent(eventName: string, data?: Record<string, any>): void {
    if (this.zenovay) {
      this.zenovay('track', eventName, {
        ...data,
        timestamp: new Date().toISOString(),
        environment: environment.production ? 'production' : 'development',
      });
    }
  }

  // User identification
  identifyUser(userId: string, traits?: Record<string, any>): void {
    if (this.zenovay) {
      this.zenovay('identify', userId, traits);
    }
  }

  // Debug mode
  enableDebug(): void {
    if (!environment.production) {
      console.log('[Zenovay] Debug mode enabled');
    }
  }
}

Diretiva de Botão Rastreável

// directives/trackable-button.directive.ts
import { Directive, HostListener, Input } from '@angular/core';
import { AnalyticsService } from '../services/analytics.service';

@Directive({
  selector: '[appTrackableButton]',
  standalone: true
})
export class TrackableButtonDirective {
  @Input() eventName!: string;
  @Input() eventData?: Record<string, any>;

  constructor(private analytics: AnalyticsService) {}

  @HostListener('click', ['$event'])
  onClick(event: MouseEvent): void {
    if (!this.eventName) {
      console.warn('[TrackableButton] No event name provided');
      return;
    }

    this.analytics.track(this.eventName, {
      ...this.eventData,
      button_text: (event.target as HTMLElement).textContent,
      timestamp: Date.now(),
    });
  }
}

Exemplo de Uso

// pages/pricing/pricing.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TrackableButtonDirective } from '../../directives/trackable-button.directive';

@Component({
  selector: 'app-pricing',
  standalone: true,
  imports: [CommonModule, TrackableButtonDirective],
  template: `
    <div class="pricing-page">
      <h1>Pricing</h1>

      <button
        appTrackableButton
        [eventName]="'plan_selected'"
        [eventData]="{
          plan: 'scale',
          billing: 'monthly',
          price: 90
        }"
        class="btn-primary">
        Choose Scale
      </button>
    </div>
  `
})
export class PricingComponent {}

Padrões Avançados

Rastreamento Condicional

Rastreie eventos apenas quando certas condições são atendidas:

// pages/product/product.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { AnalyticsService } from '../../services/analytics.service';

interface Product {
  id: string;
  name: string;
  price: number;
  category: string;
}

@Component({
  selector: 'app-product',
  template: `<div>{{ product.name }}</div>`
})
export class ProductComponent implements OnInit {
  @Input() product!: Product;

  constructor(private analytics: AnalyticsService) {}

  ngOnInit(): void {
    // Only track if product is high-value
    if (this.product.price > 1000) {
      this.analytics.track('high_value_product_viewed', {
        product_id: this.product.id,
        product_name: this.product.name,
        price: this.product.price,
        category: this.product.category,
      });
    }
  }
}

Rastreamento de Formulário

Rastreie interações e envios de formulário:

// components/contact-form/contact-form.component.ts
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { AnalyticsService } from '../../services/analytics.service';

@Component({
  selector: 'app-contact-form',
  template: `
    <form [formGroup]="contactForm" (ngSubmit)="onSubmit()">
      <input
        type="text"
        formControlName="name"
        (focus)="onFieldFocus('name')"
        placeholder="Name"
      />
      <input
        type="email"
        formControlName="email"
        (focus)="onFieldFocus('email')"
        placeholder="Email"
      />
      <textarea
        formControlName="message"
        (focus)="onFieldFocus('message')"
        placeholder="Message"
      ></textarea>
      <button type="submit">Send Message</button>
    </form>
  `
})
export class ContactFormComponent {
  contactForm: FormGroup;

  constructor(
    private fb: FormBuilder,
    private analytics: AnalyticsService
  ) {
    this.contactForm = this.fb.group({
      name: ['', Validators.required],
      email: ['', [Validators.required, Validators.email]],
      message: ['', Validators.required],
    });
  }

  onFieldFocus(fieldName: string): void {
    this.analytics.track('form_field_focused', {
      form_id: 'contact',
      field_name: fieldName,
    });
  }

  async onSubmit(): Promise<void> {
    if (this.contactForm.invalid) {
      return;
    }

    const formData = this.contactForm.value;

    // Track form submission
    this.analytics.track('contact_form_submitted', {
      form_id: 'contact',
      has_message: formData.message.length > 0,
    });

    // Submit form logic
  }
}

Rastreamento de E-commerce

Rastreie visualizações de produtos, adições ao carrinho e compras:

// services/ecommerce-tracking.service.ts
import { Injectable } from '@angular/core';
import { AnalyticsService } from './analytics.service';

interface Product {
  id: string;
  name: string;
  category: string;
  price: number;
}

interface CartItem extends Product {
  quantity: number;
}

interface Cart {
  items: CartItem[];
  total: number;
}

interface Order extends Cart {
  id: string;
  tax: number;
  shipping: number;
}

@Injectable({
  providedIn: 'root'
})
export class EcommerceTrackingService {
  constructor(private analytics: AnalyticsService) {}

  trackProductView(product: Product): void {
    this.analytics.track('product_viewed', {
      product_id: product.id,
      product_name: product.name,
      category: product.category,
      price: product.price,
      currency: 'USD',
    });
  }

  trackAddToCart(product: Product, quantity: number = 1): void {
    this.analytics.track('product_added_to_cart', {
      product_id: product.id,
      product_name: product.name,
      quantity,
      price: product.price,
      total: product.price * quantity,
    });
  }

  trackCheckout(cart: Cart): void {
    this.analytics.track('checkout_started', {
      cart_total: cart.total,
      item_count: cart.items.length,
      currency: 'USD',
    });
  }

  trackPurchase(order: Order): void {
    this.analytics.trackPurchase({
      amount: order.total,
      currency: 'USD',
      product: `Order #${order.id}`,
    });

    this.analytics.track('purchase_completed', {
      order_id: order.id,
      revenue: order.total,
      tax: order.tax,
      shipping: order.shipping,
      currency: 'USD',
      items: order.items.map(item => ({
        product_id: item.id,
        quantity: item.quantity,
        price: item.price,
      })),
    });
  }
}

Uso no Componente

// pages/product-detail/product-detail.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { EcommerceTrackingService } from '../../services/ecommerce-tracking.service';

@Component({
  selector: 'app-product-detail',
  template: `
    <div *ngIf="product">
      <h1>{{ product.name }}</h1>
      <p>\${{ product.price }}</p>
      <button (click)="addToCart()">Add to Cart</button>
    </div>
  `
})
export class ProductDetailComponent implements OnInit {
  product: any;

  constructor(
    private route: ActivatedRoute,
    private ecommerceTracking: EcommerceTrackingService
  ) {}

  ngOnInit(): void {
    // Load product and track view
    this.route.params.subscribe(params => {
      // Fetch product by params.id
      this.product = {
        id: params['id'],
        name: 'Example Product',
        price: 99.99,
        category: 'Electronics',
      };

      this.ecommerceTracking.trackProductView(this.product);
    });
  }

  addToCart(): void {
    this.ecommerceTracking.trackAddToCart(this.product);
    // Add to cart logic
  }
}

Rastreamento de Erros com ErrorHandler

Rastreie erros automaticamente:

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

@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
  handleError(error: Error): void {
    // Track error via global zenovay object
    if ((window as any).zenovay) {
      (window as any).zenovay('track', 'error_occurred', {
        error_message: error.message,
        error_stack: error.stack,
        error_name: error.name,
        url: window.location.href,
        user_agent: navigator.userAgent,
      });
    }

    // Log to console
    console.error('Error caught by GlobalErrorHandler:', error);
  }
}

Registre na configuração do aplicativo:

// app.config.ts
import { ApplicationConfig, ErrorHandler } from '@angular/core';
import { GlobalErrorHandler } from './services/global-error-handler.service';

export const appConfig: ApplicationConfig = {
  providers: [
    { provide: ErrorHandler, useClass: GlobalErrorHandler },
    // other providers
  ]
};

Identificação do Usuário

Rastreie usuários identificados:

// services/user-tracking.service.ts
import { Injectable } from '@angular/core';
import { AnalyticsService } from './analytics.service';
import { AuthService } from './auth.service';

@Injectable({
  providedIn: 'root'
})
export class UserTrackingService {
  constructor(
    private analytics: AnalyticsService,
    private auth: AuthService
  ) {
    this.initUserTracking();
  }

  private initUserTracking(): void {
    this.auth.user$.subscribe(user => {
      if (user) {
        this.analytics.identify(user.id, {
          email: user.email,
          name: user.name,
          plan: user.subscription?.plan,
          signup_date: user.createdAt,
        });
      }
    });
  }
}

Inicialize no componente do aplicativo:

// app.component.ts
import { Component } from '@angular/core';
import { UserTrackingService } from './services/user-tracking.service';

@Component({
  selector: 'app-root',
  template: '<router-outlet></router-outlet>',
})
export class AppComponent {
  constructor(private userTracking: UserTrackingService) {
    // Service automatically starts tracking on init
  }
}

Suporte TypeScript

Suporte completo a TypeScript com eventos digitados:

// types/analytics.types.ts
export interface AnalyticsEvents {
  signup_started: {
    plan: 'free' | 'pro' | 'scale' | 'enterprise';
    source: string;
  };
  product_viewed: {
    product_id: string;
    product_name: string;
    price: number;
  };
  purchase_completed: {
    order_id: string;
    revenue: number;
    currency: string;
  };
}

// Declare the global zenovay function for TypeScript
declare global {
  interface Window {
    zenovay: (...args: any[]) => void;
  }
}

// Typed tracking service
import { Injectable } from '@angular/core';
import { AnalyticsEvents } from '../types/analytics.types';

@Injectable({
  providedIn: 'root'
})
export class TypedAnalyticsService {
  private get zenovay(): any {
    return (window as any).zenovay;
  }

  track<K extends keyof AnalyticsEvents>(
    eventName: K,
    data: AnalyticsEvents[K]
  ): void {
    if (this.zenovay) {
      this.zenovay('track', eventName, data);
    }
  }
}

Integração RxJS

Aproveite os padrões reativos do Angular:

// services/analytics-rxjs.service.ts
import { Injectable } from '@angular/core';
import { AnalyticsService } from './analytics.service';
import { Observable, of } from 'rxjs';
import { tap, catchError } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class AnalyticsRxjsService {
  constructor(private analytics: AnalyticsService) {}

  trackEvent$(eventName: string, data?: Record<string, any>): Observable<void> {
    return of(this.analytics.track(eventName, data)).pipe(
      tap(() => console.log(`[Analytics] Tracked: ${eventName}`)),
      catchError(error => {
        console.error('[Analytics] Error:', error);
        throw error;
      })
    );
  }
}

Otimização de Desempenho

Debounce Tracking

Evite rastreamento excessivo:

// components/search-box/search-box.component.ts
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
import { AnalyticsService } from '../../services/analytics.service';

@Component({
  selector: 'app-search-box',
  template: `
    <input
      type="search"
      [formControl]="searchControl"
      placeholder="Search..."
    />
  `
})
export class SearchBoxComponent {
  searchControl = new FormControl('');

  constructor(private analytics: AnalyticsService) {
    this.searchControl.valueChanges.pipe(
      debounceTime(500),
      distinctUntilChanged()
    ).subscribe(query => {
      if (query) {
        this.analytics.track('search_performed', {
          query,
          length: query.length,
        });
      }
    });
  }
}

Testes

Serviço de Análises Simulado em Testes

// testing/analytics.mock.ts
import { Injectable } from '@angular/core';

@Injectable()
export class MockAnalyticsService {
  track = jasmine.createSpy('track');
  identify = jasmine.createSpy('identify');
  trackGoal = jasmine.createSpy('trackGoal');
  trackPurchase = jasmine.createSpy('trackPurchase');
}

Componente de Teste

// components/signup-button/signup-button.component.spec.ts
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SignupButtonComponent } from './signup-button.component';
import { AnalyticsService } from '../../services/analytics.service';
import { MockAnalyticsService } from '../../testing/analytics.mock';

describe('SignupButtonComponent', () => {
  let component: SignupButtonComponent;
  let fixture: ComponentFixture<SignupButtonComponent>;
  let analyticsService: MockAnalyticsService;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [SignupButtonComponent],
      providers: [
        { provide: AnalyticsService, useClass: MockAnalyticsService }
      ]
    }).compileComponents();

    fixture = TestBed.createComponent(SignupButtonComponent);
    component = fixture.componentInstance;
    analyticsService = TestBed.inject(AnalyticsService) as any;
    fixture.detectChanges();
  });

  it('should track event on click', async () => {
    const button = fixture.nativeElement.querySelector('button');
    button.click();

    await fixture.whenStable();

    expect(analyticsService.track).toHaveBeenCalledWith('signup_started', {
      plan: 'professional',
      source: 'pricing_page'
    });
  });
});

Componentes Autônomos (Angular 17+)

Integração completa com componentes autônomos:

// app.component.ts
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterOutlet],
  template: '<router-outlet></router-outlet>',
})
export class AppComponent {}

Nenhuma configuração especial de módulo Angular é necessária. O script de rastreamento Zenovay em index.html cuida de tudo. Seu AnalyticsService simplesmente envolve as chamadas window.zenovay e pode ser injetado em qualquer componente.

Configuração de Ambiente

// environments/environment.ts
export const environment = {
  production: false,
};

// environments/environment.prod.ts
export const environment = {
  production: true,
};

O código de rastreamento é configurado diretamente na tag de script index.html. Para diferentes ambientes, você pode usar o recurso de substituição de arquivo do Angular para trocar arquivos index.html, ou simplesmente usar o mesmo código de rastreamento em todos os ambientes (Zenovay cuida da separação de ambiente no painel).

Recursos Adicionais

Esta página foi útil?