3xGuy 3xGuy - 3 years ago 178
TypeScript Question

Typscript fire event every time an array changes

I am trying to observe an array in typescript 2.3.3, and I am using rxjs. my goal is to run a method every time an array is changed. Let me clarify with some code before I can tell you what I've tried.

rows: any[] = []
rows.push('test1') //fired event now
rows.splice(0,1) //fire event now
rows = [] // fire event now


Basically, if this property ever changes then I would like to have an event called.

I have researched Rx.Observable and I came up with a couple different things.


  1. Rx.Observable.from(this.rows)
    and then subscribe to it, however the subscription is never fired.

  2. Rx.Observable.of(this.rows)
    and then subscribe to it, this fires the subscription only 1 time.

    i think that
    Rx.Observable
    is the way to go here, but i'm not sure how to get this to fire event every time.
    thank you for your advice


Answer Source

I ended up creating an ObservableArray class that will fire events and has a subscribe method in it. I will post this here just so that people having this issue will be able to use it too.

export class ObservableArray {
onArrayChanges: EventEmitter<any> = new EventEmitter();
onRemovedItem: EventEmitter<any> = new EventEmitter();
onAddedItem: EventEmitter<any> = new EventEmitter();

onComponentChanges: EventEmitter<any> = new EventEmitter();

length(): number {
    return this.collection.length;
}

private collectionCount: number = 0;
private isStartup:boolean=true;
constructor(public array: any[]) {
    this.onComponentChanges.subscribe(data => {
        this.array = data;
        this.onChanges();
    });
}

private collection: any[] = [];
private subscriptions: any[] = [];

onChanges(): void {
    let collectionModel = new CollectionModel();
    collectionModel.originalValue = this.collection;
    collectionModel.newValue = (this.isStartup)?this.collection:this.array;
    collectionModel.isRecordAdded = this.isAddedItem();
    collectionModel.isRecordRemoved = this.isRemovedItem();

    this.collectionCount = this.collection.length;

    if (this.isAddedItem()) {
        this.onAddedItem.emit(collectionModel);
    }

    if (this.isRemovedItem()) {
        this.onRemovedItem.emit(collectionModel);
    }

    if (this.isChanged()) {
        this.updateCollection();
        this.onArrayChanges.emit(collectionModel);
        this.publish(collectionModel);
        this.isStartup = false;
    }
}
private isChanged(): boolean {
    let isChanged = false;
    if (this.array) {
        isChanged = this.array.length !== this.collectionCount;
    }
    return isChanged || this.isStartup;
}
private isAddedItem(): boolean {
    let isAddedItem = false;
    if (this.array) {
        isAddedItem =this.array.length > this.collectionCount;
    }
    return isAddedItem;
}
private isRemovedItem(): boolean {
    let isRemoved = false;
    if (this.array) {
        isRemoved = this.array.length < this.collectionCount;
    }
    return isRemoved;
}

private updateCollection() {
    this.collection = this.array;
}

    private publish(payload?: any) {
        for (let callback of this.subscriptions) {
            callback(payload);
        }
    }

    subscribe(callback: (payload?: any) => void) {
        this.subscriptions.push(callback);
    }
}

export class CollectionModel {
    originalValue: any[] = [];
    newValue: any[] = [];
    isRecordAdded: boolean = false;
    isRecordRemoved: boolean = false;
}

If you really wanted you could make this and ObservableArray<T> however in my case i didn't think that i needed to do this.

I hope this helps others. :)

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download