@webacad/observable-list
TypeScript icon, indicating that this package has built-in type declarations

2.0.0 • Public • Published

NPM version Build Status

WebACAD/ObservableList

Automatic observable list for angular cdk or material table

Installation

Version >= 2.0.0 depends on angular >= 6. For older versions of angular use @webacad/observable-list@^1.0.

$ npm install --save @webacad/observable-list

or with yarn

$ yarn add @webacad/observable-list

Usage

First you need to prepare your project to be able to work with this library.

This documentation will show you all the examples on User entity and UsersRepository classes.

Update user.ts:

Each observable entity needs to have an id property.

import {ObservableEntity} from '@webacad/observable-list';

export class User implements ObservableEntity
{
    
    public id: number;
    
}

Update users-repository.service.ts:

Each observable repository needs to manage four event emitters:

  • onInserted: emit newly inserted entity
  • onUpdated: emit updated entity (emit new entity for immutable entities)
  • onRemoved: emit removed entity
  • onReplaced: emit replacement of entity (must provide previous and next versions)
import {Injectable, EventEmitter} from '@angular/core';
import {ObservableRepository, ObservableReplacedEntity} from '@webacad/observable-list';
import {Observable} from 'rxjs/Observable';
import {from as ObservableFrom} from 'rxjs/observable/from';
import {User} from './user';

@Injectable()
export class UsersRepository implements ObservableRepository<User>
{
    
    public onInserted = new EventEmitter<User>();
    
    public onUpdated = new EventEmitter<User>();
    
    public onRemoved = new EventEmitter<User>();
    
    public onReplaced = new EventEmitter<ObservableReplacedEntity<User>>();
    
    public getAll(): Observable<Array<User>>
    {
        // todo: load real users
        return ObservableFrom([
            new User(1),
            new User(2),
        ]);
    }
    
    public insert(user: User): void
    {
        // todo: store new user 
        this.onInserted.emit(user);
    }
    
    public update(user: User): void
    {
        // todo: save updated user
        this.onUpdated.emit(user);
    }
    
    public remove(user: User): void
    {
        // todo: remove user
        this.onRemoved.emit(user);
    }
    
}

Now you only need to create the data source for your table with users.

import {Component, OnInit} from '@angular/core';
import {ObservableDataSource, createObservableDataSource} from '@webacad/observable-list';
import {UsersRepository} from '../users-repository.service';
import {User} from '../user';

@Component({
    selector: 'add-users-table',
    templateUrl: './users-table.component.html',
})
export class UsersTable
{

    public dataSource: ObservableDataSource<User>;
    
    constructor(
        private users: UsersRepository,
    ) {
        this.createDataSource();
    }
    
    private createDataSource(): void
    {
        this.dataSource = createObservableDataSource(this.users, this.users.getAll());
    }
    
}

The createObservableDataSource function returns DataSource from @angular/cdk with all necessary configuration.

The first argument must be an ObservableRepository and the second Observable<Array<any>> with your data.

Now just use your new data source with either cdk-table or mat-table:

<cdk-table [dataSource]="dataSource">...</cdk-table>

or

<mat-table [dataSource]="dataSource">...</mat-table>

Filter newly inserted entities

By default all newly created entities will appear in the data source. That behavior can be changed by providing the options for createObservableDataSource.

createObservableDataSource(this.users, this.users.getAll(), {
    shouldIncludeNewEntity: (user: User) => {
        return user.id === 5;
    },
});

Now only users with ID 5 will be appended.

Track by - performance improvements

Read more about trackBy in angular documentation.

This library provides you with default trackBy function which is using the ID's of your entities. If you wish to change it to something else, use the options for createObservableDataSource again.

createObservableDataSource(this.users, this.users.getAll(), {
    trackBy: (i: number, user: User) => {
        return user.uuid;
    },
});

You must set the [trackBy] in your template, otherwise angular will use it's own default implementation:

<cdk-table [dataSource]="dataSource" [trackBy]="dataSource.trackBy">...</cdk-table>

or

<mat-table [dataSource]="dataSource" [trackBy]="dataSource.trackBy">...</mat-table>

Immediate refresh without waiting for the backend

Imagine that you have a "add form" for your user and you want to show the new user row immediately after clicking on the save button.

You could use the onInserted event alone, but after the "real" data is loaded from your API, you'll have no way of replacing the previous entity.

Instead you can use the combination of onInserted and onReplaced.

import {HttpClient} from '@angular/common/http';
import {map, tap} from 'rxjs/operators';

@Injectable()
export class UsersRepository implements ObservableRepository<User>
{
    
    // ...
    
    constructor(
        private http: HttpClient,
    ) {}
    
    public insert(user: User): Observable<User>
    {
        this.onInserted.emit(user);
        
        return this.http.post('/users', {
            email: user.email,
            password: user.password,
        }).pipe(
            map((data) => this.mapDataToEntity(data)),
            tap((newUser) => this.onReplaced.emit({
                previous: user,
                next: newUser,
            })),
        );
    }
    
    // ...
    
}

Just keep in mind, that the first version of user will probably not have any ID available.

Reload all data

If you wish to refresh all data inside of your table, you can simply reuse the existing data source with the new data.

dataSource.reload(users.getAll());

The reload method accepts a new Observable<Array<any>> type.

Modify current data

dataSource.modify((items: Observable<User>) => {
    return items.pipe(
        map((user) => user.update()),
    );
});

Listen to changes

There are multiple events available on the data source class which could be used for listening to changes in data.

  • onInserted<EventEmitter<T>>: emitted when new item is added to data
  • onUpdated<EventEmitter<T>>: emitted when some item was updated by ID
  • onRemoved<EventEmitter<T>>: emitted when an item was removed from data
  • onReplaced<EventEmitter<OnReplacedArg<T>>>: emitted when an item was replaced

Dependencies (0)

    Dev Dependencies (9)

    Package Sidebar

    Install

    npm i @webacad/observable-list

    Weekly Downloads

    0

    Version

    2.0.0

    License

    MIT

    Unpacked Size

    32.3 kB

    Total Files

    21

    Last publish

    Collaborators

    • davidkcz