Skip to content

Instantly share code, notes, and snippets.

@philcockfield
Created October 20, 2012 08:11
Show Gist options
  • Save philcockfield/3922640 to your computer and use it in GitHub Desktop.
Save philcockfield/3922640 to your computer and use it in GitHub Desktop.
Checkbox view-model projection of Derby array property
asArray = (options) ->
_.chain(options).where(checked:true).map((option) -> option.key).value()
module.exports =
###
Sets up the x-bind callback handlers.
Example binding in template:
<input type="checkbox" x-bind="click:checkbox" checked="{.checked}">
@param ready: The derby [ready] object.
###
setupXBindHandlers: (ready) ->
ready (model) ->
@model = model
@checkbox = (e, el, next, dom) ->
model = @model
viewModel = model.at(el).get()
user = model.filter('users').where('id').equals(viewModel.__ref).get()?[0]
# Update checked state.
checked = $(el).attr('checked') is 'checked'
viewModel.checked = checked
# Save the new value to the actual model.
# This causes the value to be persisted back to the server.
value = asArray( user["_#{viewModel.__prop}"] )
path = "users.#{user.id}.#{viewModel.__prop}"
model.set path, value
###
Prepares a collection of models with view-models for
the given set of options.
@param items: The [Collection] of models.
@param options: The [Collection] of option definitions.
An option has:
- key: Unique identifier of the option (human readable).
- name: The display name of the option.
###
viewModels: (model, items, options) ->
viewModelsFor = (item) ->
create = (option) ->
key = option.key
viewModel =
key: key
name: option.name
checked: _(item[options.name]).contains(key)
__ref: item.id
__prop: options.name
# Create a view model for each option.
return (create option for option in options.toList())
# Enumerate each model, and add a collection of view-models,
# one for each option, to the private version of the property.
# For example:
#
# - user.roles => the real models.
# - user._roles => the view-models.
#
for item in items.toList()
item[ options.clientName ] = viewModelsFor(item)
###
Monitor for changes to the options on the server, and
update the view-models.
###
syncWithRemote: (ready, collectionName, optionsProperty) ->
ready (model) ->
collection = model.at( collectionName )
collection.on 'set', "*.#{optionsProperty}", (id, newValue, oldValue, isLocal) ->
# Only update for changes that came in remotely.
unless isLocal
# Enumerate all the option definition view-models
# udpating the 'checked' value.
item = collection.get( id )
for option, i in item['_' + optionsProperty]
path = "#{option.__ref}._#{optionsProperty}.#{i}.checked"
checked = _(newValue).contains(option.key)
collection.set path, checked
<!-- Works with the the checkbox controller. -->
<checkbox:>
<input type="checkbox" x-bind="click:checkbox" checked="{.checked}">
###
A wrapper for a Derby collection that contains
name helpers.
###
module.exports = class Collection
constructor: (@model, @name, @items) ->
@clientName = "_#{@name}"
toList: -> (value for key, value of @items.get())
toRefList: ->
# See:
# - Google group discussion: https://groups.google.com/forum/?fromgroups=#!topic/derbyjs/aT_vYbR3lyM
# - https://github.com/greelgorke/playingderbyjs/blob/master/src/summary/summary.coffee
ids = (key for key of @items.get())
path = "_ids.#{@name}.items"
@model.set path, ids
@model.refList @clientName, @items, path
Collection = require '../util/collection'
checkbox = require '../util/checkbox_controller'
config = require '../config'
{ get, view, ready } = config
get '/admin', (page, model) ->
model.subscribe 'users', (err, users) ->
model.fetch 'roles', (err, roles) ->
# Create collection, and store a private version
# of users for use by the templates.
users = new Collection(model, 'users', users)
roles = new Collection(model, 'roles', roles)
users.toRefList()
# Intitialize the option view models
checkbox.viewModels model, users, roles
# Render page.
model.set '_title', 'Administration'
page.render 'admin'
# Initialize controllers.
checkbox.setupXBindHandlers ready
checkbox.syncWithRemote ready, 'users', 'roles'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment