SteveSanderson / knockout-es5

Knockout.js meets ECMAScript 5 properties
158 stars 39 forks source link

TypeScript support #32

Open wgebczyk opened 9 years ago

wgebczyk commented 9 years ago

Hi,

Let's consider following code:

HTML

<body>
  <div data-bind="text: name"></div>
  <div data-bind="text: weight"></div>
  <div data-bind="text: price"></div>
  <div data-bind="text: size"></div>

  <button data-bind="click: function() { i.price = 556677; i.name = 'abccccc'; }">Change</button>

  <script>
    var i = new Module.Item();
    i.name = "abc";
    i.weight = 34;
    i.price = 777;
    i.size = 123;

    ko.applyBindings(i);
  </script>
</body>

TypeScript

module Module {
  export class Item {
    name: string;
    weight: number;

    private _price: number;
    get price(): number {
      return this._price;
    }
    set price(value: number) {
      this._price = value;
    }

    private _size: number;
    get size(): number {
      return this._size;
    }

    constructor() {
      this.name = "x";
      this.weight = 2233;

      ko.track(this, ["name", "price", "size"]);
    }
  }
}

The problem is that ko-es5 uses only instance members. I've prepared tweak to ko-es5 code, that handles that.

Consider following tweak to ko-es5

propertyNames.forEach(function(propertyName) {
  // Skip properties that are already tracked
  if (propertyName in allObservablesForObject) {
    return;
  }

  var workOnObj = obj;
  var descriptor = Object.getOwnPropertyDescriptor(workOnObj, propertyName);
  if (descriptor === undefined) {
    workOnObj = Object.getPrototypeOf(obj);
    descriptor = Object.getOwnPropertyDescriptor(workOnObj, propertyName);
  }

  // Skip properties where descriptor can't be redefined
  if (undefined === descriptor || false === descriptor.configurable) {
    return;
  }

  var origValue = workOnObj[propertyName],
    isArray = Array.isArray(origValue),
    observable = ko.isObservable(origValue) ? origValue
      : isArray ? ko.observableArray(origValue)
      : ko.observable(origValue);

  Object.defineProperty(workOnObj, propertyName, {
    configurable: true,
    enumerable: true,
    get: observable,
    set: ko.isWriteableObservable(observable) ? observable : undefined
  });

  allObservablesForObject[propertyName] = observable;

  if (isArray) {
    notifyWhenPresentOrFutureArrayValuesMutate(ko, observable);
  }
});

I'll investigate further compatibility with TS. Having core/pure KO that targets at least ES5/modern browsers with stripped legacy code would be nice.

jrsearles commented 9 years ago

Note - this isn't specific to TypeScript. As you mentioned the plugin only grabs the instances own properties. I think what you really want to do is track the private properties, since those should be on the instance.

jmvtrinidad commented 7 years ago

For typescript support check out this decorators that works similar like this plugin.