@ortoo/angular-resource
Version:
Magical CRUD resource management for angular
119 lines (98 loc) • 3.59 kB
text/typescript
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import {EventEmitter, Handler, Subscription} from './emitter';
import {ArrayEmitter as IArrayEmitter} from './types';
const ArrayProto = Array.prototype;
export class ArrayEmitter<T> extends Array<T> implements IArrayEmitter<T> {
public $emitter: EventEmitter;
public $subject: BehaviorSubject<ArrayEmitter<T>>;
public on: {(name: string, handler: Handler): void};
public off: {(name: string, handler: Handler): void};
public listen: {(name: string, handler: Handler): Subscription};
public once: {(name: string, handler: Handler): void};
public addListener: {(name: string, handler: Handler): void};
public removeListener: {(name: string, handler: Handler): void};
constructor(...args: any[]) {
super(...args);
let arr = this;
// Non enumerable so that we can send the array over postMessage
Object.defineProperty(arr, '$emitter', {
enumerable: false,
value: new EventEmitter(),
});
Object.defineProperty(arr, '$subject', {
enumerable: false,
value: new BehaviorSubject(this),
});
this.$emitter.on('update', () => {
this.$subject.next(this);
});
// Generate our Array Emitter methods
let ArrayEmitterMethods: any = {};
['on', 'off', 'listen', 'once', 'addListener', 'removeListener'].forEach(key => {
ArrayEmitterMethods[key] = {
enumerable: false,
value: (...methodArgs: any[]) => {
return this.$emitter[key](...methodArgs);
},
};
});
// These methods create new arrays
['reverse', 'sort', 'splice'].forEach(key => {
ArrayEmitterMethods[key] = {
enumerable: false,
value: (...methodArgs: any[]) => {
let emitter = this.$emitter;
emitter.on('update', () => {
ArrayProto[key].apply(this, methodArgs);
});
ArrayProto[key].apply(this, methodArgs);
return this;
},
};
});
['filter', 'map', 'slice'].forEach(key => {
ArrayEmitterMethods[key] = {
enumerable: false,
value: (...methodArgs: any[]) => {
let newArr: any = new ArrayEmitter();
let emitter = this.$emitter;
emitter.on('update', () => {
updateNewArr(ArrayProto[key].apply(this, methodArgs));
newArr.$emitter.emit('update', newArr);
});
updateNewArr(ArrayProto[key].apply(this, methodArgs));
function updateNewArr(updatedArr: any[]) {
newArr.length = 0;
for (let i = 0; i < updatedArr.length; i++) { // tslint:disable-line prefer-for-of
newArr.push(updatedArr[i]);
}
}
return newArr;
},
};
});
ArrayEmitterMethods.concat = {
enumerable: false,
value: (...methodArgs: any[]) => {
let newArr = new ArrayEmitter();
let allArrs = [this, ...methodArgs];
let qries = allArrs.filter((obj: any) => (obj.$emitter && Array.isArray(obj)));
qries.forEach((qry: ArrayEmitter<any>) => {
let emitter = qry.$emitter;
emitter.on('update', () => {
updateConcat();
newArr.$emitter.emit('update', newArr);
});
});
updateConcat();
return newArr;
function updateConcat() {
let concatted = ArrayProto.concat.call(this, ...methodArgs);
newArr.length = 0;
newArr.push(...concatted);
}
},
};
Object.defineProperties(arr, ArrayEmitterMethods);
}
}