kmalakoff/knockback — Issue #2: Simplified view model syntax and memory management
Author: kmalakoff
Created: 2011-11-03 07:54:24+00:00
State: open

I needed to jump through some hoops to get Knockback.ModelAttributeObservable and Knockback.LocalizedObservable working. It went as follows:

1) Use a normal class instantiated with new (meaning you call "attribute_observable = new Knockback.ModelAttributeObservable(model, {keypath: 'name'}, {})"):

class Knockback.ModelAttributeObservable
constructor: (@model, @bind_info, @view_model) ->

2) Inside the constructor create and return the observable:
constructor: (@model, @bind_info, @view_model) ->
...
@_kb_observable = ko.dependentObservable(@_onGetValue)
return kb.observable(this) # equivalent to return @_kb_observable

3) Inside the destroy method release the observable:

destroy: ->
@_kb_observable.dispose(); @_kb_observable = null

4) Bind and attach any methods to the returned observable so you can call destroy on the returned observable:

_.bindAll(this, 'destroy')
@_kb_observable.destroy = @destroy

5) You then use an attribute observable like:

class ContactViewModel
constructor: (model) ->
@name = new kb.ModelAttributeObservable(model, {keypath: 'name'}, this)
destroy:
@name.destroy()

*****************

I think the syntax could be simplified to:

PersonViewModel = () ->
@first_name = kb.ModelAttributeObservable(model, {keypath: 'name'}, this)

ko.applyBindings(new PersonViewModel())

with automatic memory management from inside the wrapped dependentObservable if the dependentObservable has a customizable callback when it is destroyed which cleans up the kb.ModelAttributeObservable.

So this would require:

1) Investigating Knockout mechanisms for having a custom callback when @_kb_observable.dispose() is called. If one could be found or created...

2) Update the Knockback.ModelAttributeObservable and Knockback.LocalizedObservable implementations to something like:

constructor: (@model, @bind_info, @view_model) ->
...
@_kb_observable = ko.dependentObservable(@_onGetValue)
_.bind(this, 'destroy'); @_kb_observable.setCustomDisposeCallback(@destroy)
return kb.observable(this) # equivalent to return @_kb_observable

destroy: ->
@_kb_observable = null

3) Expose a more Knockout like creation method like:
kb.ModelAttributeObservable = (model, bind_info, view_model) -> return new kb._ModelAttributeObservable(model, bind_info, view_model)

4) And testing memory management with a scenario like:

PersonViewModel = () ->
@first_name = kb.ModelAttributeObservable(model, {keypath: 'name'}, this)

ko.applyBindings(new PersonViewModel())

# release bindings - is there an explicit call? and how would you validate that your model attribute was released? (wrap the existing destroy method and update the registration to the ko.dependentObservable?)

*****************
When I started Knockout I was a bit surprised by their function method exporting and memory management schemes so I "stuck with what I knew": explicit new/destroy. But I think their syntax is more simple and I think Knockback can be simplified. Any volunteers ;-)