Skip to main content

Using widgets with Angular

In this tutorial, you'll learn how to easily integrate widgets into Angular, using the WatchList widget as an example. Let's create a simple widget component.

Simple widget

Let's create a component of watch list widget as basic example:

import { Component, effect, ElementRef, OnDestroy, OnInit, signal, ViewChild } from '@angular/core'
import { newWatchListWidget, WatchListWidgetState } from '@dxfeed-widgets/watch-list'

const dataProviders = {
ipfPath: '/your-path/ipf',
ipfAuthHeader: 'your-header',
feedPath: 'wss://your-path/feed',
feedAuthHeader: 'your-header',
fundamentalsPath: '/api/fundamentals',
fundamentalsAuthHeader: 'your-header',
schedulePath: '/api/schedule',
}

@Component({
selector: 'watch-list',
standalone: true,
template: ` <div class="root" #watch-list></div>`,
})
export class WatchListComponent implements OnInit, OnDestroy {
@ViewChild('watch-list', { static: true })
widgetContainer: ElementRef | undefined

widget: ReturnType<typeof newWatchListWidget> | null = null
injector = inject(Injector)
ngOnInit(): void {
this.widget = newWatchListWidget({
element: this.widgetContainer?.nativeElement,
providers: dataProviders,
// Here you can enable or disable different widget features
config: {
dataExportEnabled: true
}
// Here you can provide initial state of you widget
state: {
symbols: [],
selectedSymbol: undefined,
columns: ['symbol', 'lastPrice'],
columnSizes: {},
filters: {},
sort: undefined,
}
})
}

ngOnDestroy(): void {
this.widget?.unmount()
}
}

Connecting widgets

Using standalone widget is a possible way to use library, but let's make something more complex and create a connection between two widgets.

Firstly, we need to establish a shareable state. As an example, we can connect widgets by symbol. In this case, our app will appear as follows:

import { Component } from '@angular/core'
import { WatchListComponent } from './watch-list.component'

@Component({
selector: 'two-linked-widgets',
standalone: true,
imports: [WatchListComponent],
template: `
<div class="root">
<watch-list (selectedSymbolChange)="onSymbolSelect($event)"></watch-list>
</div>
`,
})
export class TwoLinkedComponent {
selectedSymbol: string | undefined

onSymbolSelect(symbol: string | undefined): void {
this.state.selectedSymbol = symbol
}
}

We provide some state on the top level of our application. Let's now adjust our simple component and create one more widget to facilitate the passing of widget state.

@Component({
selector: 'watch-list',
standalone: true,
template: ` <div class="root" #watch-list></div>`,
})
export class WatchListComponent implements OnInit, OnDestroy {
@ViewChild('watch-list', { static: true })
widgetContainer: ElementRef | undefined

@Output()
selectedSymbolChange: EventEmitter<string | undefined> = new EventEmitter()

widget: ReturnType<typeof newWatchListWidget> | null = null
injector = inject(Injector)
ngOnInit(): void {
this.widget = newWatchListWidget({
element: this.widgetContainer?.nativeElement,
providers: dataProviders,
// Here you can enable or disable different widget features
config: {
dataExportEnabled: true
}
// Here you can provide initial state of you widget
state: {
symbols: [],
selectedSymbol: undefined,
columns: ['symbol', 'lastPrice'],
columnSizes: {},
filters: {},
sort: undefined,
}
})

this.widget.addStateListener('selectedSymbol', symbol => {
this.selectedSymbolChange.emit(symbol)
})
}

ngOnDestroy(): void {
this.widget?.unmount()
}
}

@Component({
selector: 'time-and-sales',
standalone: true,
template: ` <div class="root" #time-and-sales></div>`,
})
export class TimeAndSalesComponent implements OnInit, OnDestroy, OnChanges {
@ViewChild('time-and-sales', { static: true })
widgetContainer: ElementRef | undefined

@Input() symbol: string | undefined

widget: ReturnType<typeof newTimeAndSalesWidget> | null = null
injector = inject(Injector)
ngOnInit(): void {
this.widget = newTimeAndSalesWidget({
element: this.widgetContainer?.nativeElement,
providers: dataProviders,
})
}

ngOnChanges(changes: SimpleChanges) {
this.widget?.updateState({ symbol: changes['symbol'].currentValue })
}

ngOnDestroy(): void {
this.widget?.unmount()
}
}

Now we can provide this data to another component or widget the following way:

import { Component } from '@angular/core'
import { WatchListComponent } from './watch-list.component'
import { TimeAndSalesComponent } from './time-and-sales.component'
import { AnyAnotherComponent } from './any-another.component'

@Component({
selector: 'two-linked-widgets',
standalone: true,
imports: [WatchListComponent, TimeAndSalesComponent, AnyAnotherComponent],
template: `
<div class="root">
<watch-list
(selectedSymbolChange)="onSymbolSelect($event)"
></watch-list>
<time-and-sales [symbol]="state.selectedSymbol"></time-and-sales
<any-another-component [symbol]="state.selectedSymbol"></any-another-component>
</div>
`,
})
export class TwoLinkedComponent {
selectedSymbol: string | undefined

onSymbolSelect(symbol: string | undefined): void {
this.state.selectedSymbol = symbol
}
}