Skip to content

Instantly share code, notes, and snippets.

@josephrexme
Forked from stephanschubert/active_data.js.coffee
Created January 29, 2016 09:17
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 josephrexme/7f0a6124d9b4d2794551 to your computer and use it in GitHub Desktop.
Save josephrexme/7f0a6124d9b4d2794551 to your computer and use it in GitHub Desktop.
Simple two-way data binding
do ($ = jQuery, exports = window) ->
class ActiveDataBinder
constructor: (uid) ->
# Use a jQuery object as simple PubSub
pubSub = $ {}
# We expect a 'data' attribute specifying the binding
# in the form 'data-bind-<uid>="<attribute>" and name
# the corresponding message in the form '<uid>:change'.
dataAttr = "bind-" + uid
message = uid + ":change"
# Listen to change events on elements with the data-binding attribute and proxy
# them to the PubSub, so that the change is "broadcasted" to all listeners.
$(document).on "change", "[data-" + dataAttr + "]", (event) ->
$input = $(this)
attr = $input.data(dataAttr)
value = $input.data("bind-value")
value ||= if $input.is(":checkbox") then $input.is(":checked") else $input.val()
pubSub.trigger message, [ attr, value ]
# PubSub propagates changes to all bound elements, setting value of
# input tags or HTML content of other tags.
pubSub.on message, (event, property, value) ->
for elem in $("[data-" + dataAttr + "='" + property + "']")
$elem = $(elem)
if $elem.data("bind-value")
# Nothing to do. No visual representation of the value.
else if $elem.is("input:not(:checkbox), textarea, select")
$elem.val(value)
else if $elem.is(":checkbox")
$elem.prop("checked", value)
else
$elem.html(value)
return pubSub
# ActiveData wraps a 'data' object identified by 'uid', listens for change
# events and provides a minimal interface of 'set' and 'get' methods for
# easy two-way data binding.
class ActiveData
constructor: (data, uid) ->
# Setup the data binding, listen for message in the form "<uid>:change"
# and update the data unless the change was initiated by us.
binder = new ActiveDataBinder(uid)
message = uid + ":change"
wrapper =
set: (attr, val) ->
data[attr] = val
binder.trigger message, [ attr, val, this ]
get: (attr) -> data[attr]
binder.on message, (event, attr, value, initiator) ->
wrapper.set(attr, value) unless initiator == wrapper
return wrapper
# Export both classes into the global namespace (usually 'window').
exports.ActiveDataBinder = ActiveDataBinder
exports.ActiveData = ActiveData
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment