Created
May 8, 2012 21:40
-
-
Save Shadowfiend/2639589 to your computer and use it in GitHub Desktop.
DataHolder is used to manage model objects with knockout observable values.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# DataHolder handles most Knockout binding situations for properties | |
# transparently. You can instantiate a DataHolder with a set of attributes | |
# that are turned into observable properties of the DataHolder. | |
# | |
# Attributes passed in that are functions are not wrapped in an observable. | |
# Additionally, all attributes whose names end in `Fn' are ensured to be | |
# wrapped in functions and set as is, without an observable. | |
# | |
# All attributes are passed through the preprocessAttribute function, which | |
# can be overridden in child classes. These can be used to do type | |
# conversions and data lookups, for example, as well as, for example, | |
# wrapping arrays in observableArray instead of observable. | |
# | |
# Incoming attribute values that are already observables will be assigned | |
# directly. | |
# | |
# You can call updateAttributesWith on a DataHolder instance at any time and | |
# pass in new attributes to update the properties of the DataHolder. This | |
# will respect existing observables, setting their values where needed by | |
# invoking them correctly and triggerin their listeners. It will also unwrap | |
# incoming observables where applicable. | |
# | |
# When instantiating a DataHolder, you can also pass it an object of | |
# defaults. This is typically used when invoking the DataHolder constructor | |
# as a super-constructor. Last but not least, you can pass in a list of | |
# attributes that should be explicitly set to undefined if not present in | |
# the list of initial attributes. These will be wrapped in observables whose | |
# values will be undefined, and will thus ensure that no errors are produced | |
# regarding undefined properties. | |
class DataHolder | |
constructor: (attributes, defaults, defaultUndefineds) -> | |
defaults ||= {} | |
defaultUndefineds ||= [] | |
defaults[attribute] = undefined for attribute in defaultUndefineds | |
attributes[prop] = value for prop, value of defaults when not attributes[prop] | |
@updateAttributesWith attributes | |
setValue: (attribute, value) -> | |
# Don't replace existing values with undefined. | |
return if typeof value == 'undefined' && this[attribute] | |
current = this[attribute] | |
if typeof value == 'function' && value['__ko_proto__'] && | |
typeof current == 'function' && current['__ko_proto__'] | |
current value() | |
else if typeof current == 'function' && current['__ko_proto__'] | |
current value | |
else if typeof value == 'function' | |
this[attribute] = value | |
else | |
this[attribute] = ko.observable value | |
preprocessAttribute: (attribute, value) -> | |
if attribute.match(/Fn$/) && typeof value != 'function' && value | |
os.processFnString value | |
else | |
value | |
updateAttributesWith: (attributes) -> | |
for attribute of attributes | |
@setValue attribute, @preprocessAttribute(attribute, attributes[attribute]) | |
# Expose globally. | |
os.DataHolder = DataHolder |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@Shadowfiend Any chance of getting an updated version of this sometime soon? ;)