Angular Integration
Learn how to integrate TradeX Chart into your Angular applications.
Installation
npm install tradex-chart
# or
yarn add tradex-chart
Basic Integration
Component Setup
// trading-chart.component.ts
import { Component, ElementRef, Input, OnInit, OnDestroy, ViewChild } from '@angular/core'
@Component({
selector: 'app-trading-chart',
template: `
<div #chartContainer class="chart-container"></div>
`,
styles: [`
.chart-container {
width: 100%;
height: 600px;
}
`]
})
export class TradingChartComponent implements OnInit, OnDestroy {
@ViewChild('chartContainer', { static: true }) chartContainer!: ElementRef
@Input() symbol: string = 'BTC/USDT'
@Input() data: number[][] = []
private chart: any = null
ngOnInit(): void {
this.initChart()
}
ngOnDestroy(): void {
if (this.chart) {
this.chart.destroy()
this.chart = null
}
}
private initChart(): void {
this.chart = document.createElement('tradex-chart')
this.chartContainer.nativeElement.appendChild(this.chart)
this.chart.start({
title: this.symbol,
state: {
ohlcv: this.data
}
})
}
public updateData(data: number[][]): void {
if (this.chart) {
this.chart.mergeData(data)
}
}
}
Module Declaration
// app.module.ts
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { TradingChartComponent } from './trading-chart/trading-chart.component'
@NgModule({
declarations: [
TradingChartComponent
],
imports: [
BrowserModule
],
schemas: [CUSTOM_ELEMENTS_SCHEMA], // Required for web components
bootstrap: [AppComponent]
})
export class AppModule { }
Usage
// app.component.ts
import { Component, OnInit } from '@angular/core'
import { HttpClient } from '@angular/common/http'
@Component({
selector: 'app-root',
template: `
<div class="app">
<h1>Trading Chart</h1>
<app-trading-chart
[symbol]="symbol"
[data]="chartData"
></app-trading-chart>
</div>
`
})
export class AppComponent implements OnInit {
symbol = 'BTC/USDT'
chartData: number[][] = []
constructor(private http: HttpClient) {}
ngOnInit(): void {
this.http.get<number[][]>('https://api.example.com/ohlcv')
.subscribe(data => {
this.chartData = data
})
}
}
Advanced Component
Full-Featured Component
import {
Component,
ElementRef,
Input,
Output,
EventEmitter,
OnInit,
OnDestroy,
OnChanges,
SimpleChanges,
ViewChild
} from '@angular/core'
import * as talib from 'talib-web'
interface ChartConfig {
title?: string
width?: number
height?: number
theme?: any
state?: any
[key: string]: any
}
@Component({
selector: 'app-tradex-chart',
template: `
<div class="chart-wrapper">
<div *ngIf="loading" class="loading">Loading chart...</div>
<div *ngIf="error" class="error">{{ error }}</div>
<div #chartContainer class="chart-container"></div>
</div>
`,
styles: [`
.chart-wrapper {
width: 100%;
height: 100%;
position: relative;
}
.chart-container {
width: 100%;
height: 100%;
}
.loading, .error {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.error {
color: red;
}
`]
})
export class TradeXChartComponent implements OnInit, OnDestroy, OnChanges {
@ViewChild('chartContainer', { static: true }) chartContainer!: ElementRef
@Input() symbol: string = ''
@Input() data: number[][] = []
@Input() config: ChartConfig = {}
@Output() ready = new EventEmitter<any>()
@Output() error = new EventEmitter<Error>()
private chart: any = null
loading = true
errorMessage: string | null = null
ngOnInit(): void {
this.initChart()
}
ngOnDestroy(): void {
if (this.chart) {
this.chart.destroy()
this.chart = null
}
}
ngOnChanges(changes: SimpleChanges): void {
if (changes['data'] && !changes['data'].firstChange) {
this.updateData(changes['data'].currentValue)
}
if (changes['symbol'] && !changes['symbol'].firstChange) {
this.updateSymbol(changes['symbol'].currentValue)
}
}
private async initChart(): Promise<void> {
try {
this.chart = document.createElement('tradex-chart')
this.chartContainer.nativeElement.appendChild(this.chart)
const chartConfig: ChartConfig = {
title: this.symbol,
talib: talib,
width: this.chartContainer.nativeElement.offsetWidth,
height: this.chartContainer.nativeElement.offsetHeight,
...this.config,
state: {
ohlcv: this.data,
...this.config.state
}
}
await this.chart.start(chartConfig)
this.loading = false
this.ready.emit(this.chart)
} catch (err: any) {
this.errorMessage = err.message
this.error.emit(err)
}
}
private updateData(data: number[][]): void {
if (this.chart && data) {
this.chart.mergeData(data)
}
}
private updateSymbol(symbol: string): void {
if (this.chart && symbol) {
this.chart.setTitle(symbol)
}
}
// Public methods
public addIndicator(name: string, params?: any): string {
return this.chart?.addIndicator(name, params)
}
public removeIndicator(id: string): void {
this.chart?.removeIndicator(id)
}
public exportImage(): string {
return this.chart?.exportImage()
}
public getChart(): any {
return this.chart
}
}
Using the Advanced Component
import { Component, ViewChild } from '@angular/core'
import { TradeXChartComponent } from './tradex-chart/tradex-chart.component'
@Component({
selector: 'app-trading-view',
template: `
<div class="trading-view">
<div class="controls">
<button (click)="handleAddRSI()">Add RSI</button>
<button (click)="handleExport()">Export Image</button>
</div>
<app-tradex-chart
#chart
[symbol]="symbol"
[data]="chartData"
[config]="chartConfig"
(ready)="onChartReady($event)"
(error)="onChartError($event)"
></app-tradex-chart>
</div>
`
})
export class TradingViewComponent {
@ViewChild('chart') chartComponent!: TradeXChartComponent
symbol = 'BTC/USDT'
chartData: number[][] = []
chartConfig = {
theme: {
candle: {
UpBodyColour: '#26a69a',
DnBodyColour: '#ef5350'
}
}
}
handleAddRSI(): void {
this.chartComponent.addIndicator('RSI', { period: 14 })
}
handleExport(): void {
const image = this.chartComponent.exportImage()
// Download or display image
}
onChartReady(chart: any): void {
console.log('Chart ready:', chart)
}
onChartError(error: Error): void {
console.error('Chart error:', error)
}
}
Real-time Updates
WebSocket Service
// websocket.service.ts
import { Injectable } from '@angular/core'
import { Observable, Subject } from 'rxjs'
export interface CandleUpdate {
timestamp: number
open: number
high: number
low: number
close: number
volume: number
isClosed: boolean
}
@Injectable({
providedIn: 'root'
})
export class WebSocketService {
private ws: WebSocket | null = null
private subject = new Subject<CandleUpdate>()
connect(url: string): Observable<CandleUpdate> {
this.ws = new WebSocket(url)
this.ws.onmessage = (event) => {
const data = JSON.parse(event.data)
this.subject.next(data)
}
this.ws.onerror = (error) => {
console.error('WebSocket error:', error)
}
return this.subject.asObservable()
}
send(message: any): void {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(message))
}
}
disconnect(): void {
if (this.ws) {
this.ws.close()
this.ws = null
}
}
}
Real-time Chart Component
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core'
import { Subscription } from 'rxjs'
import { WebSocketService, CandleUpdate } from './websocket.service'
import { TradeXChartComponent } from './tradex-chart/tradex-chart.component'
@Component({
selector: 'app-realtime-chart',
template: `
<app-tradex-chart
#chart
[symbol]="symbol"
[data]="chartData"
></app-tradex-chart>
`
})
export class RealtimeChartComponent implements OnInit, OnDestroy {
@ViewChild('chart') chartComponent!: TradeXChartComponent
symbol = 'BTC/USDT'
chartData: number[][] = []
private subscription?: Subscription
constructor(private wsService: WebSocketService) {}
ngOnInit(): void {
this.subscription = this.wsService
.connect('wss://api.example.com/ws')
.subscribe((update: CandleUpdate) => {
this.handleUpdate(update)
})
// Subscribe to symbol
this.wsService.send({
type: 'subscribe',
symbol: this.symbol
})
}
ngOnDestroy(): void {
this.subscription?.unsubscribe()
this.wsService.disconnect()
}
private handleUpdate(update: CandleUpdate): void {
const chart = this.chartComponent.getChart()
if (!chart) return
const candle = [
update.timestamp,
update.open,
update.high,
update.low,
update.close,
update.volume
]
if (update.isClosed) {
chart.addCandle(candle)
} else {
chart.updateStreamingCandle(candle)
}
}
}
State Management with NgRx
Chart State
// chart.state.ts
export interface ChartState {
symbol: string
data: number[][]
indicators: Array<{ name: string; params: any }>
timeframe: string
loading: boolean
error: string | null
}
export const initialState: ChartState = {
symbol: 'BTC/USDT',
data: [],
indicators: [],
timeframe: '1h',
loading: false,
error: null
}
Chart Actions
// chart.actions.ts
import { createAction, props } from '@ngrx/store'
export const loadChartData = createAction(
'[Chart] Load Data',
props<{ symbol: string; timeframe: string }>()
)
export const loadChartDataSuccess = createAction(
'[Chart] Load Data Success',
props<{ data: number[][] }>()
)
export const loadChartDataFailure = createAction(
'[Chart] Load Data Failure',
props<{ error: string }>()
)
export const addIndicator = createAction(
'[Chart] Add Indicator',
props<{ name: string; params: any }>()
)
export const removeIndicator = createAction(
'[Chart] Remove Indicator',
props<{ index: number }>()
)
Chart Reducer
// chart.reducer.ts
import { createReducer, on } from '@ngrx/store'
import * as ChartActions from './chart.actions'
import { initialState } from './chart.state'
export const chartReducer = createReducer(
initialState,
on(ChartActions.loadChartData, (state) => ({
...state,
loading: true,
error: null
})),
on(ChartActions.loadChartDataSuccess, (state, { data }) => ({
...state,
data,
loading: false
})),
on(ChartActions.loadChartDataFailure, (state, { error }) => ({
...state,
error,
loading: false
})),
on(ChartActions.addIndicator, (state, { name, params }) => ({
...state,
indicators: [...state.indicators, { name, params }]
})),
on(ChartActions.removeIndicator, (state, { index }) => ({
...state,
indicators: state.indicators.filter((_, i) => i !== index)
}))
)
Using with NgRx
import { Component, OnInit } from '@angular/core'
import { Store } from '@ngrx/store'
import { Observable } from 'rxjs'
import * as ChartActions from './store/chart.actions'
import { ChartState } from './store/chart.state'
@Component({
selector: 'app-chart-container',
template: `
<app-tradex-chart
[symbol]="(chartState$ | async)?.symbol || ''"
[data]="(chartState$ | async)?.data || []"
></app-tradex-chart>
`
})
export class ChartContainerComponent implements OnInit {
chartState$: Observable<ChartState>
constructor(private store: Store<{ chart: ChartState }>) {
this.chartState$ = store.select('chart')
}
ngOnInit(): void {
this.store.dispatch(ChartActions.loadChartData({
symbol: 'BTC/USDT',
timeframe: '1h'
}))
}
}
Standalone Components (Angular 14+)
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
import { CommonModule } from '@angular/common'
@Component({
selector: 'app-trading-chart',
standalone: true,
imports: [CommonModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `
<div #chartContainer class="chart-container"></div>
`,
styles: [`
.chart-container {
width: 100%;
height: 600px;
}
`]
})
export class TradingChartComponent {
// Component implementation
}
Testing
Component Testing
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
import { TradingChartComponent } from './trading-chart.component'
describe('TradingChartComponent', () => {
let component: TradingChartComponent
let fixture: ComponentFixture<TradingChartComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [TradingChartComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
}).compileComponents()
fixture = TestBed.createComponent(TradingChartComponent)
component = fixture.componentInstance
})
it('should create', () => {
expect(component).toBeTruthy()
})
it('should initialize chart on init', () => {
component.symbol = 'BTC/USDT'
component.data = [[1609459200000, 29000, 29500, 28800, 29200, 1234.56]]
fixture.detectChanges()
expect(component['chart']).toBeTruthy()
})
it('should cleanup on destroy', () => {
component.ngOnInit()
const destroySpy = jasmine.createSpy('destroy')
component['chart'] = { destroy: destroySpy }
component.ngOnDestroy()
expect(destroySpy).toHaveBeenCalled()
})
})
Common Patterns
Multi-Chart Layout
@Component({
selector: 'app-multi-chart',
template: `
<div class="grid">
<app-tradex-chart
*ngFor="let symbol of symbols"
[symbol]="symbol"
[data]="chartData[symbol]"
[config]="{ height: 400 }"
></app-tradex-chart>
</div>
`,
styles: [`
.grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
}
`]
})
export class MultiChartComponent {
symbols = ['BTC/USDT', 'ETH/USDT', 'BNB/USDT']
chartData: { [key: string]: number[][] } = {}
}
Troubleshooting
CUSTOM_ELEMENTS_SCHEMA
Always include CUSTOM_ELEMENTS_SCHEMA in your module:
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
Change Detection
For better performance with large datasets:
import { ChangeDetectionStrategy } from '@angular/core'
@Component({
changeDetection: ChangeDetectionStrategy.OnPush
})
Related Documentation
- Getting Started - Basic setup
- Configuration - Chart configuration
- WebSocket Integration - Real-time data
- API Reference - Complete API
- React Integration - React guide
- Vue Integration - Vue guide