canjs / can-define

Observable properties
https://canjs.com/doc/can-define.html
MIT License
15 stars 7 forks source link

Simple Reducer property behavior #437

Open justinbmeyer opened 5 years ago

justinbmeyer commented 5 years ago

I'd like something like the following to work:

DefineMap.extend({
  name: "string"
  nameChangeCount: {
    default: 0,
    reduce: {
        "name": (last, nameEvent)=> last + 1; 
    }
  }
})

Essentially, the reducer would specify events to bind to and a corresponding function that gets the last value, event emitted and returns the new value. Here's how fullName could be built without a getter (and https://github.com/canjs/can-define/pull/434 merged) :

DefineMap.extend({
  first: "string",
  last: "string",
  nameParts: {
    default: [],
    reduce: {
        "first": ( [first, last], {newValue} ) => [newValue, last],
        "last": ( [first, last], {newValue} ) => [first, newValue],
    }
  },
  fullName: {
    default: "",
    reduce: {
        nameParts: (last, {newValue} )=> newValue.join(" ")
    }
  }
})

Notes / Questions

How to build this

We can add a reducer observable that would work like:

new ReducerObservable({
  "first": ( [first, last], {newValue} ) => [newValue, last],
   "last": ( [first, last], {newValue} ) => [first, newValue],
}, this, 0 )

This can be mixed in similar to how ResolverObservable is here: https://github.com/canjs/can-define/blob/10d906817e238286acfcf3a1d5120232232f0983/can-define.js#L455

ReducerObservable would be a constructor function very similar in API to ResolverObservable, with the right symbols:

Reducer.prototype = {
    [getValue]()
    [setValue]()
    [onValue]() 
    [offValue]()
}