liveBook · Manning

Data source

As explained in Essential TypeScript 5, Third Edition

A data source is a component or value from which an application obtains the data it displays or processes.

A data source gives a web application one interface for obtaining products while allowing the products to come from local test data or a web service. In a stand-alone TypeScript application, AbstractDataSource holds products, categories, and an order. Its constructor starts loading; getProducts and getCategories wait for that work before returning results. section 16.5.1

Listing 16.16 The contents of the abstractdatasource.ts file in the src/data folder
import { Product, Order } from "./entities";

export type ProductProp = keyof Product;

export abstract class AbstractDataSource {
   private _products: Product[];
   private _categories: Set<string>;
   public order: Order;    
   public loading: Promise<void>;

   constructor() {
       this._products = [];
       this._categories = new Set<string>();
       this.order = new Order();
       this.loading = this.getData();
   }

   async getProducts(sortProp: ProductProp = "id", 
           category? : string): Promise<Product[]> {
       await this.loading;
       return this.selectProducts(this._products, sortProp, category);
   }

   protected async getData(): Promise<void> {
       this._products = [];
       this._categories.clear();            
       const rawData = await this.loadProducts();
       rawData.forEach(p => {
           this._products.push(p);
           this._categories.add(p.category);
       });
   }

   protected selectProducts(prods: Product[], 
           sortProp: ProductProp, category?: string): Product[] {
       return prods.filter(p=> category === undefined 
                   || p.category === category)
               .sort((p1, p2) => p1[sortProp] < p2[sortProp] 
                   ? -1 : p1[sortProp] > p2[sortProp] ? 1: 0);
   }

   async getCategories(): Promise<string[]> {
       await this.loading;
       return [...this._categories.values()];
   }

   protected abstract loadProducts(): Promise<Product[]>;
   abstract storeOrder(): Promise<number>;
}

The src/data/abstractDataSource.ts listing shows the shared state, the loading Promise, and the asynchronous product query. section 16.5.1

LocalDataSource implements loadProducts with Promise.resolve and a small product array. RemoteDataSource implements the same operation with an Axios GET request; it also sends order details to the service with POST. Starting locally lets the application take shape before HTTP requests are added. section 16.5.1 section 17.2

Using the source in a display

In src/index.ts, create a LocalDataSource and await getProducts("name"), getCategories(), or getProducts("name", "Chess"). For the browser display, pass that same instance as HtmlDisplay’s dataSource prop. HtmlDisplay queries products using its selected category and replaces its rendered HTML when a callback changes the selection. This is a simple update strategy, not an attempt to minimize browser work. section 16.5.1 section 16.8.2

An Angular version separates DataSource from an abstract DataSourceImpl. Angular injects the configured implementation into DataSource. Here, loadProducts returns an RxJS Observable<Product[]> rather than a Promise; subscribing receives product arrays as they arrive, and Angular handles display updates. section 18.3.1

Listing 18.10 The contents of the dataSource.ts file in the src/app/data folder
import { Observable } from "rxjs";
import { Injectable } from '@angular/core';
import { Product, Order } from "./entities";

export type ProductProp = keyof Product;

export abstract class DataSourceImpl {
   abstract loadProducts(): Observable<Product[]>;
   abstract storeOrder(order: Order): Observable<number>;    
}

@Injectable()
export class DataSource {
   private _products: Product[];
   private _categories: Set<string>;
   public order: Order;

   constructor(private impl: DataSourceImpl) {
       this._products = [];
       this._categories = new Set<string>();
       this.order = new Order();
       this.getData();
   }

   getProducts(sortProp: ProductProp = "id", category? : string) 
           : Product[] {
       return this.selectProducts(this._products, sortProp, category);
   }

   protected getData(): void {
       this._products = [];
       this._categories.clear();            
       this.impl.loadProducts().subscribe(rawData => {
           rawData.forEach(p => {
               this._products.push(p);
               this._categories.add(p.category);
           });
       });  
   }

   protected selectProducts(prods: Product[], sortProp: ProductProp, 
           category?: string): Product[] {
       return prods
            .filter(p => category === undefined || p.category === category)
               .sort((p1, p2) => p1[sortProp] < p2[sortProp] 
                   ? -1 : p1[sortProp] > p2[sortProp] ? 1: 0);
   }

   getCategories(): string[] {
       return [...this._categories.values()];
   }

   storeOrder(): Observable<number> {
       return this.impl.storeOrder(this.order);
   }
}

The src/app/data/dataSource.ts listing shows the injectable Angular class, its abstract implementation dependency, and its Observable-based loading contract. section 18.3.1

How Essential TypeScript 5, Third Edition covers it

How deep it goes
Works through shared interfaces, asynchronous loading, display updates, and an Angular alternative; leaves Observable’s more complex sequence features unexplored.
Examples
Uses local product records, category-filtered HTML, Axios GET and POST requests, and an injectable Angular data source.
What it assumes
Assumes familiarity with TypeScript classes and JavaScript asynchronous operations; introduces Angular injection and Observable use in context.
What you can do after
Build it from scratch with interchangeable local and remote loaders, or apply it via Angular dependency injection.
How it connects
Connects data sources to Promises, async/await, HTTP requests, dependency injection, and RxJS Observables.

Also explained in

Compare how 13 books explain data source →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Essential TypeScript 5, Third Edition. The section links point to the parts of the book it draws on.