Skip to content
This repository was archived by the owner on Jan 16, 2020. It is now read-only.

Commit a4a4636

Browse files
committed
Work on declarative linking from html to object, and from object to object, including nested objects.
The markup could be produced by nested jQuery templates. (To do). Declarative binding from html to object working for top-level fields. Nested binding from html to object not yet completed.
1 parent f1c9b6b commit a4a4636

File tree

5 files changed

+194
-52
lines changed

5 files changed

+194
-52
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# jQuery Data Link plugin plugin v1.0.0pre.
1+
# jQuery Data Link plugin v1.0.0pre.
22

33
_Note: This plugin is currently in beta form and may change significantly before version 1.0 is released. See tagged versions for stable Beta releases. Requires jquery version 1.4.2._
44

beta2/datalink.html

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,42 @@
3737

3838
<script type="text/javascript">
3939
var person = {
40-
firstName: "Jo",
41-
lastName: "Proctor",
42-
address: {
43-
city: "Redmond"
40+
firstName: "Jo",
41+
lastName: "Proctor",
42+
address: {
43+
city: "Redmond",
44+
},
45+
roleColor: "yellow"
4446
},
45-
roleColor: "yellow"
46-
};
47+
person2 = {
48+
firstName: "Jo2",
49+
lastName: "Proctor2",
50+
address: {
51+
city: "Redmond2",
52+
},
53+
roleColor: "green"
54+
},
55+
person3 = {
56+
firstName: "Jo3",
57+
lastName: "Proctor3",
58+
address: {
59+
city: "Redmond3",
60+
},
61+
roleColor: "red"
62+
};
4763

4864
$.dataLink( person, "#myContainer" ).push();
65+
4966
$.dataLink( "#myContainer", person );
67+
68+
$.dataLink( person, person2 );
69+
$.dataLink( person, person3 ).push();
70+
71+
function nameConvert( value ) {
72+
return value + " lives in";
73+
}
5074

51-
function titleConvert( value, source, path, target ) {
75+
function titleConvert( value, source, path, target, map ) {
5276
return source.firstName + " lives in " + value;
5377
}
5478

@@ -62,12 +86,17 @@
6286
// Either of these will work, thanks to the binding to intermediate objects
6387
$.setField( person, "address.city", person.address.city + "Xxx" );
6488
}
89+
6590
</script>
6691

6792
<script type="text/javascript">
6893
function showData() {
6994
$( "#console" ).append("-----------------");
7095
$( "#showData" ).tmpl( person ).appendTo( "#console" );
96+
$( "#console" ).append("-----------------");
97+
$( "#showData" ).tmpl( person2 ).appendTo( "#console" );
98+
$( "#console" ).append("-----------------");
99+
$( "#showData" ).tmpl( person3 ).appendTo( "#console" );
71100
}
72101
</script>
73102

beta2/jquery.datalink2.js

Lines changed: 65 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ var linkSettings, decl,
2121

2222
linkAttr = "data-jq-link",
2323
bindAttr = "data-jq-bind",
24+
pathAttr = "data-jq-path",
2425

2526
unsupported = {
2627
"htmlhtml": 1,
@@ -85,13 +86,47 @@ var linkSettings, decl,
8586

8687
function addBinding( map, from, to, callback, links ) {
8788

88-
function findJqObject( jqObject, type ) {
89+
// ============================
90+
// Helpers for "toObject" links
91+
92+
function setFields( sourceObj, basePath, cb ) {
93+
var field, isObject, sourceVal;
94+
95+
for ( field in sourceObj ) {
96+
isObject = 1;
97+
sourceVal = sourceObj[ field ];
98+
if ( sourceObj.hasOwnProperty(field) && !( $.isFunction( sourceVal ) || sourceVal.toJSON)) {
99+
setFields( sourceVal, (basePath ? basePath + "." : basePath) + field, cb );
100+
}
101+
}
102+
return isObject || cb( sourceObj, basePath, thisMap.convert, sourceObj );
103+
}
104+
105+
function getLinkedPath( elem, path ) {
106+
var basePath = elem.getAttribute( "data-jq-path" );
107+
if ( basePath ) {
108+
path = basePath + (path ? "." + path : "");
109+
}
110+
return elem.parentNode === fromObj ? path : getLinkedPath( elem.parentNode, path );
111+
}
112+
113+
function convertAndSetField( val, path, cnvt, sourceObj ) {
114+
path = isFromHtml ? getLinkedPath( sourceObj, path ) : path;
115+
$.setField( toObj, path, cnvt
116+
? cnvt( val, path, sourceObj, toObj, thisMap )
117+
: val
118+
);
119+
}
120+
// ============================
121+
// Helper for --- TODO clean up between different cases....
122+
123+
function findJqObject( jqObject, type ) {
89124
var object, nodeName, path, linkedElems,
90125
length = jqObject.length;
91126

92127
if ( length ) {
93128
object = jqObject[0];
94-
if ( object.nodeType ) {
129+
if ( object.nodeType && type !== "from") {
95130
path = thisMap[ type ];
96131
if ( path ) {
97132

@@ -104,14 +139,14 @@ function addBinding( map, from, to, callback, links ) {
104139

105140
jqObject = jqObject.find( path ).add( jqObject.filter( path ) ); // TODO REPLACE BY ABOVE in the case of default binding, and remove support for random default binding - if perf concerns require it...
106141
thisMap[ type ] = 0;
107-
thisMap[ type + "Attr" ] = "default";
108142
}
109143
} else if ( length > 1 ) {
110144
jqObject = $([ jqObject.get() ]); // For objects: don't wrap multiples - consider as equivalent to a jQuery object containing single object - namely the array of objects.
111145
}
112146
}
113147
return jqObject;
114148
}
149+
// ============================
115150

116151
var thisMap = typeof map === "string" ? { to: map } : map && $.extend( {}, map ); // Note: "string" corresponds to 'to'. Is this intuitive? It is useful for filtering object copy: $.link( person, otherPerson, ["lastName", "address.city"] );
117152

@@ -131,7 +166,7 @@ function addBinding( map, from, to, callback, links ) {
131166
toObj = to[0],
132167
toType = objectType( toObj ),
133168
eventType = isFromHtml ? "change" : fromType + "Change",
134-
169+
135170
// TODO Verify optimization for memory footprint in closure captured by handlers, and perf optimization for using function declaration rather than statement?
136171
handler = function( ev, eventArgs ) {
137172
var cancel, sourceValue, sourcePath,
@@ -142,7 +177,7 @@ function addBinding( map, from, to, callback, links ) {
142177
var setter, fromAttr, $source;
143178

144179
fromAttr = thisMap.fromAttr;
145-
if ( fromAttr === "default" ) {
180+
if ( !fromAttr ) {
146181
// Merge in the default attribute bindings for this source element
147182
fromAttr = linkSettings.merge[ source.nodeName.toLowerCase() ];
148183
fromAttr = fromAttr ? fromAttr.from.fromAttr : "text";
@@ -178,7 +213,7 @@ function addBinding( map, from, to, callback, links ) {
178213
to.each( function( _, elem ) {
179214
var setter, targetPath , matchLinkAttr,
180215
targetValue = sourceValue,
181-
$target = $( elem ),
216+
$target = $( elem ),
182217

183218
htmlArrayOperation = {
184219
"add": function() {
@@ -215,7 +250,7 @@ function addBinding( map, from, to, callback, links ) {
215250
if ( convert && $.isFunction( convert )) {
216251
targetValue = convert( targetValue, source, sourcePath, elem, thisMap );
217252
}
218-
if ( attr === "default" ) {
253+
if ( !attr ) {
219254
// Merge in the default attribute bindings for this target element
220255
attr = linkSettings.merge[ elem.nodeName.toLowerCase() ];
221256
attr = attr? attr.to.toAttr : "text";
@@ -256,46 +291,30 @@ function addBinding( map, from, to, callback, links ) {
256291
});
257292
},
258293
"object": function() {
259-
function setFields( sourceObj, basePath, cb ) {
260-
var field, isObject, sourceVal;
261-
262-
for ( field in fromObj ) {
263-
isObject = 1;
264-
sourceVal = fromObj[ field ];
265-
if ( fromObj.hasOwnProperty(field) && !( $.isFunction( sourceVal ) || sourceVal.toJSON)) {
266-
setFields( sourceVal, (basePath ? basePath + "." : basePath) + field, cb );
267-
}
268-
}
269-
return isObject || cb( basePath, sourceObj, convert );
270-
}
271294

272-
function convertAndSetField( toPath, val, cnvt ) {
273-
$.setField( toObj, toPath, cnvt
274-
? cnvt( val, source, toPath, toObj, thisMap )
275-
: val
276-
);
277-
}
278295
// Find toPath using thisMap.to, or if not specified, use declarative specification
279296
// provided by decl.applyLinkInfo, applied to source element
280297
var convert = thisMap.Convert,
281298
toPath = thisMap.to || !isFromHtml && sourcePath;
282299

283300
if (toPath ) {
284-
convertAndSetField( toPath, thisMap.convert );
301+
convertAndSetField( sourceValue, toPath, thisMap.convert, source );
285302
} else if ( !isFromHtml ) {
286303
// This is triggered by trigger(), and there is no thisMap.from or thisMap.to specified.
287304
// This will set fields on existing objects or subobjects on the target, but will not create new subobjects, since
288305
// such objects are not linked so this would not trigger events on them. For deep copying without triggering events, use $.extend.
289306
setFields( source, "", convertAndSetField );
290307
} else { // from html. (Mapping from array to object not supported)
291308

292-
var tmplItem = $.tmplItem && $.tmplItem( source );
293-
if ( !(tmplItem && tmplItem.key) || (tmplItem.data === toObj )) {
294-
decl.applyLinkInfo( source, function(all, path, declCnvt){
295-
// TODO support for named converters
296-
convertAndSetField( path, sourceValue, window[ declCnvt ] || convert );
297-
});
298-
}
309+
// var tmplItem = $.tmplItem && $.tmplItem( source );
310+
// if ( !(tmplItem && tmplItem.key) || (tmplItem.data === toObj )) {
311+
312+
decl.applyLinkInfo( source, function(all, path, declCnvt){
313+
// TODO support for named converters
314+
convertAndSetField( sourceValue, path, window[ declCnvt ] || convert, source );
315+
});
316+
317+
// }
299318
}
300319
},
301320
"array": function() {
@@ -334,17 +353,17 @@ function addBinding( map, from, to, callback, links ) {
334353
ev.stopImmediatePropagation();
335354
}
336355
};
337-
356+
var j, l;
338357
switch ( fromType + toType ) {
339358
case "htmlarray" :
340-
for ( var j=0, l=toObj.length; j<l; j++ ) {
359+
for ( j=0, l=toObj.length; j<l; j++ ) {
341360
addBinding( thisMap, from, $( toObj[j] ), callback, links );
342361
}
343362
break;
344363

345364
case "arrayhtml" :
346365
from.bind( eventType, handler );
347-
for ( var j=0, l=fromObj.length; j<l; j++ ) {
366+
for ( j=0, l=fromObj.length; j<l; j++ ) {
348367
addBinding( thisMap, $( fromObj[j] ), to, callback, links );
349368
}
350369
break;
@@ -383,6 +402,10 @@ function addBinding( map, from, to, callback, links ) {
383402
}
384403
}
385404

405+
406+
// ============================
407+
// Helpers
408+
386409
function objectType( object ) {
387410
return object
388411
? object.nodeType
@@ -394,7 +417,7 @@ function objectType( object ) {
394417
}
395418

396419
function declarativeMap( fromType, toType ) {
397-
return !unsupported[ fromType + toType ] && $.extend( decl.from[fromType], decl.to[toType], { decl: true } );
420+
return !unsupported[ fromType + toType ] && $.extend( {}, decl.from[fromType], decl.to[toType], { decl: true } );
398421
}
399422

400423
function wrapObject( object ) {
@@ -447,6 +470,7 @@ function changeArray( array, eventArgs ) {
447470
}
448471
return ret;
449472
}
473+
// ============================
450474

451475
$.extend({
452476
dataLink: function( from, to, maps, callback ) {
@@ -538,11 +562,12 @@ $.extend({
538562
decl: {
539563
linkAttr: linkAttr,
540564
bindAttr: bindAttr,
565+
pathAttr: pathAttr,
541566
applyLinkInfo: function( elem, setTarget ){
542567
var linkInfo = elem.getAttribute( decl.linkAttr );
543-
if ( linkInfo ) {
568+
if ( linkInfo !== null ) {
544569
// toPath: convert end
545-
linkInfo.replace( /([\w\.]+)(?:\:\s*(\w+)\(\)\s*)?$/g, setTarget );
570+
linkInfo.replace( /([\w\.]*)(?:\:\s*(\w+)\(\)\s*)?$/, setTarget );
546571
}
547572
//lastName:convert1()
548573
// Alternative using name attribute:
@@ -552,7 +577,7 @@ $.extend({
552577
},
553578
applyBindInfo: function( elem, setTarget ){
554579
var bindInfo = elem.getAttribute( decl.bindAttr );
555-
if ( bindInfo ) {
580+
if ( bindInfo !== null ) {
556581
// toAttr: toPath convert( toPath ) end
557582
bindInfo.replace( /(?:([\w\-]+)\:\s*)?(?:(?:([\w\.]+)|(\w+)\(\s*([\w\.]+)\s*\))(?:$|,))/g, setTarget );
558583
}

0 commit comments

Comments
 (0)