Angular RxJS library

Overview

The following guide shows how to create an Angular application using RxJS library and our DataGrid component.

angular datagrid rxjs observable

The Angular application that we are going to create will use Smart Angular GridComponent, The purpose of the application is to demonstrate how to use RxJS with Smart.Grid.

The RxJS library

RxJS is a library for composing asynchronous and event-based programs by using observable sequences. It provides one core type, the Observable, satellite types (Observer, Schedulers, Subjects) and operators inspired by Array#extras (map, filter, reduce, every, etc) to allow handling asynchronous events as collections. RxJS provides an implementation of the Observable type.

Project Configuration

  1. Create a new Angular project with the following command:
    ng new smart-project
    
  2. Add Smart UI for Angular to your project
    ng add smart-webcomponents-angular
    
  3. Edit src/app/app.module.ts
    import { NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { GridModule } from 'smart-webcomponents-angular/grid';
    import { AppComponent } from './app.component';
    import { FormsModule } from '@angular/forms';
    import { HttpClientModule } from '@angular/common/http';
    
    @NgModule({
        declarations: [ AppComponent ],
        imports: [ BrowserModule, FormsModule, GridModule, HttpClientModule ],
        bootstrap: [ AppComponent ]
    })
    
    export class AppModule { }
    
  4. Edit src/app/app.component.html

    It defines the Grid and its properties.
    <smart-grid #grid id="grid" [appearance]="appearance" [behavior]="behavior" [dataSource]="dataSource" [columns]="columns"></smart-grid>
    
  5. Edit src/app/app.component.css

    In this file, we define the styles of the DataGrid and its columns.
    /* This is the CSS used in the demo */
    smart-grid {
        width: 100%;
    }
    
    .change-container,
    .value-container {
        display: block;
    }
    
    .change-container {
        display: flex;
        margin-right: 5px;
    
    }
    
    .change-container.negative {
        color: rgb(229, 57, 53);
    }
    
    .change-container.positive {
        color: rgb(67, 160, 71);
    }
    
    .change-container::before {
        content: var(--smart-icon-up);
        width: 100%;
        height: 100%;
        font-family: var(--smart-font-family-icon);
        font-style: normal;
        font-weight: normal;
        font-size: var(--smart-arrow-size);
        color: inherit;
        text-decoration: inherit;
        font-variant: normal;
        text-transform: none;
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        justify-content: center;
        position: relative;
        margin-right: 5px;
    }
    
    .change-container.negative::before {
        transform: rotate(180deg);
    }
    
    .value-container {
        padding: 3px;
        background-color: rgb(30, 102, 90);
        color: white;
    }
    
    .smart-grid-cell-template {
        padding-right: 5px;
    }
    
    .smart-grid-cell-template[column="price"] {
        display: flex;
        align-items: center;
        height: 100%;
        line-height: 1;
        margin-left: 10px;
    }
    
  6. Create a new file src/app/data.ts
    
    interface IRowGenerateData {
    	id: number;
    	reportsTo: number | null;
    	available: boolean | null;
    	firstName: string;
    	lastName: string;
    	name: string;
    	productName: string;
    	quantity: string | number;
    	total: string | number;
    	price: string | number;
    	date: Date;
    	leaf: boolean;
    }
    
    export function GetData(rowscount?: number, last?: number, hasNullValues?: boolean): IRowGenerateData[] {
    	const data: IRowGenerateData[] = new Array();
    
    	if (rowscount === undefined) {
    		rowscount = 100;
    	}
    
    	let startIndex = 0;
    
    	if (last) {
    		startIndex = rowscount;
    		rowscount = last - rowscount;
    	}
    
    	const firstNames =
    		[
    			'Andrew', 'Nancy', 'Shelley', 'Regina', 'Yoshi', 'Antoni', 'Mayumi', 'Ian', 'Peter', 'Lars', 'Petra', 'Martin', 'Sven', 'Elio', 'Beate', 'Cheryl', 'Michael', 'Guylene'
    		];
    
    	const lastNames =
    		[
    			'Fuller', 'Davolio', 'Burke', 'Murphy', 'Nagase', 'Saavedra', 'Ohno', 'Devling', 'Wilson', 'Peterson', 'Winkler', 'Bein', 'Petersen', 'Rossi', 'Vileid', 'Saylor', 'Bjorn', 'Nodier'
    		];
    
    	const productNames =
    		[
    			'Black Tea', 'Green Tea', 'Caffe Espresso', 'Doubleshot Espresso', 'Caffe Latte', 'White Chocolate Mocha', 'Caramel Latte', 'Caffe Americano', 'Cappuccino', 'Espresso Truffle', 'Espresso con Panna', 'Peppermint Mocha Twist'
    		];
    
    	const priceValues =
    		[
    			'2.25', '1.5', '3.0', '3.3', '4.5', '3.6', '3.8', '2.5', '5.0', '1.75', '3.25', '4.0'
    		];
    
    	for (let i = 0; i < rowscount; i++) {
    		const row = {} as IRowGenerateData;
    
    		const productindex = Math.floor(Math.random() * productNames.length);
    		const price = parseFloat(priceValues[productindex]);
    		const quantity = 1 + Math.round(Math.random() * 10);
    
    		row.id = startIndex + i;
    		row.reportsTo = Math.floor(Math.random() * firstNames.length);
    
    		if (i % Math.floor(Math.random() * firstNames.length) === 0) {
    			row.reportsTo = null;
    		}
    
    		row.available = productindex % 2 === 0;
    
    		if (hasNullValues === true) {
    			if (productindex % 2 !== 0) {
    				const random = Math.floor(Math.random() * rowscount);
    				row.available = i % random === 0 ? null : false;
    			}
    		}
    
    		row.firstName = firstNames[Math.floor(Math.random() * firstNames.length)];
    		row.lastName = lastNames[Math.floor(Math.random() * lastNames.length)];
    		row.name = row.firstName + ' ' + row.lastName;
    		row.productName = productNames[productindex];
    		row.price = price;
    		row.quantity = quantity;
    		row.total = price * quantity;
    
    		const date = new Date();
    		date.setFullYear(2016, Math.floor(Math.random() * 11), Math.floor(Math.random() * 27));
    		date.setHours(0, 0, 0, 0);
    		row.date = date;
    
    		data[i] = row;
    	}
    
    	return data;
    }
    
  7. Edit src/app/app.component.ts

    In the code we load the initial data via a Observable subscription. The data updates are through another Observable subscription.
    import { Component, ViewChild, AfterViewInit, ViewEncapsulation } from '@angular/core';
    import { GridComponent, GridColumn, DataAdapter, Smart } from 'smart-webcomponents-angular/grid';
    import { GetData } from './data'
    import { Observable, from  } from 'rxjs';
    
    @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['app.component.css'],
        encapsulation: ViewEncapsulation.None
    })
    
    export class AppComponent implements AfterViewInit {	
    	@ViewChild("grid", { read: GridComponent, static: false }) grid!: GridComponent;
    
        // setup Datagrid's price column template.
        changeTemplate(formatObject: any) {
            const value: number = formatObject.value, opacity: number = Math.abs(value) / 100;
            if (value < 0) {
                formatObject.cell.background = `rgba(229, 57, 53, ${opacity})`;
            }
            else if (value > 0) {
                formatObject.cell.background = `rgba(67, 160, 71, ${opacity})`;
            }
            formatObject.template = value + '%';
        }
    	
    	// setup Datagrid appearance.
    	appearance = {
            alternationCount: 2,
            showColumnLines: false,
            showColumnHeaderLines: false
        };
    	
    	// setup Datagrid columns resize.
        behavior = { columnResizeMode: 'growAndShrink' };
      
        // setup Datagrid data source.  
        dataSource = new window.Smart.DataAdapter({
            dataSource: [],
            dataFields: [
                'commodity: string',
                'price: number',
                'priceChange: number',
                'day: number',
                'week: number',
                'month: number',
                'year: number',
                'date: date'
            ]
        });
    	
    	// setup Datagrid columns.
        columns = [
            { label: 'Commodity', dataField: 'commodity' },
            {
                label: 'Price', dataField: 'price', align: 'left', cellsAlign: 'left', template: function (formatObject: any) {
                    const value: number = formatObject.value.toFixed(2);
    				const priceChange: number = (this as any).grid.dataSource[formatObject.row.index].priceChange;
                  
      			    if (priceChange === 0) {
                        formatObject.template = value;
                    }
                    else {
                        formatObject.template = `
    ${priceChange}
    ${value}
    `; } } }, { label: 'Weekly', dataField: 'week', template: this.changeTemplate }, { label: 'Monthly', dataField: 'month', template: this.changeTemplate }, { label: 'Yearly', dataField: 'year', template: this.changeTemplate } ]; ngAfterViewInit(): void { const that = this; // init demo server. const demoServer = this.setupDemoServer(), initServer = demoServer.init(), serverData = demoServer.getData(); initServer.subscribe(function(dataRows) { that.dataSource = new window.Smart.DataAdapter({ dataSource: dataRows.slice(0), dataFields: [ 'commodity: string', 'price: number', 'priceChange: number', 'day: number', 'week: number', 'month: number', 'year: number', 'date: date' ] }); serverData.subscribe(function(rows) { // suspend updates. that.grid.beginUpdate(); // update cell values. for(let i = 0; i < that.grid.rows.length; i++) { const row = that.grid.rows[i]; const priceCell = row.getCell('price'); const priceCellChange = row.getCell('priceChange'); row.data.priceChange = rows[i].priceChange; row.data.price = rows[i].price; } // resume updates. that.grid.endUpdate(); }); }); } // setup demo server. setupDemoServer() { function demoServer() { 'use strict'; this.dataRows = []; } // init server and fetch data from url. demoServer.prototype.init = function() { return from( new Promise((resolve, reject) => { fetch('https://raw.githubusercontent.com/HTMLElements/smart-webcomponents-angular/master/data.json') .then(response => response.json()) .then(data => { let dataSet = data; this.dataRows = dataSet.map((value) => { const newData = Object.assign(value); newData.priceChange = 0; return newData; }); resolve(this.dataRows); }); }) ); }; // get data and update it every 1s using Observable. demoServer.prototype.getData = function() { const that = Object(this); // dynamic updates. return Observable.create(function(observer) { const interval = setInterval(() =>{ for (let i = 0; i < that.dataRows.length; i++) { const index = Math.floor(that.dataRows.length * Math.random()); const currentdataRows = that.dataRows[index]; let value = currentdataRows.price; if (Math.random() > 3 / 5) { value = value + 1e-10; currentdataRows.priceChange = 0; continue; } let valueChange = (Math.random() > 0.5 ? 1 : -1) * value * ((Math.random() * 15 + 1) / 100); valueChange = parseFloat(valueChange.toFixed(2)); value = Math.max(1, parseFloat((value + valueChange).toFixed(2))); currentdataRows.price = value; currentdataRows.priceChange = valueChange; } observer.next(that.dataRows); }, 1000); return function() { window.clearInterval(interval); }; }); }; return new demoServer(); } }



Angular Datagrid example