diff --git a/src/api/object-api.ts b/src/api/object-api.ts index eba347874..65dec00a0 100644 --- a/src/api/object-api.ts +++ b/src/api/object-api.ts @@ -45,6 +45,25 @@ export function values(obj: any): string[] { ) } +export function entries(map: ObservableMap): ReadonlyArray +export function entries(ar: IObservableArray): ReadonlyArray +export function entries(obj: T): ReadonlyArray +export function entries(obj: any): string[][] { + if (isObservableObject(obj)) { + return keys(obj).map(key => [key, obj[key]]) + } + if (isObservableMap(obj)) { + return keys(obj).map(key => [key, obj.get(key)]) + } + if (isObservableArray(obj)) { + return obj.map((key, index) => [index.toString(), key]) + } + return fail( + process.env.NODE_ENV !== "production" && + "'entries()' can only be used on observable objects, arrays and maps" + ) +} + export function set(obj: ObservableMap, values: { [key: string]: V }) export function set(obj: ObservableMap, key: K, value: V) export function set(obj: IObservableArray, index: number, value: T) diff --git a/src/mobx.ts b/src/mobx.ts index d1f6bb2bf..21d978c8e 100644 --- a/src/mobx.ts +++ b/src/mobx.ts @@ -97,7 +97,7 @@ export { autorun, reaction, IReactionOptions } from "./api/autorun" export { when, IWhenOptions } from "./api/when" export { action, isAction, runInAction, IActionFactory } from "./api/action" -export { keys, values, set, remove, has, get } from "./api/object-api" +export { keys, values, entries, set, remove, has, get } from "./api/object-api" export { decorate } from "./api/decorate" export { configure } from "./api/configure" export { onBecomeObserved, onBecomeUnobserved } from "./api/become-observed" diff --git a/test/base/api.js b/test/base/api.js index adf024fb6..2ef8ba05c 100644 --- a/test/base/api.js +++ b/test/base/api.js @@ -56,6 +56,7 @@ test("correct api should be exposed", function() { "transaction", "untracked", "values", + "entries", "when" ].sort() ) diff --git a/test/base/object-api.js b/test/base/object-api.js index 04f06f370..ba0aa8eb5 100644 --- a/test/base/object-api.js +++ b/test/base/object-api.js @@ -1,5 +1,17 @@ const mobx = require("../../src/mobx") -const { keys, when, set, remove, values, reaction, observable, extendObservable, has, get } = mobx +const { + keys, + when, + set, + remove, + values, + entries, + reaction, + observable, + extendObservable, + has, + get +} = mobx test("keys should be observable when extending", () => { const todos = observable({}) @@ -66,6 +78,25 @@ test("object - set, remove, values are reactive", () => { expect(snapshots).toEqual([[3], [3, 4], [5, 4], [5]]) }) +test("object - set, remove, entries are reactive", () => { + const todos = observable({}) + const snapshots = [] + + reaction(() => entries(todos), entries => snapshots.push(entries)) + + expect(has(todos, "x")).toBe(false) + expect(get(todos, "x")).toBe(undefined) + set(todos, "x", 3) + expect(has(todos, "x")).toBe(true) + expect(get(todos, "x")).toBe(3) + remove(todos, "y") + set(todos, "z", 4) + set(todos, "x", 5) + remove(todos, "z") + + expect(snapshots).toEqual([[["x", 3]], [["x", 3], ["z", 4]], [["x", 5], ["z", 4]], [["x", 5]]]) +}) + test("object - set, remove, keys are reactive", () => { const todos = observable({ a: 3 }) const snapshots = [] @@ -101,6 +132,25 @@ test("map - set, remove, values are reactive", () => { expect(snapshots).toEqual([[3], [3, 4], [5, 4], [5]]) }) +test("map - set, remove, entries are reactive", () => { + const todos = observable.map({}) + const snapshots = [] + + reaction(() => entries(todos), entries => snapshots.push(entries)) + + expect(has(todos, "x")).toBe(false) + expect(get(todos, "x")).toBe(undefined) + set(todos, "x", 3) + expect(has(todos, "x")).toBe(true) + expect(get(todos, "x")).toBe(3) + remove(todos, "y") + set(todos, "z", 4) + set(todos, "x", 5) + remove(todos, "z") + + expect(snapshots).toEqual([[["x", 3]], [["x", 3], ["z", 4]], [["x", 5], ["z", 4]], [["x", 5]]]) +}) + test("map - set, remove, keys are reactive", () => { const todos = observable.map({ a: 3 }) const snapshots = [] @@ -145,6 +195,34 @@ test("array - set, remove, values are reactive", () => { ]) }) +test("array - set, remove, entries are reactive", () => { + const todos = observable.array() + const snapshots = [] + + reaction(() => entries(todos), entries => snapshots.push(entries)) + + expect(has(todos, 0)).toBe(false) + expect(get(todos, 0)).toBe(undefined) + set(todos, 0, 2) + expect(has(todos, 0)).toBe(true) + expect(get(todos, 0)).toBe(2) + + set(todos, "1", 4) + set(todos, 3, 4) + set(todos, 1, 3) + remove(todos, 2) + remove(todos, "0") + + expect(snapshots).toEqual([ + [["0", 2]], + [["0", 2], ["1", 4]], + [["0", 2], ["1", 4], ["2", undefined], ["3", 4]], + [["0", 2], ["1", 3], ["2", undefined], ["3", 4]], + [["0", 2], ["1", 3], ["2", 4]], + [["0", 3], ["1", 4]] + ]) +}) + test("observe & intercept", () => { let events = [] const todos = observable( @@ -235,4 +313,5 @@ test("computed props are not considered part of collections", () => { expect(get(x, "y")).toBe(undefined) // disputable? expect(keys(x)).toEqual([]) expect(values(x)).toEqual([]) + expect(entries(x)).toEqual([]) })