diff --git a/src/api/observe.ts b/src/api/observe.ts index 7efe06444c..4cb0bf41b8 100644 --- a/src/api/observe.ts +++ b/src/api/observe.ts @@ -3,16 +3,16 @@ import {ObservableMap, IMapChange} from "../types/observablemap"; import {IObjectChange} from "../types/observableobject"; import {IComputedValue} from "../core/computedvalue"; -import {IObservableValue} from "../types/observablevalue"; +import {IObservableValue, IValueDidChange} from "../types/observablevalue"; import {Lambda} from "../utils/utils"; import {getAdministration} from "../types/type-utils"; -export function observe(value: IObservableValue | IComputedValue, listener: (newValue: T, oldValue: T | undefined) => void, fireImmediately?: boolean): Lambda; +export function observe(value: IObservableValue | IComputedValue, listener: (change: IValueDidChange) => void, fireImmediately?: boolean): Lambda; export function observe(observableArray: IObservableArray, listener: (change: IArrayChange | IArraySplice) => void, fireImmediately?: boolean): Lambda; export function observe(observableMap: ObservableMap, listener: (change: IMapChange) => void, fireImmediately?: boolean): Lambda; -export function observe(observableMap: ObservableMap, property: string, listener: (newValue: T, oldValue: T | undefined) => void, fireImmediately?: boolean): Lambda; +export function observe(observableMap: ObservableMap, property: string, listener: (change: IValueDidChange) => void, fireImmediately?: boolean): Lambda; export function observe(object: Object, listener: (change: IObjectChange) => void, fireImmediately?: boolean): Lambda; -export function observe(object: Object, property: string, listener: (newValue: any, oldValue: any | undefined) => void, fireImmediately?: boolean): Lambda; +export function observe(object: Object, property: string, listener: (change: IValueDidChange) => void, fireImmediately?: boolean): Lambda; export function observe(thing, propOrCb?, cbOrFire?, fireImmediately?): Lambda { if (typeof cbOrFire === "function") return observeObservableProperty(thing, propOrCb, cbOrFire, fireImmediately); diff --git a/src/core/computedvalue.ts b/src/core/computedvalue.ts index 316cd475d2..4ec46a1a47 100644 --- a/src/core/computedvalue.ts +++ b/src/core/computedvalue.ts @@ -5,11 +5,12 @@ import {allowStateChangesStart, allowStateChangesEnd, createAction} from "./acti import {createInstanceofPredicate, getNextId, valueDidChange, invariant, Lambda, unique, joinStrings} from "../utils/utils"; import {isSpyEnabled, spyReport} from "../core/spy"; import {autorun} from "../api/autorun"; +import {IValueDidChange} from "../types/observablevalue"; export interface IComputedValue { get(): T; set(value: T): void; - observe(listener: (newValue: T, oldValue: T) => void, fireImmediately?: boolean): Lambda; + observe(listener: (change: IValueDidChange) => void, fireImmediately?: boolean): Lambda; } /** @@ -157,14 +158,19 @@ export class ComputedValue implements IObservable, IComputedValue, IDeriva return valueDidChange(this.compareStructural, newValue, oldValue); } - observe(listener: (newValue: T, oldValue: T | undefined) => void, fireImmediately?: boolean): Lambda { + observe(listener: (change: IValueDidChange) => void, fireImmediately?: boolean): Lambda { let firstTime = true; let prevValue: T | undefined = undefined; return autorun(() => { let newValue = this.get(); if (!firstTime || fireImmediately) { const prevU = untrackedStart(); - listener(newValue, prevValue); + listener({ + type: "update", + object: this, + newValue, + oldValue: prevValue + }); untrackedEnd(prevU); } firstTime = false; diff --git a/src/types/listen-utils.ts b/src/types/listen-utils.ts index 05ae178622..ba0025eae3 100644 --- a/src/types/listen-utils.ts +++ b/src/types/listen-utils.ts @@ -20,19 +20,14 @@ export function registerListener(listenable: IListenable, handler: Function): }); } -export function notifyListeners(listenable: IListenable, change: T | T[]) { +export function notifyListeners(listenable: IListenable, change: T) { const prevU = untrackedStart(); let listeners = listenable.changeListeners; if (!listeners) return; listeners = listeners.slice(); for (let i = 0, l = listeners.length; i < l; i++) { - if (Array.isArray(change)) { - listeners[i].apply(null, change); - } - else { - listeners[i](change); - } + listeners[i](change); } untrackedEnd(prevU); } diff --git a/src/types/observablevalue.ts b/src/types/observablevalue.ts index b44ee5786a..97c90dbf59 100644 --- a/src/types/observablevalue.ts +++ b/src/types/observablevalue.ts @@ -12,6 +12,10 @@ export interface IValueWillChange { newValue: T; } +export interface IValueDidChange extends IValueWillChange { + oldValue: T | undefined; +} + export type IUNCHANGED = {}; export const UNCHANGED: IUNCHANGED = {}; @@ -20,7 +24,7 @@ export interface IObservableValue { get(): T; set(value: T): void; intercept(handler: IInterceptor>): Lambda; - observe(listener: (newValue: T, oldValue: T) => void, fireImmediately?: boolean): Lambda; + observe(listener: (change: IValueDidChange) => void, fireImmediately?: boolean): Lambda; } export class ObservableValue extends BaseAtom implements IObservableValue, IInterceptable>, IListenable { @@ -76,8 +80,14 @@ export class ObservableValue extends BaseAtom implements IObservableValue, const oldValue = this.value; this.value = newValue; this.reportChanged(); - if (hasListeners(this)) - notifyListeners(this, [newValue, oldValue]); // in 3.0, use an object instead! + if (hasListeners(this)) { + notifyListeners(this, { + type: "update", + object: this, + newValue, + oldValue + }); + } } public get(): T { @@ -89,9 +99,14 @@ export class ObservableValue extends BaseAtom implements IObservableValue, return registerInterceptor(this, handler); } - public observe(listener: (newValue: T, oldValue: T | undefined) => void, fireImmediately?: boolean): Lambda { + public observe(listener: (change: IValueDidChange) => void, fireImmediately?: boolean): Lambda { if (fireImmediately) - listener(this.value, undefined); + listener({ + object: this, + type: "update", + newValue: this.value, + oldValue: undefined + }); return registerListener(this, listener); } diff --git a/test/babel/babel-tests.js b/test/babel/babel-tests.js index 980134a620..490db8726b 100644 --- a/test/babel/babel-tests.js +++ b/test/babel/babel-tests.js @@ -94,8 +94,8 @@ test('decorators', function(t) { var events = []; var d1 = observe(o, (ev) => events.push(ev.name, ev.oldValue)); - var d2 = observe(o, 'price', (newValue, oldValue) => events.push(newValue, oldValue)); - var d3 = observe(o, 'total', (newValue, oldValue) => events.push(newValue, oldValue)); + var d2 = observe(o, 'price', (ev) => events.push(ev.newValue, ev.oldValue)); + var d3 = observe(o, 'total', (ev) => events.push(ev.newValue, ev.oldValue)); o.price = 4; diff --git a/test/makereactive.js b/test/makereactive.js index f6b3e27597..e5acb6a8af 100644 --- a/test/makereactive.js +++ b/test/makereactive.js @@ -8,8 +8,8 @@ var voidObserver = function(){}; function buffer() { var b = []; - var res = function(newValue) { - b.push(newValue); + var res = function(x) { + b.push(x); }; res.toArray = function() { return b; @@ -161,7 +161,7 @@ test('observable4', function(t) { var b = buffer(); m.observe(m.computed(function() { return x.map(function(d) { return d.x }); - }), b, true); + }), x => b(x.newValue), true); x[0].x = 3; x.shift(); @@ -177,7 +177,7 @@ test('observable4', function(t) { var b2 = buffer(); m.observe(m.computed(function() { return x2.map(function(d) { return d.x }); - }), b2, true); + }), x => b2(x.newValue), true); x2[0].x = 3; x2.shift(); diff --git a/test/observables.js b/test/observables.js index 4ed64de37e..5aaf976a14 100644 --- a/test/observables.js +++ b/test/observables.js @@ -11,8 +11,8 @@ var voidObserver = function(){}; function buffer() { var b = []; - var res = function(newValue) { - b.push(newValue); + var res = function(x) { + b.push(x.newValue); }; res.toArray = function() { return b; @@ -746,7 +746,7 @@ test('nested observable2', function(t) { }); var b = []; - var sub = m.observe(total, function(x) { b.push(x); }, true); + var sub = m.observe(total, function(x) { b.push(x.newValue); }, true); price.set(150); factor.set(7); // triggers innerCalc twice, because changing the outcome triggers the outer calculation which recreates the inner calculation @@ -777,7 +777,7 @@ test('expr', function(t) { }); var b = []; - var sub = m.observe(total, function(x) { b.push(x); }, true); + var sub = m.observe(total, function(x) { b.push(x.newValue); }, true); price.set(150); factor.set(7); // triggers innerCalc twice, because changing the outcome triggers the outer calculation which recreates the inner calculation @@ -870,7 +870,7 @@ test('expr2', function(t) { }); var b = []; - var sub = m.observe(total, function(x) { b.push(x); }, true); + var sub = m.observe(total, function(x) { b.push(x.newValue); }, true); price.set(150); factor.set(7); // triggers innerCalc twice, because changing the outcome triggers the outer calculation which recreates the inner calculation diff --git a/test/observe.js b/test/observe.js index 73e363dfeb..5cbfde127f 100644 --- a/test/observe.js +++ b/test/observe.js @@ -4,20 +4,20 @@ var m = require('..'); test('observe object and map properties', function(t) { var map = m.map({ a : 1 }); var events = []; - + t.throws(function() { m.observe(map, "b", function() {}); }); - - var d1 = m.observe(map, "a", function(newV, oldV) { - events.push([newV, oldV]); + + var d1 = m.observe(map, "a", function(e) { + events.push([e.newValue, e.oldValue]); }); - + map.set("a", 2); map.set("a", 3); d1(); map.set("a", 4); - + var o = m.observable({ a: 5 }); @@ -25,23 +25,23 @@ test('observe object and map properties', function(t) { t.throws(function() { m.observe(o, "b", function() {}); }); - var d2 = m.observe(o, "a", function(newV, oldV) { - events.push([newV, oldV]); + var d2 = m.observe(o, "a", function(e) { + events.push([e.newValue, e.oldValue]); }); - + o.a = 6; o.a = 7; d2(); o.a = 8; - + t.deepEqual(events, [ [2, 1], [3, 2], [6, 5], [7, 6] ]); - - t.end(); + + t.end(); }); test('observe computed values', function(t) { @@ -51,10 +51,10 @@ test('observe computed values', function(t) { var f = m.observable(0); var c = m.computed(function() { return v.get(); }); - var d2 = c.observe(function(newV, oldV) { + var d2 = c.observe(function(e) { v.get(); f.get(); - events.push([newV, oldV]); + events.push([e.newValue, e.oldValue]); }); v.set(6); diff --git a/test/tojs.js b/test/tojs.js index d99e2fca42..21cf2ec40a 100644 --- a/test/tojs.js +++ b/test/tojs.js @@ -72,8 +72,8 @@ test('json2', function(t) { var ab = []; var tb = []; - m.observe(analyze, function(d) { ab.push(d); }, true); - m.observe(alltags, function(d) { tb.push(d); }, true); + m.observe(analyze, function(d) { ab.push(d.newValue); }, true); + m.observe(alltags, function(d) { tb.push(d.newValue); }, true); o.todos[0].details.url = "boe"; o.todos[1].details.url = "ba"; diff --git a/test/typescript/typescript-tests.ts b/test/typescript/typescript-tests.ts index 0a310f45ac..cd3b9e9771 100644 --- a/test/typescript/typescript-tests.ts +++ b/test/typescript/typescript-tests.ts @@ -38,8 +38,8 @@ test('decorators', function(t) { var events: any[] = []; var d1 = observe(o, (ev: IObjectChange) => events.push(ev.name, ev.oldValue)); - var d2 = observe(o, 'price', (newValue, oldValue) => events.push(newValue, oldValue)); - var d3 = observe(o, 'total', (newValue, oldValue) => events.push(newValue, oldValue)); + var d2 = observe(o, 'price', (ev) => events.push(ev.newValue, ev.oldValue)); + var d3 = observe(o, 'total', (ev) => events.push(ev.newValue, ev.oldValue)); o.price = 4;