diff --git a/src/create-view-model.ts b/src/create-view-model.ts index 0024c87..b9b457c 100644 --- a/src/create-view-model.ts +++ b/src/create-view-model.ts @@ -12,7 +12,7 @@ import { _getAdministration, $mobx } from "mobx" -import { invariant } from "./utils" +import { invariant, getAllMethodsAndProperties } from "./utils" export interface IViewModel { model: T @@ -42,7 +42,8 @@ export class ViewModel implements IViewModel { constructor(public model: T) { invariant(isObservableObject(model), "createViewModel expects an observable object") - Object.getOwnPropertyNames(model).forEach(key => { + // use this helper as Object.getOwnPropertyNames doesn't return getters + getAllMethodsAndProperties(model).forEach((key: any) => { if (key === ($mobx as any) || key === "__mobxDidRunLazyInitializers") { return } diff --git a/src/utils.ts b/src/utils.ts index d143ac1..5efa330 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -23,3 +23,13 @@ export function addHiddenProp(object: any, propName: string, value: any) { value }) } + +const isGetter = (x: any, name: any) => (Object.getOwnPropertyDescriptor(x, name) || {}).get +const isFunction = (x: any, name: any) => typeof x[name] === "function"; +const deepFunctions = (x: any): any => + x && x !== Object.prototype && + Object.getOwnPropertyNames(x) + .filter(name => isGetter(x, name) || isFunction(x, name)) + .concat(deepFunctions(Object.getPrototypeOf(x)) || []); +const distinctDeepFunctions = (x: any): any => Array.from(new Set(deepFunctions(x))); +export const getAllMethodsAndProperties = (x: any): any => distinctDeepFunctions(x).filter((name: string) => name !== "constructor" && !~name.indexOf("__")); diff --git a/test/create-view-model.js b/test/create-view-model.ts similarity index 87% rename from test/create-view-model.js rename to test/create-view-model.ts index 3e90f17..2d08c1f 100644 --- a/test/create-view-model.js +++ b/test/create-view-model.ts @@ -1,25 +1,46 @@ -"use strict" - -const utils = require("../src/mobx-utils") -const mobx = require("mobx") +import * as utils from "../src/mobx-utils"; +import * as mobx from "mobx"; mobx.configure({ enforceActions: "observed" }) -test("create view model", () => { - function Todo(title, done, usersInterested) { - mobx.extendObservable(this, { - title: title, - done: done, - usersInterested: usersInterested, - get usersCount() { - return this.usersInterested.length - } - }) +class TodoClass { + @mobx.observable title; + @mobx.observable done; + @mobx.observable usersInterested; + @mobx.computed + get usersCount() { + return this.usersInterested.length } +} + +function Todo(title, done, usersInterested) { + mobx.extendObservable(this, { + title: title, + done: done, + usersInterested: usersInterested, + get usersCount() { + return this.usersInterested.length + } + }) +} + +test("test NON Class/decorator createViewModel behaviour", () => { + const model = new Todo("coffee", false, ["Vader", "Madonna"]); + + tests(model); +}) - const model = new Todo("coffee", false, ["Vader", "Madonna"]) - const viewModel = utils.createViewModel(model) +test("test Class/decorator createViewModel behaviour", () => { + const model = new TodoClass(); + model.title = "coffee"; + model.done = false; + model.usersInterested = ["Vader", "Madonna"]; + tests(model); +}) + +function tests(model) { + const viewModel = utils.createViewModel(model); let tr let vr // original rendering @@ -154,4 +175,4 @@ test("create view model", () => { d1() d2() -}) +} \ No newline at end of file