These are chat archives for canjs/canjs

24th
Jun 2015
Matthew Phillips
@matthewp
Jun 24 2015 13:27
I have a list of 5 items
I call .destroy() on one of them
I would expect the list to be 4
where is the logic where it gets spliced out of the list?
Matthew Phillips
@matthewp
Jun 24 2015 13:50
i think you're right
thanks
espretto
@espretto
Jun 24 2015 15:26
hi there, i'm new to gitted and canjs, anybody home?
Chris Gomez
@akagomez
Jun 24 2015 15:31
Welcome.
espretto
@espretto
Jun 24 2015 15:31
great! could you help me with some canjs basics?
Chris Gomez
@akagomez
Jun 24 2015 15:32
I’ll do my best.
espretto
@espretto
Jun 24 2015 15:35
i've been working with emberjs and knockoutjs but am still struggling atm with canjs observables/computed attributes - hands on: how do you typically wire up model requests depending on some observable e.g. the route?
as suggested in the docs i created a computed representing the request deferred
can.Map.extend({
   model: null,
    state: 'pending', 

    promise: compute(function () {
      var scope = this,
          promise;

      promise = ConfigModel
        .findOne({
          version: scope.attr('version'),
          title: scope.attr('panoTitle')
        })
        .then(function (model) {
          scope.attr('model', model);
        })
        .always(function () {
          scope.attr('state', promise.state());
        })
        .promise();

      scope.attr('state', promise.state());

      return promise;
    })
});
problem is the promise doesn't evaluate its dependencies until someone binds to it
Chris Gomez
@akagomez
Jun 24 2015 15:40
@espretto That’s by design.
espretto
@espretto
Jun 24 2015 15:40
makes sense, now..

if i rewrite state to depend on promise

...
state: compute(function () {
  return this.attr('promise').state();
});
...

the binding won't get set up. i intend to request {{state}} in my stache template to trigger the request - if that's how it's supposed to be.

return this.promise().state() won't setup the dependency either.
neither does stache's live-binding
espretto
@espretto
Jun 24 2015 15:46
the only solution to force computed properties to evaluate their dependencies was to add
Component.extend({
  ...
  scope: MapCtr,
  events: {
    '{scope} promise': noop,
    ...
  }
  ...
});
Chris Gomez
@akagomez
Jun 24 2015 15:48
So this.attr(‘promise’) didn’t call the promise’ compute function?
espretto
@espretto
Jun 24 2015 15:52

yes it did, however, state does not revaluate itself when promise does. i was expecting any call to .attr would bind the computed being created to that attribute, but it doesn't. which is why i tried sth. like

Component.extend({
  ...
  scope: {
    promise: '...',
    state: compute(function (newState) {
      if (arguments.length) return newState;
      var self = this;
      self.bind('promise', function (evt, newPromise) {
        newPromise.always(function () {
          self.attr('state', this.state());
        })
      })
    })
  }
  ...
});

but that doesn't work either.

i think i basically try to achieve sth. like amd can/list/promise does
Chris Gomez
@akagomez
Jun 24 2015 15:56
Is the promise property changing? Or is the state of the promise property changing?
espretto
@espretto
Jun 24 2015 15:56
the state depends on promise
and promise depends on panoTitle and version
Chris Gomez
@akagomez
Jun 24 2015 15:57
And you’re saying that promise does not get re-evaluated when panoTitle or version change?
espretto
@espretto
Jun 24 2015 15:58
only after the call to bind (not stache live-binding but can.Map#bind)
attr doesn't duffice
the call to bind i implemented through the component's events object which btw i don't understand the naming of - why not just control?
Chris Gomez
@akagomez
Jun 24 2015 16:00
Are you changing panoTitle and version with .attr(‘panoTitle’)?
espretto
@espretto
Jun 24 2015 16:01
yes i am - well, actually
<input can-value="panoTitle"/>
Chris Gomez
@akagomez
Jun 24 2015 16:04
Seems like you’re doing it all right. I’m curious if you can reproduce in a JSBin so I can take a closer look.
espretto
@espretto
Jun 24 2015 16:08
yes i'd like to! this is so exciting: i only recently came to want to contribute to such "bigger" projects, and canjs so far is doing a great job. the fiddle will take me a few minutes - how to notify you? how did you @ me? ah i see - easy
do you have time for another question? this one should be easier
Chris Gomez
@akagomez
Jun 24 2015 16:09
Yeah, sure. If I can’t answer I’m sure somebody else will.
espretto
@espretto
Jun 24 2015 16:12
i figured defining components is the way to go for nested views so i'm building my whole app of them. how do give viewModel/scope (are these the same?) computed properties access to dom elements? here is the draft:
Component.extend({
  scope: {
    question: computed(function () {
      return howToAccessThisComponentsElement; // ?
    })
  }
})
Chris Gomez
@akagomez
Jun 24 2015 16:13
Yes, viewModel and scope are the same. scope was recently renamed to viewModel for various reasons.
We strongly discourage DOM manipulation from the scope.
espretto
@espretto
Jun 24 2015 16:14
as of 2.3-pre? i'm working with 2.2
Chris Gomez
@akagomez
Jun 24 2015 16:14
2.2 uses scope as an alias to viewModel.
In fact, our hope is that there is seldom a need to manipulate DOM outside of a template.
espretto
@espretto
Jun 24 2015 16:16
viewModel suits it better i agree, if only events in components was renamed to control and Map#serialize to Map#toJSON and ..
Chris Gomez
@akagomez
Jun 24 2015 16:16
But if you do need to manipulate DOM via JS, you should do it in an event handler bound to a “state” change (i.e. viewModel property change event)
espretto
@espretto
Jun 24 2015 16:18
well in this case i'd like to bind a property to third party plugin which wraps an element and has its own events, setup and teardown functions (google.maps.StreetViewPanorama)
so i went about it like this:
Component.extend({
  viewModel: {
    internalProp: compute({
      get: function () {
        // missing element access here
      },
      set: '', // set
      on: '', // bind to the plugin's custom events
      off: '', // unbind from the plugin's custom events
    })
  },
  events: {
    inserted: '', // init 3rd party plugin on element
    removed: '' // teardown 3rd party plugin from element
  }
});
espretto
@espretto
Jun 24 2015 16:26
i ended up constructing the computed properties within the inserted event handler where the plugin's instance is created as well. i set those onto the scope/viewModel but am not sure if i myself have to tear them down or whether the framework will do it for me?
Chris Gomez
@akagomez
Jun 24 2015 16:27
You’ve got the right idea. I personally would just create the bindings to properties in inserted though.
You’ll have to remove those yourself I think.
espretto
@espretto
Jun 24 2015 16:31
how do you do that? calling scope.prop.off() or can.unbindAndTeardown? from within events.off or events.removed? will i have to call this._super.apply(this, arguments) if i override one of those? will this._super work inside component.events?
does the removed event occur after the element has been removed or just before that?
Chris Gomez
@akagomez
Jun 24 2015 16:48
inserted: function () {
  var scope = this.scope;
  this.plugin = new Plugin();
  this.plugin.on(‘event’, function (data) { scope.attr(‘relevantProperty’, data); }
},
removed: function () {
  this.plugin.off(‘event’);
}
This is, if I’m understanding you correctly.
espretto
@espretto
Jun 24 2015 16:59
what about
scope: { ... },
events: {
  inserted: function () {
    // ..init
    this.scope.attr('computed', compute(initial, {
      // get, set, on, off // bind to some plugin value
     }));
  },
  removed: function () {
    // do i have to tear down the computed? even if its on the scope thus within range of canjs?
  }
}
Justin Meyer
@justinbmeyer
Jun 24 2015 17:01
@espretto don't use computes on the prototype
use the define plugin
much better
espretto
@espretto
Jun 24 2015 17:03

sth like

scope: function (tagAttrs, parentScope, elem) {
  return {
    define: {
      computed: {
        // `value/Value, type/Type, get, set, serialize` etc.
        // access to `elem` yes, but without the `on` and `off` options this is a dead end, what now?
      }
    }
  }
}

using the define plugin there is no on and off options, how to circumvent that?

Chris Gomez
@akagomez
Jun 24 2015 17:13
@espretto I’ve never needed on/off in the context of a view model. What’s your use case?
espretto
@espretto
Jun 24 2015 17:15
this one (see settings object to compute)
    var input = document.getElementById("age")
    var value = can.compute("",{
        get: function(){
            return input.value;
        },
        set: function(newVal){
            input.value = newVal;
        },
        on: function(updated){
            input.addEventListener("change", updated, false);
        },
        off: function(updated){
            input.removeEventListener("change", updated, false);
        }
    })
in my case the value that is set/get/listened to is hidden within the plugin which has it's own methods i.e. not canjs' event system to get/set the value and listen to changes
espretto
@espretto
Jun 24 2015 17:29
sure i could add and remove the listeners as you suggested within inserted and removed with the listeners setting and changing scope/viewModel properties but that scatters the whole logic to maintain one attribute over three places sope/viewModel events.inserted and events.removed which is way inferior to the above almost self-contained approach. sry, i have to leave, i'll be back later this day
just leave me what you think - i'll @akagomez you once i have the jsbin ready for the other issue.
thank you for your time. the help is very much appreciated!
espretto
@espretto
Jun 24 2015 20:34
@akagomez here goes my computed request scenario http://jsbin.com/noxajixonu/edit?js,console,output
Chris Gomez
@akagomez
Jun 24 2015 20:56
@espretto The promise compute won’t be run unless something is bound to it. Either in the template, in the events object, or directly (compute.bind(‘change’)).
Also, you have '{scope.promise} change’: function () { … }. change events in CanJS are not the same as in other libraries.
A change means that some property on an entire object has changed.
For instance..
var map = new can.Map({ foo: ‘bar’, boo: ‘baz’ });
map.bind(‘change’, function () { console.log(‘!!!’) });
map.attr(‘foo’, ‘baz’); //-> !!!
map.attr(‘boo’, ‘bar’); //-> !!!
What you likely wanted was…
var map = new can.Map({ foo: ‘bar’, boo: ‘baz’ });
map.bind(‘boo’, function () { console.log(‘!!!’) });
map.attr(‘foo’, ‘baz’); //-> 
map.attr(‘boo’, ‘bar’); //-> !!!
Or, rather…
‘{scope} promise’: function () { … }
The reason I mention this is that change events have a lot of overhead. For performance reasons they should be avoided when possible.
espretto
@espretto
Jun 24 2015 21:01
i tried that, but still the promise now keep re-evaluating itself, plus i'd prefer a solution without a noop binding
good to know that about change
Chris Gomez
@akagomez
Jun 24 2015 21:02
The promise keeps re-evaluating because state goes from pending to resolved on every call to the promise compute.
Since promise is dependent on state, it causes a loop.
espretto
@espretto
Jun 24 2015 21:05

it seems the promise's call to attr('state', state)sets up a dependency if i'm not mistaken, anyway.
if you could fix the jsbin to work without binding a noop handler to work correctly i.e.

  • update the state when promise changes and when resolved/rejected
  • update the promise if idKey changes
  • update the model when promise resolves

that would grant me a good night sleep!

Chris Gomez
@akagomez
Jun 24 2015 21:15
@espretto Are you really on 2.0.3?
@espretto The binding on set seems to be a 2.0.3 bug. If you reference latest, your approach works fine: http://jsbin.com/xahaxugobi/1/edit?html,js,console,output
espretto
@espretto
Jun 24 2015 22:14
2.0.3 was what i found in jsbin's list, my bad. the binding on set persists in latest though see here. your solution does work for me, however intercepting the idKey setter kind of defeats the purpose of observables, after all, idKey shouldn't have to know what depends on it at all. i'll call it a day. again thanks for your time and efforts! hope to see you around here soon.