Actualizaciones de datos en vivo de Angular Grid
El componente Grid es capaz de manejar miles de actualizaciones por segundo, sin dejar de responder a las interacciones del usuario.
最速で機能豊富な Angular Data Grid は、ページング、ソート、フィルタリング、グループ化、PDF および Excel へのエクスポートなどの機能を提供します。究極のアプリ構築エクスペリエンスとデータ操作に必要なすべてが揃っています。
Angular Ejemplo de actualización de datos en tiempo real
En el ejemplo siguiente se muestra el rendimiento de la cuadrícula cuando todos los registros se actualizan varias veces por segundo. Utilice los controles de la interfaz de usuario para elegir el número de registros cargados y la frecuencia de las actualizaciones. Introduzca los mismos datos en el Gráfico de líneas para experimentar las potentes capacidades de gráficos de Ignite UI for Angular. El Chart
botón mostrará Category Prices per Region
los datos de las filas seleccionadas y el botón de columna Chart
mostrará lo mismo para la fila actual.
import { AfterViewInit, Component, HostBinding, OnDestroy, ViewChild } from '@angular/core';
import { Router } from '@angular/router';
import { IgxDialogComponent, IgxOverlayOutletDirective, OverlaySettings, IgxDialogTitleDirective, IgxDialogActionsDirective, IgxButtonDirective, IgxFocusDirective } from 'igniteui-angular';
import { IgxCategoryChartComponent, IgxCategoryChartCoreModule } from 'igniteui-angular-charts';
import { Stock } from '../data/financialData';
import { ControllerComponent } from './controllers.component';
import { GridFinJSComponent } from './grid-finjs.component';
@Component({
selector: 'app-finjs-main',
styleUrls: ['./main.component.scss'],
templateUrl: './main.component.html',
imports: [ControllerComponent, GridFinJSComponent, IgxOverlayOutletDirective, IgxDialogComponent, IgxDialogTitleDirective, IgxCategoryChartCoreModule, IgxDialogActionsDirective, IgxButtonDirective, IgxFocusDirective]
})
export class FinJSDemoComponent implements OnDestroy, AfterViewInit {
@ViewChild('finGrid', { static: true }) public finGrid: GridFinJSComponent;
@ViewChild('controllers', { static: true }) public controller: ControllerComponent;
@ViewChild('dialog', { static: true }) public dialog: IgxDialogComponent;
@ViewChild('chart1', { static: true }) public chart: IgxCategoryChartComponent;
@ViewChild(IgxOverlayOutletDirective, { static: true }) public outlet: IgxOverlayOutletDirective;
public overlaySettings: OverlaySettings = {
modal: false,
closeOnOutsideClick: true
};
@HostBinding('class.dark-theme')
public darkTheme = false;
public properties = ['price', 'country'];
public chartData: Stock[] = [];
public volume = 1000;
public frequency = 500;
private _timer: ReturnType<typeof setInterval>;
public onSwitchChanged(event: { action: string; value: boolean }): void {
switch (event.action) {
case 'toolbar': {
this.finGrid.showToolbar = event.value;
break;
}
case 'grouped': {
this.finGrid.toggleGrouping();
break;
}
case 'theme': {
this.darkTheme = event.value;
break;
}
default: break;
}
}
public ngAfterViewInit(): void {
this.overlaySettings.outlet = this.outlet;
}
public onVolumeChanged(volume: number): void {
this.volume = volume;
this.finGrid.dataService.hasRemoteConnection ? this.finGrid.dataService
.broadcastParams(this.controller.frequency, this.volume, false) : this.finGrid.dataService.getData(volume);
}
public onFrequencyChanged(frequency: number): void {
this.frequency = frequency;
}
public onPlayAction(event: { action: string }): void {
switch (event.action) {
case 'playAll': {
if (this.finGrid.dataService.hasRemoteConnection) {
this.finGrid.dataService.broadcastParams(this.frequency, this.volume, true);
} else {
const currData: Stock[] = this.finGrid.grid.filteredSortedData ?? this.finGrid.grid.data;
this._timer = setInterval(() => this.finGrid.dataService.updateAllPriceValues(currData), this.controller.frequency);
}
break;
}
case 'stop': {
this.finGrid.dataService.hasRemoteConnection ? this.finGrid.dataService.stopLiveData() : this.stopFeed();
break;
}
case 'chart': {
if (this.finGrid.grid.selectedRows.length !== 0) {
this.setChartData(this.finGrid.grid.selectedRows);
this.dialog.open(this.overlaySettings);
} else {
this.controller.toast.open('Please select some rows first!');
};
break;
}
default: {
break;
}
}
}
public setChartData(args: Stock[]): void {
this.chartData = [];
args.forEach(rowKey => {
const row: Stock = this.finGrid.grid.getRowByKey(rowKey).data;
this.chartData.push(row);
this.chart.notifyInsertItem(this.chartData, this.chartData.length - 1, row);
});
// this.controller.controls[2].disabled = this.chartData.length === 0;
this.setLabelIntervalAndAngle();
this.setChartConfig('Countries', 'Prices (USD)', 'Data Chart with prices by Category and Country');
}
public onCloseHandler(): void {
if (this.finGrid.grid.navigation.activeNode) {
if (this.finGrid.grid.navigation.activeNode.row === -1) {
this.finGrid.grid.theadRow.nativeElement.focus();
} else {
this.finGrid.grid.tbody.nativeElement.focus();
}
this.controller.playButtons.deselectButton(2);
}
}
public closeDialog(): void {
this.controller.playButtons.deselectButton(2);
this.dialog.close();
}
public setChartConfig(xAsis: string, yAxis: string, title: string): void {
// update label interval and angle based on data
this.setLabelIntervalAndAngle();
this.chart.xAxisTitle = xAsis;
this.chart.yAxisTitle = yAxis;
this.chart.chartTitle = title;
}
public setLabelIntervalAndAngle(): void {
const intervalSet = this.chartData.length;
if (intervalSet < 10) {
this.chart.xAxisLabelAngle = 0;
this.chart.xAxisInterval = 1;
} else if (intervalSet < 15) {
this.chart.xAxisLabelAngle = 30;
this.chart.xAxisInterval = 1;
} else if (intervalSet < 40) {
this.chart.xAxisLabelAngle = 90;
this.chart.xAxisInterval = 1;
} else if (intervalSet < 100) {
this.chart.xAxisLabelAngle = 90;
this.chart.xAxisInterval = 3;
} else if (intervalSet < 200) {
this.chart.xAxisLabelAngle = 90;
this.chart.xAxisInterval = 5;
} else if (intervalSet < 400) {
this.chart.xAxisLabelAngle = 90;
this.chart.xAxisInterval = 7;
} else if (intervalSet > 400) {
this.chart.xAxisLabelAngle = 90;
this.chart.xAxisInterval = 10;
}
this.chart.yAxisAbbreviateLargeNumbers = true;
}
public openSingleRowChart(rowData: Stock): void {
this.chartData = [];
setTimeout(() => {
this.chartData = this.finGrid.grid.data.filter(item => item.region === rowData.region &&
item.category === rowData.category);
this.chart.notifyInsertItem(this.chartData, this.chartData.length - 1, {});
this.setLabelIntervalAndAngle();
this.chart.chartTitle = 'Data Chart with prices of ' + this.chartData[0].category + ' in ' +
this.chartData[0].region + ' Region';
this.dialog.open();
}, 200);
}
public stopFeed(): void {
if (this._timer) {
clearInterval(this._timer);
}
}
public ngOnDestroy(): void {
this.stopFeed();
}
}
ts<div class="main__wrapper ig-scrollbar" [class.fin-dark-theme]="darkTheme">
<app-finjs-controllers #controllers
(switchChanged)="onSwitchChanged($event)"
(volumeChanged)="onVolumeChanged($event)"
(frequencyChanged)="onFrequencyChanged($event)"
(playAction)="onPlayAction($event)">
</app-finjs-controllers>
<app-finjs-grid #finGrid
(selectedDataChanged)="setChartData($event)"
(keyDown)="dialog.open()"
(chartColumnKeyDown)="openSingleRowChart($event)">
</app-finjs-grid>
</div>
<div igxOverlayOutlet #outlet="overlay-outlet">
<igx-dialog #dialog [closeOnOutsideSelect]="true" (closing)="onCloseHandler()">
<div (keydown)="closeDialog()" class="chart-container">
<igx-dialog-title> Chart </igx-dialog-title>
<div>
<igx-category-chart #chart1 [dataSource]="chartData" width="100%" chartType="column" xAxisInterval="20"
xAxisLabelAngle="90" [includedProperties]="properties" height="400px" [tooltipTemplate]="seriesTooltip">
</igx-category-chart>
</div>
<div igxDialogActions>
<button igxButton (click)="closeDialog()" [igxFocus]="dialog.isOpen">ОК</button>
</div>
</div>
</igx-dialog>
</div>
<ng-template let-series="series" let-item="item" #seriesTooltip>
<div class="tooltipTable">
<div class="tooltipRow">
<div><b>Category:</b> {{ item.category }}</div>
</div>
<div class="tooltipRow">
<div><b>Country:</b> {{ item.country }}</div>
</div>
<div class="tooltipRow">
<div><b>Price:</b> ${{ item.price }}</div>
</div>
</div>
</ng-template>
html.main__wrapper {
height: 100%;
min-height: 100%;
display: flex;
flex-direction: column;
app-finjs-grid {
height: 100%;
}
}
.chart-container {
width: 50vw;
}
scss
¿Te gusta esta muestra? Obtenga acceso a nuestro kit de herramientas de Ignite UI for Angular completo y comience a crear sus propias aplicaciones en minutos. Descárgalo gratis.
Vinculación de datos y actualizaciones
Un servicio proporciona datos al componente cuando se carga la página y cuando se utiliza el controlador deslizante para recuperar una cierta cantidad de registros. Mientras que en un escenario real los datos actualizados se consumirían desde el servicio, aquí los datos se actualizan en código. Esto se hace para mantener la demostración simple y enfocarse en su objetivo principal: demostrar el rendimiento de la red.
<igx-grid #grid [data]="data"></igx-grid>
html
public ngOnInit() {
this.localService.getData(this.volume);
this.volumeSlider.onValueChange.subscribe(x => this.localService.getData(this.volume);
this.localService.records.subscribe(x => { this.data = x; });
}
typescript
Angular tuberías se utilizan internamente para actualizar la vista de cuadrícula. Un cambio en el valor del campo de datos o un cambio en el objeto de datos/referencia de recopilación de datos activará las canalizaciones correspondientes. Sin embargo, este no es el caso de las columnas, que están enlazadas a complex data objects
, porque la tubería pura Angular no detectará un cambio en una propiedad anidada. Para resolver la situación, proporcione una nueva referencia de objeto para el objeto de datos que contiene la propiedad. Ejemplo:
<igx-grid #grid [data]="data">
<igx-column field="price.usd"></igx-column>
</igx-grid>
html
private updateData(data: IRecord[]) {
const newData = []
for (const rowData of data) {
rowData.price = { usd: getUSD(), eur: getEUR() };
newData.push({...rowData});
}
this.grid.data = newData;
}
typescript
Plantillas
La actualización de la vista funciona de la misma manera para columnas con una plantilla predeterminada y para columnas con una plantilla personalizada. Sin embargo, se recomienda mantener las plantillas personalizadas relativamente simples. A medida que crece el número de elementos en la plantilla, también aumenta el impacto negativo en el rendimiento.
Transmisión de datos en vivo con Dock Manager y componentes igxGrid
El propósito de esta demostración es mostrar un tablero de pantalla financiera con flujo de datos en tiempo real mediante un back-end de concentrador de SignalR. Como puede ver, el componente igxGrid maneja con facilidad las actualizaciones de alta frecuencia del servidor. El código de la aplicación ASP.NET Core que usa SignalR se puede encontrar en este repositorio público de GitHub.
/* eslint-disable max-len */
import { AfterViewInit, ChangeDetectorRef, Component, ComponentFactoryResolver, ElementRef, Renderer2, OnDestroy, OnInit, DoCheck, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { AbsoluteScrollStrategy, ConnectedPositioningStrategy, DefaultSortingStrategy, GridColumnDataType, IgxColumnComponent, IgxGridComponent, IgxOverlayOutletDirective, IgxSelectComponent, OverlaySettings, SortingDirection, IgxSwitchComponent, IgxLabelDirective, IgxPrefixDirective, IgxIconComponent, IgxSelectItemComponent, IgxButtonDirective, IgxCellTemplateDirective, IgxPaginatorComponent } from 'igniteui-angular';
import { IgcDockManagerLayout, IgcDockManagerPaneType, IgcSplitPane, IgcSplitPaneOrientation } from 'igniteui-dockmanager';
import { Subject } from 'rxjs';
import { first, takeUntil } from 'rxjs/operators';
import { FloatingPanesService } from '../services/floating-panes.service';
import { SignalRService } from '../services/signal-r.service';
import { DockSlotComponent, GridHostDirective } from './dock-slot.component';
import { FormsModule } from '@angular/forms';
import { NgFor, NgIf, AsyncPipe, CurrencyPipe } from '@angular/common';
@Component({
encapsulation: ViewEncapsulation.None,
providers: [SignalRService, FloatingPanesService],
selector: 'app-finjs-dock-manager',
templateUrl: './grid-finjs-dock-manager.component.html',
styleUrls: ['./grid-finjs-dock-manager.component.scss'],
imports: [IgxSwitchComponent, FormsModule, IgxSelectComponent, IgxLabelDirective, IgxPrefixDirective, IgxIconComponent, NgFor, IgxSelectItemComponent, IgxButtonDirective, IgxOverlayOutletDirective, IgxGridComponent, IgxColumnComponent, IgxCellTemplateDirective, NgIf, IgxPaginatorComponent, GridHostDirective, AsyncPipe, CurrencyPipe],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class GridFinJSDockManagerComponent implements OnInit, OnDestroy, AfterViewInit, DoCheck {
@ViewChild('grid1', { static: true }) public grid1: IgxGridComponent;
@ViewChild('grid2', { static: true }) public grid2: IgxGridComponent;
@ViewChild(GridHostDirective) public host: GridHostDirective;
@ViewChild('dock', { read: ElementRef }) public dockManager: ElementRef<HTMLIgcDockmanagerElement>;
@ViewChild('priceTemplate', { read: TemplateRef })
public priceTemplate: TemplateRef<any>;
@ViewChild(IgxSelectComponent) public select: IgxSelectComponent;
@ViewChild('freq', { read: IgxSelectComponent }) public selectFrequency: IgxSelectComponent;
@ViewChild(IgxOverlayOutletDirective) outlet: IgxOverlayOutletDirective;
public isDarkTheme = true;
public frequencyItems: number[] = [300, 600, 900];
public frequency = this.frequencyItems[1];
public dataVolumeItems: number[] = [100, 500, 1000, 5000, 10000];
public dataVolume: number = this.dataVolumeItems[1];
public isLoading = true;
public data: any;
public liveData = true;
public columnFormat = { digitsInfo: '1.3-3'};
public columnFormatChangeP = { digitsInfo: '2.3-3'};
public slotCounter = 1;
public customOverlaySettings: OverlaySettings = {
positionStrategy: new ConnectedPositioningStrategy(),
scrollStrategy: new AbsoluteScrollStrategy()
};
public freqOverlaySettings: OverlaySettings = {
positionStrategy: new ConnectedPositioningStrategy(),
scrollStrategy: new AbsoluteScrollStrategy()
};
public docLayout: IgcDockManagerLayout = {
rootPane: {
type: IgcDockManagerPaneType.splitPane,
orientation: IgcSplitPaneOrientation.horizontal,
panes: [
{
type: IgcDockManagerPaneType.contentPane,
contentId: 'actionPane',
header: 'Actions pane',
size: 20,
isPinned: false,
allowClose: false
},
{
size: 50,
type: IgcDockManagerPaneType.contentPane,
contentId: 'gridStockPrices',
header: 'Stock Market Data',
allowClose: false
},
{
type: IgcDockManagerPaneType.splitPane,
orientation: IgcSplitPaneOrientation.vertical,
size: 50,
panes: [
{
type: IgcDockManagerPaneType.documentHost,
size: 50,
rootPane: {
type: IgcDockManagerPaneType.splitPane,
orientation: IgcSplitPaneOrientation.horizontal,
panes: [
{
type: IgcDockManagerPaneType.tabGroupPane,
panes: [
{
type: IgcDockManagerPaneType.contentPane,
contentId: 'forexMarket',
header: 'Market Data 1'
},
{
type: IgcDockManagerPaneType.contentPane,
contentId: 'content4',
header: 'Market Data 2'
}
]
}
]
}},
{
type: IgcDockManagerPaneType.contentPane,
contentId: 'etfStockPrices',
header: 'Market Data 3',
size: 50,
allowClose: false
}
]
}
]
},
floatingPanes: []
};
public columns: { field: string,
width: string,
sortable: boolean,
filterable: boolean,
type: GridColumnDataType,
groupable?: boolean,
cellClasses?: string,
bodyTemplate?: string } [] = [
{ field: 'buy', width: '110px', sortable: false, filterable: false, type: 'currency' },
{ field: 'sell', width: '110px', sortable: false, filterable: false, type: 'currency' },
{ field: 'openPrice', width: '120px', sortable: true, filterable: true, type: 'currency'},
{ field: 'lastUpdated', width: '120px', sortable: true, filterable: true, type: 'date'},
{ field: 'spread', width: '110px', sortable: false, filterable: false, type: 'number' },
{ field: 'volume', width: '110px', sortable: true, filterable: false, type: 'number' },
{ field: 'settlement', width: '100px', sortable: true, filterable: true, type: 'string', groupable: true },
{ field: 'country', width: '100px', sortable: true, filterable: true, type: 'string'},
{ field: 'highD', width: '110px', sortable: true, filterable: false, type: 'currency' },
{ field: 'lowD', width: '110px', sortable: true, filterable: false, type: 'currency' },
{ field: 'highY', width: '110px', sortable: true, filterable: false, type: 'currency' },
{ field: 'lowY', width: '110px', sortable: true, filterable: false, type: 'currency' },
{ field: 'startY', width: '110px', sortable: true, filterable: false, type: 'currency' },
{ field: 'indGrou', width: '136px', sortable: false, filterable: false, type: 'string'},
{ field: 'indSect', width: '136px', sortable: false, filterable: false, type: 'string'},
{ field: 'indSubg', width: '136px', sortable: false, filterable: false, type: 'string'},
{ field: 'secType', width: '136px', sortable: false, filterable: false, type: 'string'},
{ field: 'issuerN', width: '136px', sortable: false, filterable: false, type: 'string'},
{ field: 'moodys', width: '136px', sortable: false, filterable: false, type: 'string'},
{ field: 'fitch', width: '136px', sortable: false, filterable: false, type: 'string'},
{ field: 'dbrs', width: '136px', sortable: false, filterable: false, type: 'string'},
{ field: 'collatT', width: '136px', sortable: false, filterable: false, type: 'string'},
{ field: 'curncy', width: '136px', sortable: false, filterable: false, type: 'string'},
{ field: 'security', width: '136px', sortable: false, filterable: false, type: 'string'},
{ field: 'sector', width: '136px', sortable: false, filterable: false, type: 'string'},
{ field: 'cusip', width: '136px', sortable: false, filterable: false, type: 'string'},
{ field: 'ticker', width: '136px', sortable: false, filterable: false, type: 'string'},
{ field: 'cpn', width: '136px', sortable: false, filterable: false, type: 'string'}
];
private destroy$ = new Subject<any>();
constructor(public dataService: SignalRService, private paneService: FloatingPanesService, private cdr: ChangeDetectorRef, private componentFactoryResolver: ComponentFactoryResolver, private elementRef: ElementRef, private renderer:Renderer2) {}
public ngOnInit() {
this.dataService.startConnection(this.frequency, this.dataVolume, true, false);
this.data = this.dataService.data;
this.data.pipe(takeUntil(this.destroy$)).subscribe((data) => {
if (data.length !== 0) {
this.isLoading = false;
};
});
}
public ngOnDestroy() {
this.dataService.stopLiveData();
this.destroy$.next(true);
this.destroy$.complete();
}
public ngDoCheck() {
if (this.isDarkTheme) {
this.renderer.removeClass(this.elementRef.nativeElement, 'light-theme');
this.renderer.addClass(this.elementRef.nativeElement, 'dark-theme');
}
else {
this.renderer.removeClass(this.elementRef.nativeElement, 'dark-theme');
this.renderer.addClass(this.elementRef.nativeElement, 'light-theme');
}
}
public ngAfterViewInit() {
// This 500ms timeout is used as a workaround for StackBlitz ExpressionChangedAfterItHasBeenChecked Error
setTimeout(() => {
const x = (this.dockManager.nativeElement.getBoundingClientRect().width / 3);
const y = (this.dockManager.nativeElement.getBoundingClientRect().height / 3);
this.paneService.initialPanePosition = { x, y };
this.grid2.selectColumns(['price', 'change', 'changeP']);
this.customOverlaySettings.target = this.select.inputGroup.element.nativeElement;
this.customOverlaySettings.outlet = this.outlet;
this.freqOverlaySettings.target = this.selectFrequency.inputGroup.element.nativeElement;
this.freqOverlaySettings.outlet = this.outlet;
this.grid1.groupingExpressions = [{
dir: SortingDirection.Desc,
fieldName: 'category',
ignoreCase: false,
strategy: DefaultSortingStrategy.instance()
},
{
dir: SortingDirection.Desc,
fieldName: 'type',
ignoreCase: false,
strategy: DefaultSortingStrategy.instance()
},
{
dir: SortingDirection.Desc,
fieldName: 'settlement',
ignoreCase: false,
strategy: DefaultSortingStrategy.instance()
}];
}, 500);
}
public paramsChanged() {
this.dataService.hasRemoteConnection ? this.dataService.broadcastParams(this.frequency, this.dataVolume, true, false) :
this.dataService.startConnection(this.frequency, this.dataVolume, true, false);
this.data = this.dataService.data;
}
public stopFeed() {
this.dataService.stopLiveData();
}
public streamData(event) {
event.checked ? this.paramsChanged() : this.stopFeed();
this.liveData = event.checked;
}
/* eslint-disable @typescript-eslint/member-ordering */
/** Grid CellStyles and CellClasses */
private negative = (rowData: any): boolean => rowData['changeP'] < 0;
private positive = (rowData: any): boolean => rowData['changeP'] > 0;
private changeNegative = (rowData: any): boolean => rowData['changeP'] < 0 && rowData['changeP'] > -1;
private changePositive = (rowData: any): boolean => rowData['changeP'] > 0 && rowData['changeP'] < 1;
private strongPositive = (rowData: any): boolean => rowData['changeP'] >= 1;
private strongNegative = (rowData: any, key: string): boolean => rowData['changeP'] <= -1;
public trends = {
changeNeg: this.changeNegative,
changePos: this.changePositive,
negative: this.negative,
positive: this.positive,
strongNegative: this.strongNegative,
strongPositive: this.strongPositive
};
public trendsChange = {
changeNeg2: this.changeNegative,
changePos2: this.changePositive,
strongNegative2: this.strongNegative,
strongPositive2: this.strongPositive
};
public createGrid() {
const id: string = 'slot-' + this.slotCounter++;
const splitPane: IgcSplitPane = {
type: IgcDockManagerPaneType.splitPane,
orientation: IgcSplitPaneOrientation.horizontal,
floatingWidth: 550,
floatingHeight: 350,
panes: [
{
type: IgcDockManagerPaneType.contentPane,
header: id,
contentId: id
}
]
};
this.paneService.appendPane(splitPane);
this.dockManager.nativeElement.layout.floatingPanes.push(splitPane);
this.docLayout = { ...this.dockManager.nativeElement.layout };
this.cdr.detectChanges();
// Create Dock Slot Component
const dockSlotComponentFactory = this.componentFactoryResolver.resolveComponentFactory(DockSlotComponent);
const dockSlotComponent = this.host.viewContainerRef.createComponent(dockSlotComponentFactory);
dockSlotComponent.instance.id = id;
dockSlotComponent.instance.viewInit.pipe(first()).subscribe(() => {
const gridViewContainerRef = dockSlotComponent.instance.gridHost.viewContainerRef;
this.loadGridComponent(gridViewContainerRef, dockSlotComponent.instance.destroy$);
});
}
public loadGridComponent(viewContainerRef: ViewContainerRef, destructor: Subject<any>) {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(IgxGridComponent);
viewContainerRef.clear();
const componentRef = viewContainerRef.createComponent(componentFactory);
const grid = (componentRef.instance as IgxGridComponent);
grid.autoGenerate = true;
this.dataService.data.pipe(takeUntil(destructor)).subscribe(d => grid.data = d);
grid.columnInit.pipe(takeUntil(destructor)).subscribe((col: IgxColumnComponent) => {
if (col.field === 'price') {
col.cellClasses = this.trends;
col.bodyTemplate = this.priceTemplate;
}
if (col.field === 'change' || col.field === 'changeP') {
col.cellClasses = this.trendsChange;
}
});
grid.columnSelection = 'multiple';
grid.cellSelection = 'none';
// Use detectChanges because of ExpressionChangedAfterItHasBeenChecked Error when creating a dynamic pane
this.cdr.detectChanges();
}
/* eslint-enable @typescript-eslint/member-ordering */
}
ts<igc-dockmanager #dock class="dock-m-position ig-scrollbar" [layout]="docLayout">
<div class="actionPane" slot="actionPane" style="height: 100%; padding: 20px;">
<div class="actionItem">
Change theme: <br/> <igx-switch [(ngModel)]="isDarkTheme">Dark Mode</igx-switch>
</div>
<div class="actionItem">
Start/Stop live data: <igx-switch [(ngModel)]="liveData" (change)="streamData($event)">{{ liveData ===
true ? 'Streaming' : 'Not Streaming' }}</igx-switch>
</div>
<div class="actionItem">
<!-- Change volume -->
<igx-select [(ngModel)]="dataVolume" (ngModelChange)="paramsChanged()" [overlaySettings]="customOverlaySettings">
<label igxLabel>Change data volume</label>
<igx-prefix>
<igx-icon>view_list</igx-icon>
</igx-prefix>
<igx-select-item *ngFor="let item of dataVolumeItems" [value]="item">
{{item}}
</igx-select-item>
</igx-select>
</div>
<div class="actionItem">
<!-- Change frequency -->
<igx-select [(ngModel)]="frequency" (ngModelChange)="paramsChanged()" [overlaySettings]="freqOverlaySettings" #freq>
<label igxLabel>Change update frequency</label>
<igx-prefix>
<igx-icon>cell_wifi</igx-icon>
</igx-prefix>
<igx-select-item *ngFor="let item of frequencyItems" [value]="item">
{{item}}
</igx-select-item>
</igx-select>
</div>
<div igxButton (click)="createGrid()" [disabled]="docLayout.floatingPanes.length >= 5">Add Floating Pane</div>
<div igxOverlayOutlet #outlet></div>
</div>
<div slot="gridStockPrices" style="height: 100%;">
<igx-grid #grid1 [data]="data | async" [isLoading]="isLoading"
[allowFiltering]="true" [filterMode]="'excelStyleFilter'" [primaryKey]="'id'"
[columnSelection]="'multiple'" [cellSelection]="'none'" [outlet]="filteringOverlayOutlet">
<igx-column [field]="'id'" [width]="'70px'" [hidden]="true" [sortable]="true"></igx-column>
<igx-column [field]="'category'" [width]="'120px'" [sortable]="true"></igx-column>
<igx-column [field]="'type'" [width]="'100px'" [sortable]="true" [filterable]="false">
</igx-column>
<igx-column [field]="'contract'" [width]="'100px'" [sortable]="true" [groupable]="true">
</igx-column>
<igx-column [field]="'price'" [width]="'130px'" dataType="number" [cellClasses]="trends"
[sortable]="true">
<ng-template igxCell let-cell="cell" #priceTemplate>
<div class="finjs-icons">
<span>{{cell.value | currency:'USD':'symbol':'1.4-4'}}</span>
<igx-icon *ngIf="trends.positive(cell.row.data)">trending_up</igx-icon>
<igx-icon *ngIf="trends.negative(cell.row.data)">trending_down</igx-icon>
</div>
</ng-template>
</igx-column>
<igx-column [field]="'change'" [width]="'120px'" dataType="number" [headerClasses]="'headerAlignSyle'"
[sortable]="true" [cellClasses]="trendsChange">
</igx-column>
<igx-column [field]="'changeP'" [width]="'110px'" dataType="percent"
[pipeArgs]="columnFormatChangeP" [sortable]="true" [cellClasses]="trendsChange">
</igx-column>
<igx-column *ngFor="let c of columns" [field]="c.field" [width]="c.width"
[sortable]="c.sortable" [filterable]="c.filterable" [dataType]="c.type"
[cellClasses]="c.cellClasses" [bodyTemplate]="c.bodyTemplate" [groupable]="c.groupable">
</igx-column>
</igx-grid>
</div>
<div slot="forexMarket" style="height: 100%;">
<igx-grid #grid2 [data]="data | async" [isLoading]="isLoading"
[allowFiltering]="true" [filterMode]="'excelStyleFilter'" [primaryKey]="'id'" [outlet]="filteringOverlayOutlet"
[columnSelection]="'multiple'" [cellSelection]="'none'">
<igx-column [field]="'id'" [width]="'70px'" [hidden]="true" [sortable]="true"></igx-column>
<igx-column [field]="'category'" [width]="'120px'" [sortable]="true" [groupable]="true"></igx-column>
<igx-column [field]="'type'" [width]="'100px'" [sortable]="true" [filterable]="false" [groupable]="true">
</igx-column>
<igx-column [field]="'contract'" [width]="'100px'" [sortable]="true" [groupable]="true">
</igx-column>
<igx-column [field]="'price'" [width]="'120px'" dataType="number" [cellClasses]="trends"
[sortable]="true">
<ng-template igxCell let-cell="cell">
<div class="finjs-icons">
<span>{{cell.value | currency:'USD':'symbol':'1.4-4'}}</span>
<igx-icon *ngIf="trends.positive(cell.row.data)">trending_up</igx-icon>
<igx-icon *ngIf="trends.negative(cell.row.data)">trending_down</igx-icon>
</div>
</ng-template>
</igx-column>
<igx-column [field]="'change'" [width]="'120px'" dataType="number" [headerClasses]="'headerAlignSyle'"
[sortable]="true" [cellClasses]="trendsChange">
</igx-column>
<igx-column [field]="'changeP'" [width]="'110px'" dataType="percent"
[pipeArgs]="columnFormatChangeP" [sortable]="true" [cellClasses]="trendsChange">
</igx-column>
<igx-column *ngFor="let c of columns" [field]="c.field" [width]="c.width"
[sortable]="c.sortable" [filterable]="c.filterable" [dataType]="c.type"
[cellClasses]="c.cellClasses" [bodyTemplate]="c.bodyTemplate" [groupable]="c.groupable">
</igx-column>
</igx-grid>
</div>
<div slot="content4" style="height: 100%;">
<igx-grid #grid3 [data]="data | async" [isLoading]="isLoading"
[allowFiltering]="true" [filterMode]="'excelStyleFilter'" [primaryKey]="'id'" [outlet]="filteringOverlayOutlet"
[columnSelection]="'multiple'" [cellSelection]="'none'">
<igx-column [field]="'id'" [width]="'70px'" [hidden]="true" [sortable]="true"></igx-column>
<igx-column [field]="'category'" [width]="'120px'" [sortable]="true" [groupable]="true"></igx-column>
<igx-column [field]="'type'" [width]="'100px'" [sortable]="true" [filterable]="false" [groupable]="true">
</igx-column>
<igx-column [field]="'contract'" [width]="'100px'" [sortable]="true" [groupable]="true">
</igx-column>
<igx-column [field]="'price'" [width]="'120px'" dataType="number" [cellClasses]="trends"
[sortable]="true">
<ng-template igxCell let-cell="cell">
<div class="finjs-icons">
<span>{{cell.value | currency:'USD':'symbol':'1.4-4'}}</span>
<igx-icon *ngIf="trends.positive(cell.row.data)">trending_up</igx-icon>
<igx-icon *ngIf="trends.negative(cell.row.data)">trending_down</igx-icon>
</div>
</ng-template>
</igx-column>
<igx-column [field]="'change'" [width]="'120px'" dataType="number" [headerClasses]="'headerAlignSyle'"
[sortable]="true" [cellClasses]="trendsChange">
</igx-column>
<igx-column [field]="'changeP'" [width]="'110px'" dataType="percent"
[pipeArgs]="columnFormatChangeP" [sortable]="true" [cellClasses]="trendsChange">
</igx-column>
<igx-column *ngFor="let c of columns" [field]="c.field" [width]="c.width"
[sortable]="c.sortable" [filterable]="c.filterable" [dataType]="c.type"
[cellClasses]="c.cellClasses" [bodyTemplate]="c.bodyTemplate" [groupable]="c.groupable">
</igx-column>
</igx-grid>
</div>
<div slot="etfStockPrices" style="height: 100%;">
<igx-grid #grid4 [data]="data | async" [isLoading]="isLoading"
[allowFiltering]="true" [filterMode]="'excelStyleFilter'" [primaryKey]="'id'" [outlet]="filteringOverlayOutlet"
[columnSelection]="'multiple'" [cellSelection]="'none'">
<igx-paginator></igx-paginator>
<igx-column [field]="'id'" [width]="'70px'" [hidden]="true" [sortable]="true"></igx-column>
<igx-column [field]="'category'" [width]="'120px'" [sortable]="true" [groupable]="true"></igx-column>
<igx-column [field]="'type'" [width]="'100px'" [sortable]="true" [filterable]="false" [groupable]="true">
</igx-column>
<igx-column [field]="'contract'" [width]="'100px'" [sortable]="true" [groupable]="true">
</igx-column>
<igx-column [field]="'price'" [width]="'120px'" dataType="number" [cellClasses]="trends"
[sortable]="true">
<ng-template igxCell let-cell="cell">
<div class="finjs-icons">
<span>{{cell.value | currency:'USD':'symbol':'1.4-4'}}</span>
<igx-icon *ngIf="trends.positive(cell.row.data)">trending_up</igx-icon>
<igx-icon *ngIf="trends.negative(cell.row.data)">trending_down</igx-icon>
</div>
</ng-template>
</igx-column>
<igx-column [field]="'change'" [width]="'120px'" dataType="number" [headerClasses]="'headerAlignSyle'"
[sortable]="true" [cellClasses]="trendsChange">
</igx-column>
<igx-column [field]="'changeP'" [width]="'110px'" dataType="percent"
[pipeArgs]="columnFormatChangeP" [sortable]="true" [cellClasses]="trendsChange">
</igx-column>
<igx-column *ngFor="let c of columns" [field]="c.field" [width]="c.width"
[sortable]="c.sortable" [filterable]="c.filterable" [dataType]="c.type" [cellClasses]="c.cellClasses"
[bodyTemplate]="c.bodyTemplate" [groupable]="c.groupable">
</igx-column>
</igx-grid>
</div>
<ng-template #host gridHost>
</ng-template>
</igc-dockmanager>
<div [class]="isDarkTheme ? 'dark-theme' : 'light-theme'" #filteringOverlayOutlet="overlay-outlet" igxOverlayOutlet></div>
html@use 'igniteui-dockmanager/dist/collection/styles/igc.themes';
@use '../../variables' as *;
.actionItem {
margin-block-end: rem(20px);
}
.finjs-icons {
display: flex;
align-items: center;
igx-icon {
font-size: rem(16px);
width: rem(16px);
height: rem(16px);
margin-inline-start: rem(4px);
}
}
.changePos,
.changeNeg,
.strongPositive,
.strongNegative {
color: contrast-color(null, 'gray', 500) !important;
.igx-grid__td-text {
padding: rem(2px) rem(5px);
}
}
.positive {
color: color(null, 'success', 500) !important;
}
.positive.strongPositive {
.igx-grid__td-text {
color: color(null, 'success', 500, .8) !important;
}
}
.negative {
color: color(null, 'error', 500) !important;
}
.negative.strongNegative {
.igx-grid__td-text {
color: color(null, 'success', 500, .8) !important;
}
}
// NORMAL
// positive
.changePos {
.igx-grid__td-text {
background: color(null, 'success', 500, .5);
}
}
.changePos1 {
background: color(null, 'success', 500, .5);
color: contrast-color(null, 'gray', 900);
}
.changePos2 {
.igx-grid__td-text {
border-inline-end: rem(4px) solid color(null, 'success', 500, .5);
padding-inline-end: rem(15px);
}
}
// negative
.changeNeg {
.igx-grid__td-text {
background: color(null, 'error', 500, .5);
}
}
.changeNeg1 {
background: color(null, 'error', 500, .5);
color: contrast-color(null, 'gray', 900);
}
.changeNeg2 {
.igx-grid__td-text {
border-inline-end: rem(4px) solid color(null, 'error', 500, .5);
padding-inline-end: rem(9px);
}
}
// STRONG
// positive
.strongPositive {
.igx-grid__td-text {
background: color(null, 'success', 500);
}
}
.strongPositive1 {
background: color(null, 'success', 500);
color: contrast-color(null, 'gray', 900);
}
.strongPositive2 {
.igx-grid__td-text {
border-inline-end: rem(4px) solid color(null, 'success', 500);
padding-inline-end: rem(15px);
}
}
// negative
.strongNegative {
.igx-grid__td-text {
background: color(null, 'error', 500);
color: contrast-color(null, 'gray', 900);
}
}
.strongNegative1 {
background: color(null, 'error', 500);
color: contrast-color(null, 'gray', 900);
}
.strongNegative2 {
.igx-grid__td-text {
border-inline-end: rem(4px) solid color(null, 'error', 500);
padding-inline-end: rem(9px);
}
}
igx-grid {
--ig-size: var(--ig-size-small);
.grid-area {
margin-block-start: 1rem;
overflow-y: hidden;
overflow-x: hidden;
width: 100%;
}
// selected
.igx-grid__td--column-selected.changePos1,
.igx-grid__td--column-selected.changePos2,
.igx-grid__td--column-selected.changePos {
background-color: color(null, 'success', 500) !important;
.finjs-icons,
.igx-grid__td-text {
color: contrast-color(null, 'gray', 900);;
}
}
.igx-grid__td--column-selected.changeNeg1,
.igx-grid__td--column-selected.changeNeg2,
.igx-grid__td--column-selected.changeNeg {
background-color: color(null, 'error', 500) !important;
.finjs-icons,
.igx-grid__td-text {
color: contrast-color(null, 'gray', 900);
}
}
// selected
.igx-grid__td--column-selected.strongPositive1,
.igx-grid__td--column-selected.strongPositive2,
.igx-grid__td--column-selected.strongPositive {
background-color: color(null, 'success', 500) !important;
.finjs-icons,
.igx-grid__td-text {
color: contrast-color(null, 'gray', 900);
}
}
.igx-grid__td--column-selected.strongNegative1,
.igx-grid__td--column-selected.strongNegative2,
.igx-grid__td--column-selected.strongNegative {
background-color: color(null, 'error', 500) !important;
.finjs-icons,
.igx-grid__td-text {
color: contrast-color(null, 'gray', 900);
}
}
}
igx-select {
--ig-size: var(--ig-size-small);
}
scss
Iniciar la conexión del concentrador
Signal-r.service maneja la conectividad y las actualizaciones de los parámetros manejables expuestos, frecuencia, volumen y alternancia de estado de actualización en vivo (Inicio/Detención).
this.hubConnection = new signalR.HubConnectionBuilder()
.configureLogging(signalR.LogLevel.Trace)
.withUrl('https://es.infragistics.com/angular-apis/webapi/streamHub')
.build();
this.hubConnection
.start()
.then(() => {
this.hasRemoteConnection = true;
this.registerSignalEvents();
this.broadcastParams(interval, volume, live, updateAll);
})
.catch(() => {});
ts
Según la frecuencia especificada, los Grids recibirán un total de 30 nuevas actualizaciones desde el servidor. Se aplican clases cellStyle específicas a las tres columnas que manejan los cambios (Precio, Cambio y Cambio en porcentaje).
Frecuencia de actualización y volumen de datos.
Al utilizar el panel de Acción a la izquierda, puede administrar la frecuencia de la alimentación de datos y el volumen de los datos solicitados. Todas las cuadrículas utilizan la misma fuente de datos. Siéntase libre de utilizar los otros elementos de acción para detener la fuente de datos, cambiar el tema de la aplicación o agregar dinámicamente un contenedor DockSlot con un igxGrid.
Usamos el método 'updateparameters' para solicitar un nuevo conjunto de datos con cierta frecuencia. Este método es parte de la implementación del centro de transmisión de SignalR.
this.hubConnection.invoke('updateparameters', frequency, volume, live, updateAll)
.then(() => console.log('requestLiveData', volume))
.catch(err => {
console.error(err);
});
ts
Cree dinámicamente componentes DockSlot y Grid
Al utilizar ComponentFactoryResolver, podemos crear componentes DockSlot y Grid sobre la marcha.
Componente DockManager
Aproveche el componente web Dock Manager y cree su propia vista web utilizando el expediente o los paneles flotantes. Para agregar un nuevo panel flotante, continúe, abra el panel de Acción a la derecha y haga clic en el botón "Agregar panel flotante". Arrastre y suelte el nuevo panel en la ubicación deseada.
Referencias de API
- Componente IgxGrid
- Estilos de componentes IgxGrid
- ComponenteColumnaIgx
- IgxGridRow
- IgxÁrbolCuadrículaFila
- IgxHierarchicalGridRow
- IgxGridCell
Recursos adicionales
- Virtualización y rendimiento
- Paginación
- Filtración
- Clasificación
- resúmenes
- Columna en movimiento
- Fijación de columnas
- Cambio de tamaño de columna
- Selección