/**
* Usage
*
* const model = {
* username: new Observable<string>('foo'),
* age: new Observable<number>(45)
* }
*
* const disposeUsernameObserver = model.username.addObserver((next, prev) => console.log(prev, next))
* model.username.value = 'antoher one'
*
* // later
* disposeUsernameObserver()
*
*/
interface Observer<T> {
(nextValue: T, prevValue: T): void
}
class Observable<T> {
private observers: Observer<T>[] = []
constructor(private _value: T) { }
get value(): T {
return this._value
}
set value(nextValue: T) {
if (nextValue !== this._value) {
this.notifyObservers(nextValue, this.value)
this._value = nextValue
}
}
public addObserver(observer: Observer<T>): Function {
this.observers = [...this.observers, observer]
return () => this.removeObserver(observer)
}
public removeObserver(observer: Observer<T>): Observable<T> {
this.observers = this.observers.filter(obs => obs !== observer)
return this
}
private notifyObservers(nextValue, prevValue) {
this.observers.forEach(observer => observer(nextValue, prevValue))
}
}