Created
December 29, 2011 14:06
-
-
Save larscwallin/1534239 to your computer and use it in GitHub Desktop.
jquery.bondage, Wrapper for jquery-datalink
This file contains hidden or 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
Welcome to jquery.bondage! | |
This project wraps the jquery.datalink plugin which does a great job of linking a data set (json object) to a HTML form. | |
The datalink plug does not update the form for you which i felt was a bit annoying :/ Well, instead of doing a one of | |
for the project i was working on i decided to make a reusable plugin :) | |
jquery.bondage adds some features to jquery.datalink, | |
- Form rendering | |
- Centralized API for management of linked forms/datasets | |
API: | |
- bondage.registerDataSet(json_object,"dataset_name") | |
Registers a json data object using a name key which is later used for retrieval/assignment. | |
- bondage.unregisterDataSet("dataset_name") | |
Unregisters a json data object using a name key. | |
- bondage.bind("dataset_name",htmlFormElement,json_object_map) | |
Registers a binding between a registered dataset and a *reference* to HTML Form Element. | |
Note that the form *MUST* have a unique name attribute as this is automatically used as a reference key for | |
later retrieval (in the refresh() method for example). | |
The last parameter is an optional name map if the form fields differs from the field names in the dataset. | |
- bondage.refresh("htmlFormElementNameAttribute") | |
Updates one of the form values to display changes to the dataset which has been registered using the bind() method. | |
I added a simple example html page and also included the jquery.datalink.js file in the Gist. | |
I'll most probably update this plug in the near future. It seems to work fine for now though :) | |
Ciao, | |
Lars |
This file contains hidden or 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
<html> | |
<head> | |
<script type="text/javascript" src="http://code.jquery.com/jquery.js"></script> | |
<script type="text/javascript" src="../jquery.datalink.js"></script> | |
<script type="text/javascript" src="jquery.bondage.js"></script> | |
<script type="text/javascript"> | |
var activity = { | |
id:11, | |
comment:"Went well", | |
date_performed:"20111229", | |
performed_by:"you", | |
registered_by:"me", | |
type:"default", | |
location:"737" | |
}; | |
$().ready(function(){ | |
$().bondage.registerDataSet(activity,"activity"); | |
$().bondage.bind("activity",$("form")[0]); | |
$().bondage.refresh("Activity"); | |
}); | |
</script> | |
</head> | |
<body> | |
<form id="" class="Activity" name="Activity" data-objecttype="Activity"> | |
<fieldset name="properties"> | |
<input type="hidden" name="id" value=""/> | |
<label for="date_performed">Date performed</label> | |
<input name="date_performed" value="" type="text"/> | |
<label for="performed_by">Performed by</label> | |
<input name="performed_by" value="" type="text"/> | |
<label for="registered_by">Registered by</label> | |
<input name="registered_by" value="" type="text"/> | |
<label for="location">Location</label> | |
<input name="location" value="" type="text"/> | |
<label for="comment">Comment</label> | |
<textarea name="comment"></textarea> | |
</fieldset> | |
<fieldset name="actions"> | |
</fieldset> | |
</form> | |
</body> | |
</html> |
This file contains hidden or 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
/* | |
* jquery.bondage 0.1 | |
* A simple wrapper for jquery-datalink (https://github.com/BorisMoore/jquery-datalink) | |
* Adds the ability to display bound data in the form by calling the bondage.refresh() method. | |
* | |
* API: | |
* - bondage.registerDataSet(json_object,"dataset_name") | |
* Registers a json data object using a name key which is later used for retrieval/assignment. | |
* | |
* - bondage.unregisterDataSet("dataset_name") | |
* Unregisters a json data object using a name key. | |
* | |
* - bondage.bind("dataset_name",htmlFormElement,json_object_map) | |
* Registers a binding between a registered dataset and a *reference* to HTML Form Element. | |
* Note that the form *MUST* have a unique name attribute as this is automatically used as a reference key for | |
* later retrieval (in the refresh() method for example). | |
* The last parameter is an optional name map if the form fields differs from the field names in the dataset. | |
* | |
* - bondage.refresh("htmlFormElementNameAttribute") | |
* Updates one of the bound form's values to display changes to the dataset which has been registered using the | |
* bind() method. | |
* | |
* | |
*/ | |
(function( $ ){ | |
$.fn.bondage = { | |
dataSets:{}, | |
forms:{}, | |
registerDataSet : function(dataSet,key){ | |
if(!this.dataSets[key]){ | |
this.dataSets[key] = dataSet; | |
}else{ | |
console.log("jquery.bondage.unregisterDataSet(): key '" + key + "' was found in the collection. Can not create duplicate."); | |
return false; | |
} | |
return true; | |
}, | |
unregisterDataSet : function(key){ | |
if(this.dataSets[key]){ | |
this.dataSets[key] = dataSet; | |
}else{ | |
console.log("jquery.bondage.unregisterDataSet(): key '" + key + "' was not found in the collection."); | |
return false; | |
} | |
return true; | |
}, | |
bind : function(dataSetKey,form,mapping){ | |
var dataSet = this.dataSets[dataSetKey]; | |
var result; | |
if(dataSet){ | |
result = $(form).link(activity); | |
if(result){ | |
this.forms[form.name] = { | |
"form":form, | |
"dataSet":dataSet, | |
"mapping":mapping | |
}; | |
}else{ | |
console.log("jquery.bondage.bind(): jquery-datalink.link() returned false. Unable to link form to dataSet."); | |
return false; | |
} | |
}else{ | |
console.log("jquery.bondage.bind(): dataSetKey '" + dataSetKey + "' is not registered."); | |
return false; | |
} | |
}, | |
refresh : function(formName){ | |
var form = this.forms[formName]; | |
var dataSet; | |
var mapping; | |
if(form){ | |
dataSet = form.dataSet; | |
mapping = form.mapping; | |
fields = form.form.elements; | |
$(fields).each(function(){ | |
this.value = dataSet[this.name]; | |
}); | |
}else{ | |
console.log("jquery.bondage.refresh(): Form '" + formName + "' was not found. Unable to refresh."); | |
return false; | |
} | |
} | |
}; | |
})( jQuery ); |
This file contains hidden or 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
/*! | |
* jQuery Data Link plugin v1.0.0pre | |
* http://github.com/jquery/jquery-datalink | |
* | |
* Copyright Software Freedom Conservancy, Inc. | |
* Dual licensed under the MIT or GPL Version 2 licenses. | |
* http://jquery.org/license | |
*/ | |
(function( $, undefined ){ | |
var oldcleandata = $.cleanData, | |
links = [], | |
fnSetters = { | |
val: "val", | |
html: "html", | |
text: "text" | |
}, | |
eventNameSetField = "setField", | |
eventNameChangeField = "changeField"; | |
function getLinks(obj) { | |
var data = $.data( obj ), | |
cache, | |
fn = data._getLinks || (cache={s:[], t:[]}, data._getLinks = function() { return cache; }); | |
return fn(); | |
} | |
function bind(obj, wrapped, handler) { | |
wrapped.bind( obj.nodeType ? "change" : eventNameChangeField, handler ); | |
} | |
function unbind(obj, wrapped, handler) { | |
wrapped.unbind( obj.nodeType ? "change" : eventNameChangeField, handler ); | |
} | |
$.extend({ | |
cleanData: function( elems ) { | |
for ( var j, i = 0, elem; (elem = elems[i]) != null; i++ ) { | |
// remove any links with this element as the source | |
// or the target. | |
var links = $.data( elem, "_getLinks" ); | |
if ( links ) { | |
links = links(); | |
// links this element is the source of | |
var self = $(elem); | |
$.each(links.s, function() { | |
unbind( elem, self, this.handler ); | |
if ( this.handlerRev ) { | |
unbind( this.target, $(this.target), this.handlerRev ); | |
} | |
}); | |
// links this element is the target of | |
$.each(links.t, function() { | |
unbind( this.source, $(this.source), this.handler ); | |
if ( this.handlerRev ) { | |
unbind( elem, self, this.handlerRev ); | |
} | |
}); | |
links.s = []; | |
links.t = []; | |
} | |
} | |
oldcleandata( elems ); | |
}, | |
convertFn: { | |
"!": function(value) { | |
return !value; | |
} | |
}, | |
setField: function(target, field, value) { | |
if ( target.nodeType ) { | |
var setter = fnSetters[ field ] || "attr"; | |
$(target)[setter](value); | |
} else { | |
var parts = field.split("."); | |
parts[1] = parts[1] ? "." + parts[1] : ""; | |
var $this = $( target ), | |
args = [ parts[0], value ]; | |
$this.triggerHandler( eventNameSetField + parts[1] + "!", args ); | |
if ( value !== undefined ) { | |
target[ field ] = value; | |
} | |
$this.triggerHandler( eventNameChangeField + parts[1] + "!", args ); | |
} | |
} | |
}); | |
function getMapping(ev, changed, newvalue, map) { | |
var target = ev.target, | |
isSetData = ev.type === eventNameChangeField, | |
mappedName, | |
convert, | |
name; | |
if ( isSetData ) { | |
name = changed; | |
if ( ev.namespace ) { | |
name += "." + ev.namespace; | |
} | |
} else { | |
name = (target.name || target.id); | |
} | |
if ( !map ) { | |
mappedName = name; | |
} else { | |
var m = map[ name ]; | |
if ( !m ) { | |
return null; | |
} | |
mappedName = m.name; | |
convert = m.convert; | |
if ( typeof convert === "string" ) { | |
convert = $.convertFn[ convert ]; | |
} | |
} | |
return { | |
name: mappedName, | |
convert: convert, | |
value: isSetData ? newvalue : $(target).val() | |
}; | |
} | |
$.extend($.fn, { | |
link: function(target, mapping) { | |
var self = this; | |
if ( !target ) { | |
return self; | |
} | |
function matchByName(name) { | |
var selector = "[name=" + name + "], [id=" + name +"]"; | |
// include elements in this set that match as well a child matches | |
return self.filter(selector).add(self.find(selector)); | |
} | |
if ( typeof target === "string" ) { | |
target = $( target, this.context || null )[ 0 ]; | |
} | |
var hasTwoWay = !mapping, | |
map, | |
mapRev, | |
handler = function(ev, changed, newvalue) { | |
// a dom element change event occurred, update the target | |
var m = getMapping( ev, changed, newvalue, map ); | |
if ( m ) { | |
var name = m.name, | |
value = m.value, | |
convert = m.convert; | |
if ( convert ) { | |
value = convert( value, ev.target, target ); | |
} | |
if ( value !== undefined ) { | |
$.setField( target, name, value ); | |
} | |
} | |
}, | |
handlerRev = function(ev, changed, newvalue) { | |
// a change or changeData event occurred on the target, | |
// update the corresponding source elements | |
var m = getMapping( ev, changed, newvalue, mapRev ); | |
if ( m ) { | |
var name = m.name, | |
value = m.value, | |
convert = m.convert; | |
// find elements within the original selector | |
// that have the same name or id as the field that updated | |
matchByName(name).each(function() { | |
newvalue = value; | |
if ( convert ) { | |
newvalue = convert( newvalue, target, this ); | |
} | |
if ( newvalue !== undefined ) { | |
$.setField( this, "val", newvalue ); | |
} | |
}); | |
} | |
}; | |
if ( mapping ) { | |
$.each(mapping, function(n, v) { | |
var name = v, | |
convert, | |
convertBack, | |
twoWay; | |
if ( $.isPlainObject( v ) ) { | |
name = v.name || n; | |
convert = v.convert; | |
convertBack = v.convertBack; | |
twoWay = v.twoWay !== false; | |
hasTwoWay |= twoWay; | |
} else { | |
hasTwoWay = twoWay = true; | |
} | |
if ( twoWay ) { | |
mapRev = mapRev || {}; | |
mapRev[ n ] = { | |
name: name, | |
convert: convertBack | |
}; | |
} | |
map = map || {}; | |
map[ name ] = { name: n, convert: convert, twoWay: twoWay }; | |
}); | |
} | |
// associate the link with each source and target so it can be | |
// removed automaticaly when _either_ side is removed. | |
self.each(function() { | |
bind( this, $(this), handler ); | |
var link = { | |
handler: handler, | |
handlerRev: hasTwoWay ? handlerRev : null, | |
target: target, | |
source: this | |
}; | |
getLinks( this ).s.push( link ); | |
if ( target.nodeType ) { | |
getLinks( target ).t.push( link ); | |
} | |
}); | |
if ( hasTwoWay ) { | |
bind( target, $(target), handlerRev ); | |
} | |
return self; | |
}, | |
unlink: function(target) { | |
this.each(function() { | |
var self = $(this), | |
links = getLinks( this ).s; | |
for (var i = links.length-1; i >= 0; i--) { | |
var link = links[ i ]; | |
if ( link.target === target ) { | |
// unbind the handlers | |
//wrapped.unbind( obj.nodeType ? "change" : "changeData", handler ); | |
unbind( this, self, link.handler ); | |
if ( link.handlerRev ) { | |
unbind( link.target, $(link.target), link.handlerRev ); | |
} | |
// remove from source links | |
links.splice( i, 1 ); | |
// remove from target links | |
var targetLinks = getLinks( link.target ).t, | |
index = $.inArray( link, targetLinks ); | |
if ( index !== -1 ) { | |
targetLinks.splice( index, 1 ); | |
} | |
} | |
} | |
}); | |
}, | |
setField: function(field, value) { | |
return this.each(function() { | |
$.setField( this, field, value ); | |
}); | |
} | |
}); | |
})(jQuery); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment