Denny.NET

I can haz ASP.NET goodness?

About the author

Denny Ferrassoli
Developer at Casting Networks. MCP / .NET
E-mail me Send mail
Add to Technorati Favorites

Recent posts

Recent comments

Authors

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2008

JavaScript Model Objects (JMO) - An Idea

A rough prototype of what I like to call "JavaScript Model Objects" or JMO for short.While playing with ASP.NET AJAX I came across an idea of using AJAX, JSON, and JavaScript to bind data onto page elements. So today I decided to play around and try to create a "repeater" but on the client-side. This is the gist of the idea...

#1. Get the data which I want to bind using AJAX and JSON.

Example JSON data: 

var myTestObj =
{
            "rows":
                        [{"id":1, "title":"Test one", "imagesrc":"images/ajax.gif", "isTest":true},
                        {"id":2, "title":"Test two", "imagesrc":"images/playmini.gif", "isTest":true},
                        {"id":3, "title":"Test three", "imagesrc":"images/playmini.gif", "isTest":true}]
}

In this example the JSON data must have "rows" with an array of the row columns/values.

#2. Define the template that I want to bind to. This is defined directly on my HTML page.

Example:

<table>
            <tr id="JMO1">
                        <td style="border: solid 1px black">!id!</td>
                        <td style="border: solid 1px black">!title!</td>
                        <td style="border: solid 1px black"><img src="!imagesrc!" /></td>
                        <td style="border: solid 1px black">!isTest!</td>
            </tr>
</table>

As you can see I have a table with 4 columns. I want to bind to the TR object so I give my TR an id. The places where I want the data to appear are surrounded by exclamation points, like !<name>!. Also notice that I have given the TR tag an ID of "JMO1" which I will use to pass to my binding function. The TR tag is essentially my "Model Object."

#3. The functions to bind the data to my model object... Please note this is a very rough sketch of the functionality, and yes I am using jQuery:

function initJMO(modelObj, dataObj) {    
        var obj = $(modelObj);
        var data = eval(dataObj);
        var dataMembers = getDataMembers(obj)
                  
        if (dataMembers.length > 0) {
                    // Deep clone the model, save reference
                    //var clone = obj.clone(true);
                    var dataClone = obj[0].outerHTML;
                    
                    for (x = data.rows.length-1; x >= 0; x--) {
                          var thisRow = dataClone;
                                   
                          for (i = 0; i < dataMembers.length; i++) {
                                  var rg = new RegExp("!" + dataMembers[i] + "!"); rg.global = true;

                                  if (dataMembers[i] == "length") {
                                         ev = data.rows.length.toString();
                                  } else {
                                         ev = eval("data.rows[" + x + "]." + dataMembers[i]).toString();
                                  }
                                        
                                  thisRow = thisRow.replace(rg, ev);
                          }

                          obj.after(thisRow);
                    }
                            
                    // Remove model
                    obj.remove();
        }
}

function getDataMembers(obj) {
              var str = obj[0].outerHTML;

              if (str.length > 0) {
                          var rg = /!(\w+)!/g;
                          var match = str.match(rg);
                          var dMembers = [];
                          dMembers.push("length");

                          if (match != null) {
                                      for(i = 0; i < match.length; i++) {
                                            dMembers.push(match[i].replace(/!/g,""));
                                      }
                          }

                          return dMembers;
              } else {
                          return [];
              }
}

$(function() {
              initJMO("#JMO1", myTestObj);
} );

initJMO requires 2 params... 1 is the model object (our TR in this case). 2 is the data object (our un-eval'd JSON). 

initJMO clones the model object, finds all the data members (column names) we want to bind to and then goes through and replaces each !<name>! with the corresponding JSON column's data. It then appends the source of our cloned/replaced element after the model object, thus repeating the data with each row of our data object. Finally initJMO removes the model object from the page.

So what you get is your model object repeated with every row of your data object.

The cool thing about this is you can define ANY element as the model object!Now yes this still needs a lot of work and there are a few issues with it but I'd like to get some feedback. Good idea? Has this been done before?

A sample page can be viewed here: http://www.dennydotnet.com/Test/Default2.aspx

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: Client-Side | Server-Side
Posted by SuperGhost on Friday, August 10, 2007 9:48 PM
Permalink | Comments (19) | Post RSSRSS comment feed

Comments

DotNetKicks.com

Friday, August 10, 2007 10:11 PM

trackback

Trackback from DotNetKicks.com

JavaScript Model Objects - An Idea?

ajaxgirl.com

Friday, August 17, 2007 9:43 AM

pingback

Pingback from ajaxgirl.com

Ajax Girl » Blog Archive » JavaScript Model Objects

David D.

Friday, August 17, 2007 10:35 AM

David D.

This seems to be a lot of work and a subset of using a templating library such as the trimpath Template library. Am I missing something?

Tiago Albineli Motta

Friday, August 17, 2007 10:39 AM

Tiago Albineli Motta

Seems very good, but would be good if we could do "ifs" and loops something like de <c:forEach> and <c:if> of JSTL

Peter Michaux

Friday, August 17, 2007 12:22 PM

Peter Michaux

I've been thinking about similar issues lately. You would be able to save some download cost by using a compressed JSON format (eg http://peter.michaux.ca/article/2652). I'm not a fan of sending templates and also a template engine to the browser but the alternative requires some preprocessing on the server side to build compiled templates (eg http://peter.michaux.ca/article/3512)

blog.napyfab.com

Friday, August 17, 2007 4:35 PM

pingback

Pingback from blog.napyfab.com

links for 2007-08-17 « napyfab:blog

Yusuf Akyol

Friday, August 17, 2007 5:34 PM

Yusuf Akyol

Very very usefull script. Thanks a lot.
If you convert it prototype, many of developers will be more happy.
Best regrds.

billy-girlardo.com

Friday, August 17, 2007 6:47 PM

pingback

Pingback from billy-girlardo.com

All in a days work…

Yusuf Akyol

Saturday, August 18, 2007 5:58 AM

Yusuf Akyol

With Deny.Net permission, here is for prototype version:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "www.w3.org/.../xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>JMO</title>
<script type="text/javascript" src="scripts/prototype.js"></script>
<script type="text/javascript">

var myTestObj =
{
"rows":
[{"id":1, "title":"Test one", "message":"images/rssbutton.gif", "isTest":true},
{"id":2, "title":"Test two", "message":"images/ajax-loader.gif", "isTest":true},
{"id":3, "title":"Test three", "message":"images/mail.gif", "isTest":true}]
}


function initJMO(modelObj, dataObj) {

var obj = $(modelObj);
var objAncestor = obj.ancestors()[0];
var data = eval(dataObj);
var dataMembers = getDataMembers(objAncestor);

if (dataMembers.length > 0) {
// Deep clone the model, save reference
//var clone = obj.clone(true);
// Y.A. var dataClone = obj[0].outerHTML;
var dataClone = objAncestor.innerHTML;

for (x = data.rows.length-1; x >= 0; x--) {
var thisRow = dataClone;

for (i = 0; i < dataMembers.length; i++) {
var rg = new RegExp("!" + dataMembers[i] + "!"); rg.global = true;
if (dataMembers[i] == "length") {
ev = data.rows.length.toString();
} else {
ev = eval("data.rows[" + x + "]." + dataMembers[i]).toString();
}

thisRow = thisRow.replace(rg, ev);
}

// Y.A: obj.after(thisRow);
new Insertion.Top(obj.ancestors()[0],thisRow)
}

// Remove model
obj.remove();
}

}

function getDataMembers(obj) {

// Y.A: var str = obj[0].outerHTML;
var str = obj.innerHTML;
if (str.length > 0) {
var rg = /!(\w+)!/g;
var match = str.match(rg);
var dMembers = [];
dMembers.push("length");

if (match != null) {
for(i = 0; i < match.length; i++) {
dMembers.push(match[i].replace(/!/g,""));
}
}
return dMembers;
} else {
return [];
}
}
</script>

</head>
<body>
<div>
<table id="demo" style="border: solid 2px black; width: 50%;">
<thead>
<tr style="background-color: Gray">
<th>
id</th>
<th>
title</th>
<th>
message</th>
<th>
test</th>
</tr>
</thead>
<tbody>
<tr id="JMO1">
<td style="border: solid 1px black">
!id!</td>
<td style="border: solid 1px black">
!title!</td>
<td style="border: solid 1px black">
<img src="!message!" /></td>
<td style="border: solid 1px black">
!isTest!</td>
</tr>
</tbody>
</table>
</div>
</body>
<script type="text/javascript">
// Y.A. initJMO('#JMO1', myTestObj) ;
initJMO('JMO1', myTestObj) ;
</script>
</html>

Yusuf Akyol

Saturday, August 18, 2007 6:09 AM

Yusuf Akyol

Hi,
I must say again your work is very very usefull. Thanks a lot.
But your sample doesn't work at FireFox. Also, images aren't displayed Prototype version at FireFox.
Best regards.

JeffBurr

Saturday, August 18, 2007 6:33 AM

JeffBurr

If you haven't, you should check out the Ext library. This approach is incorporated in JSON data stores and binding either a grid or view (template) to that data.

Ext has a lot of additional bells and whistles, but I am finding that those come in pretty handy, too.

MarkeeO

Saturday, August 18, 2007 8:41 AM

MarkeeO

Great work.. You could be up to something here.. Laughing

The only thing I'm seeing as a future problem for this is the issue of being able to degrade gracefully.. Let's say that a browser doesn't allow javascript.. Yeah, I know almost all web application users allow javascript with their browser.. But gracefully degrading code is something good to offer to users to..

Is it possible to place the identifiers for the model object outside the visible page?.. Perhaps using styles or ids?..

MarkeeO

Saturday, August 18, 2007 8:41 AM

MarkeeO

And oh, yeah.. It doesn't work with Firefox.. Laughing

outatime.wordpress.com

Saturday, August 18, 2007 4:59 PM

pingback

Pingback from outatime.wordpress.com

JavaScript Model Objects « outaTiME

proquaker

Monday, August 20, 2007 12:35 AM

proquaker

I think something similar was done by asp.net ajax library in its beta when it was called "Atlas". There was a xml template for describing how to bind the data.

Denny Ferrassoli

Monday, August 20, 2007 8:21 AM

Denny Ferrassoli

Tiago:
A few days after writing this post I ran across jTemplates (http://jtemplates.tpython.com/)" rel="nofollow">http://jtemplates.tpython.com/) which is a complete template engine for use with jQuery. It's very nice and easy to use, supports templating via external url's, etc... check it out Smile


Yusuf:
A quick note... when I built this I used "Eval" to return the data a better method is to use the collection instead, something like:

ev = data.rows[x][dataMembers[i].toString()];

(I think that was it) Testing revealed much better performance (0.125 sec's for 50 records compared to originally 2 sec's)


proquaker:
You're correct, you can do this using XML-SCRIPT from the ASP.NET AJAX Futures release. The only issue I had with this is that you would need to learn another syntax. I was hoping to keep it native to JavaScript.

However after seeing jTemplates (http://jtemplates.tpython.com/)" rel="nofollow">http://jtemplates.tpython.com/) I think I'm going to start using that. I wasn't aware that there was anything out there like this and wanted to throw the idea out there to see if it had any supporters. Apparently it does, but there are quite a few implementations much farther ahead than this quick prototype. I don't see a need to reinvent the wheel on this one. Check out the AJAXIAN post (Friday) for the other libraries that accomplish this.

dayneo

Tuesday, August 21, 2007 7:44 AM

dayneo

Hi there,

Interesting you now starting to play with this. I'm surprised that more people havn't been playing with it. I come from a more desktop environment dealing with MS languages and always liked their data binding functionality. So, when the business said they wanted all their apps on the web, I was not too happy.

To make a long story short, I have implemented my own databinding functionality using javascript. I use 4 main libraries as the base, namely: prototype, base.js, cssQuery and behaviour. I could probably get this down by consolidating code, but it works as it is.

Obviously my development is with business applications, so I deal with data and not flashy graphics. I currently have databinding working for tables, list boxes, input boxes and a lot more. I am happy to share the code if you are interested, but not too keen on writing all the explanations. Let me know if you would like to see it.

If I get round to blogging my framework, I will post it back here.

Charles

Tuesday, September 11, 2007 11:40 AM

Charles

Yes, as was stated Ext has this functionality, and jQuery has it as well. I have developed SuperFlyDOM (based off FlyDOM), a DOM creation plugin that also maps data to a JSON DOM Template. In addition, jQuery Metadata and MetaObjects plugins map data to designated areas directly in HTML, as you have done with !variable!

Advantages of SuperFlyDOM are its fast, small codebase that also handles IE (and other) exceptions, and its flexibility with JSON data and templates.

http://jquery.com/plugins/project/SuperFlyDOM

Codeville gb

Tuesday, November 06, 2007 11:53 PM

Codeville

Indeed, this could be the way of the future!

I've been using techniques like this in production for several years already, and have recently built it into a reusable, general-purpose control for ASP.NET that makes it very easy to connect strongly-typed .NET objects to UI templates on the client side. You can use it to build repeaters, grids, hierarchies etc, all updated on the client, and with any user edits mapped back to the server. It's open-source, so I'm hoping it will be useful to some folks.

Check out the tutorial and demos at
blog.codeville.net/.../

Add comment


(Will show your Gravatar icon)  

  Country flag

biuquote
  • Comment
  • Preview
Loading