diff --git a/CHANGELOG.md b/CHANGELOG.md
index cd9f5d2b5..7339c6574 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
* `@observable` is now always defined on the class and not in the instances. This means that `@observable` properties are enumerable, but won't appear if `Object.keys` or `hasOwnProperty` is used on a class _instance_.
* if an (argumentless) action is passed to `observable` / `extendObservable`, it will not be converted into a computed property.
* Implemented #316: `whyRun()`
+* Fixed #285: class instances are now also converted by `toJS`. Also members defined on prototypes which are enumerable are converted.
# 2.2.2:
diff --git a/README.md b/README.md
index 106c1df76..91d3b70f9 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,6 @@ _Simple, scalable state management_
[![Build Status](https://travis-ci.org/mobxjs/mobx.svg?branch=master)](https://travis-ci.org/mobxjs/mobx)
[![Coverage Status](https://coveralls.io/repos/mobxjs/mobx/badge.svg?branch=master&service=github)](https://coveralls.io/github/mobxjs/mobx?branch=master)
[![Join the chat at https://gitter.im/mobxjs/mobx](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mobxjs/mobx?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-[![#mobx channel on reactiflux discord](https://img.shields.io/badge/discord-%23mobx%20%40reactiflux-blue.svg)](https://discord.gg/0ZcbPKXt5bYAa2J1)
* Installation: `npm install mobx --save`. React bindings: `npm install mobx-react --save`
* [Ten minute, interactive MobX + React tutorial](https://mobxjs.github.io/mobx/getting-started.html)
@@ -27,7 +26,7 @@ _Anything that can be derived from the application state, should be derived. Aut
which includes the UI, data serialization, server communication, etc.
-
+
React and MobX together are a powerful combination. React renders the application state by providing mechanisms to translate it into a tree of renderable components. MobX provides the mechanism to store and update the application state that React then uses.
diff --git a/src/api/tojson.ts b/src/api/tojs.ts
similarity index 91%
rename from src/api/tojson.ts
rename to src/api/tojs.ts
index 955555a01..d0ca477d5 100644
--- a/src/api/tojson.ts
+++ b/src/api/tojs.ts
@@ -40,14 +40,16 @@ export function toJS(source, detectCycles: boolean = true, __alreadySeen: [any,
);
return res;
}
- if (typeof source === "object" && isPlainObject(source)) {
+ if (isObservable(source) && source.$mobx instanceof ObservableValue)
+ return toJS(source(), detectCycles, __alreadySeen);
+ if (source instanceof ObservableValue)
+ return toJS(source.get(), detectCycles, __alreadySeen);
+ if (typeof source === "object") {
const res = cache({});
- for (let key in source) if (source.hasOwnProperty(key))
+ for (let key in source)
res[key] = toJS(source[key], detectCycles, __alreadySeen);
return res;
}
- if (isObservable(source) && source.$mobx instanceof ObservableValue)
- return toJS(source(), detectCycles, __alreadySeen);
return source;
}
diff --git a/src/mobx.ts b/src/mobx.ts
index 78be170e8..e5da2430f 100644
--- a/src/mobx.ts
+++ b/src/mobx.ts
@@ -44,7 +44,7 @@ export { observe } from "./api/obse
export { intercept } from "./api/intercept";
export { autorun, autorunAsync, autorunUntil, when, reaction } from "./api/autorun";
export { expr } from "./api/expr";
-export { toJSON, toJS } from "./api/tojson";
+export { toJSON, toJS } from "./api/tojs";
export { ITransformer, createTransformer } from "./api/createtransformer";
export { whyRun } from "./api/whyrun";
diff --git a/test/babel/babel-tests.js b/test/babel/babel-tests.js
index be96ba7a5..49ba6cec3 100644
--- a/test/babel/babel-tests.js
+++ b/test/babel/babel-tests.js
@@ -523,3 +523,28 @@ test("enumerability", t => {
t.end();
})
+
+test("issue 285 (babel)", t => {
+ const {observable, toJS} = mobx;
+
+ class Todo {
+ id = 1;
+ @observable title;
+ @observable finished = false;
+ @observable childThings = [1,2,3];
+ constructor(title) {
+ this.title = title;
+ }
+ }
+
+ var todo = new Todo("Something to do");
+
+ t.deepEqual(toJS(todo), {
+ id: 1,
+ title: "Something to do",
+ finished: false,
+ childThings: [1,2,3]
+ })
+
+ t.end();
+})
\ No newline at end of file
diff --git a/test/observables.js b/test/observables.js
index c0981b8f0..ad797a0b1 100644
--- a/test/observables.js
+++ b/test/observables.js
@@ -1099,6 +1099,58 @@ test('json cycles', function(t) {
t.end();
})
+test('#285 class instances with toJS', t => {
+ function Person() {
+ this.firstName = "michel";
+ mobx.extendObservable(this, {
+ lastName: "weststrate",
+ tags: ["user", "mobx-member"],
+ fullName: function() {
+ return this.firstName + this.lastName
+ }
+ })
+ }
+
+ const p1 = new Person();
+ // check before lazy initialization
+ t.deepEqual(mobx.toJS(p1), {
+ firstName: "michel",
+ lastName: "weststrate",
+ tags: ["user", "mobx-member"]
+ });
+
+ // check after lazy initialization
+ t.deepEqual(mobx.toJS(p1), {
+ firstName: "michel",
+ lastName: "weststrate",
+ tags: ["user", "mobx-member"]
+ });
+
+ t.end()
+})
+
+test('#285 non-mobx class instances with toJS', t => {
+ function Person() {
+ this.firstName = "michel";
+ this.lastName = mobx.observable("weststrate");
+ }
+
+ const p1 = new Person();
+ // check before lazy initialization
+ t.deepEqual(mobx.toJS(p1), {
+ firstName: "michel",
+ lastName: "weststrate"
+ });
+
+ // check after lazy initialization
+ t.deepEqual(mobx.toJS(p1), {
+ firstName: "michel",
+ lastName: "weststrate"
+ });
+
+ t.end()
+})
+
function stripSpyOutput(events) {
events.forEach(ev => {
delete ev.time;
diff --git a/test/typescript-tests.ts b/test/typescript-tests.ts
index e1b71ed57..57e3c6ae3 100644
--- a/test/typescript-tests.ts
+++ b/test/typescript-tests.ts
@@ -796,3 +796,27 @@ test("enumerability", t => {
t.end();
})
+test("issue 285 (babel)", t => {
+ const {observable, toJS} = mobx;
+
+ class Todo {
+ id = 1;
+ @observable title: string;
+ @observable finished = false;
+ @observable childThings = [1,2,3];
+ constructor(title: string) {
+ this.title = title;
+ }
+ }
+
+ var todo = new Todo("Something to do");
+
+ t.deepEqual(toJS(todo), {
+ id: 1,
+ title: "Something to do",
+ finished: false,
+ childThings: [1,2,3]
+ })
+
+ t.end();
+})