npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@webacad/observable-list

v2.0.0

Published

Automatic observable list for angular cdk table

Downloads

6

Readme

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