New Observables API discussion

I’m trying to figure out how to structure the new can-define technology that allows attr-less observables.

I see three needs:

  1. To have something analogous to can.Map (Based on can.Construct)
  2. To have something that works with normal constructor functions
  3. To have something that can make observables from POJOs (proxy)

something analogous to can.Map

I think we need to have something that works and looks just like can.Map except you don’t use .attr() and there’s no bubbling/change events. In 3.0, everything else will still be Type.extend.

I’ve thought about something like:

Observable = require("can-observable");

MyObservable = Observable.extend({
  init: function(){},
  method: function(){},
  define: {
    prop: { … }
  }
});

myo = new MyObservable({prop: "foo"})
myo.on("prop", function(){})

myo.prop = "BAR";

// how to add a new property?

Thoughts:

  • I like Observable because it’s a noun similar to Map and List.
  • Observable is a lot to type compared to Map and List.
  • If somehow we do create a similar List variant, what would it be called?
  • Kinda strange using Observable and List together.

Alternatives:

  • Defined
  • DMap - Defined map
  • DefinedMap
  • DefineMap - require("can-define/map")
  • Observe - We used to have can.Observe
  • Model - Super confusing historically

something that works with normal constructor functions

I’d like people to be able to use Class expressions and make properties observable.

var define = require("can-define");
var Person = class {
  constructor(first, last) {
    this.first = first;
    this.last = last;
  }
  method(){ }
};

define(Person.prototype, {
  first: "*",
  last: "*",
  fullName: {
    get: function() {
      return this.first + " " + this.last;
    }
  }
});

Thoughts:

  • I think this is alright. I REALLY wish class had some type of extension callback.

something that can make observables from POJOs

We need to take objects and convert them to observables, analogous to:

var obs = new can.Map({foo: "bar"})

There’s two ways we can do this:

  1. via getter/setters
  2. via Proxy

Ideally, the API would work with getter/setters and use proxies if they are provided.

Getter/Setters

Getter/Setters allow us two options:

  1. Mutate the object in place and add “observability” to it.
  2. Create a new observable object.

Mutate the object in place

We can transform objects to being observable:

var observe = require("can-observe");

var obj = {prop: "FOO"};
observe(obj);
obj.prop = "BAR";

// if you want to add properties:
observe(obj,"newProp","ZED");
obj.on("newProp", function(){ ... });

Create a new observable object.

var Observe = require("can-observe");
var obj = new Observe({prop: "FOO"});
obj.prop

// If you want to add new properties?

Proxy

Proxies don’t really need to work on a particular “type”. They can wrap anything and make it observable:

var observe = require("can-observe");
var obj = observe({prop: "FOO"});
obj.prop

var arr = observe([1,2,3])
arr[0]

Adding properties works automatically.

Thoughts on POJOs:

  • proxy works pretty differently than getter/setters to maybe its best not to unify?
  • if we don’t unify, do we create them as discrete projects?

For #1, here’s what I’m thinking: https://github.com/canjs/can-define/blob/master/map/map-test.js

I like DefinedMap, it is saying what we are doing with the object.
We have also no problem to implement this on can.List -> DefinedList.

But you are right, how we add new properties to the defined map?

Maybe I didn’t thought to much about this…

Good work… !
Happy of the discussion tomorrow…

Wow, I didn’t know about class expressions, that’s interesting. I wonder when they would be better to use over the class declaration.

I think this is alright. I REALLY wish class had some type of extension callback.

Aside from the Component tag case, is there anything else you do when one of our types are being extended?

Yes, setup all the define properties, Model would build .findAll.