Skip to content

Instantly share code, notes, and snippets.

@justinbmeyer
Created January 9, 2018 03:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save justinbmeyer/1b8f7330ccc6178e310fbc27fb9592a9 to your computer and use it in GitHub Desktop.
Save justinbmeyer/1b8f7330ccc6178e310fbc27fb9592a9 to your computer and use it in GitHub Desktop.

@function can-define.types.value value @parent can-define.behaviors

Specify the behavior of a property by listening to changes in other properties.

@signature value(resolve, listenTo, stopListening)

The value behavior is used to compose a property value from events dispatched by other properties on the map. value enables techniques very similar to using event streams and functional reactive programming. When the property is bound for the first time, value will be called with resolve, listenTo, and stopListening. Use listenTo to listen to events dispatched on the map or other observables, stopListening to stop listening to those events if needed, and resolve to set a new value on the observable. For example, the following counts the number of times the name property changed:

Person = DefineMap.extend("Person",{
    name: "string",
    nameChangeCount: {
        value(resolve, listenTo){
            var count = 0;

            listenTo("name", () => {
                resolve(++count);
            });

            resolve(count);
        }
    }
});

var p = new Person();
p.on("nameChangedCount", (ev, newVal)=> {
    console.log("name changed", newVal, "times");
});

p.name = "Justin" // logs name changed 1 times
p.name = "Ramiya" // logs name changed 2 times

@param {function(Any)} resolve(value) Sets the value of this property as value.

@param {function(Any,String,Fuction)} listenTo(bindTarget,event,handler) abc.

@param {function(Any,String,Fuction)} stopListening(bindTarget,event,handler) abcd.

@return {function} An optional teardown function. If provided, the teardown function will be called when the property is unbound.

@body

Use

The value behavior should be used where the [can-define.types.get] behavior can not derive a property value from instantaneous values. This often happens in situations where the fact that something changes needs to saved in the state of the application.

Lets first see an example where [can-define.types.get] should be used, the ubiquitous fullName property. The following creates a fullName property that derives its value from the instantaneous first and last values:

DefineMap.extend("Person", {
    first: "string",
    last: "string",
    fullName: {
        get() {
            return this.first + " " + this.last;
        }
    }
});

[can-define.types.get] is great for these types of values. But [can-define.types.get] is unable to derive property values based on the change of values or the passage of time.

The following fullNameChangeCount increments every time fullName changes:

DefineMap.extend("Person", {
    first: "string",
    last: "string",
    fullName: {
        get() {
            return this.first + " " + this.last;
        }
    },
    fullNameChangeCount: {
        value(resolve, listenTo){
            var count = 0;
            resolve(0);
            listenTo("fullName", ()=> {
                resolve(++count)
            })
        }
    }
});

The following time property increments every second:

var Timer = DefineMap.extend("Timer",{
    time: {
        value(resolve) {
            resolve(new Date());

            var interval = setInterval(() => {
                resolve(new Date())
            },1000);

            return () => {
                clearInterval(interval);
            };
        }
    }
});

var timer = new Timer();
timer.on("time", function(ev, newVal, oldVal){
    console.log(newVal) //-> logs a new date every second
});

// how do you stop an interval? // lets just "kill" if there's no binding at the end of this .... // temporarily bind

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment