The same reader who prompted my last diversion (HTML/Adobe AIR Application Diversion One - User Login) also spurred me into another late night of coding. This time his question involved how you could display data loaded via Ajax. While certainly not just an AIR concern (any Ajax application has to worry about this), my experiments focused on the AIR side. Before going any further, let me discuss how I handle this issue now.
I've done "data display" now three ways:
- More recently I discovered jQuery templates (see my post on it). I really dig this - but it has an issue in AIR. I'm not going to go into why just yet - but it's coming up in my AIR series.
- So outside of the two methods above, most of the time I simply loop over the data, create a big ass string (that's the technical term, honest), and inject it into the DOM.
So that's what I've done so far. I'm sure there are other options, but these have worked great for me so far. My reader though threw me for a loop and asked about doing layout using XSLT. XSLT stands for XSL transformations. I won't go into the complete description here (if you want more detail, see this page), but it's essentially a way to take some XML, combine it with a XML document that describes layout, and generate HTML. It's kinda cool. ColdFusion supports it. But honestly? I've never used it. The thought of using it for Ajax though was quite intriguing.
I did some research and discovered that you can actually do XSLT on the client. This tutorial, XSLT on the Client, talks about how you can load both an XML and XSL file via Ajax and then combine them to create layout. I took their code and simplified it to make it work with AIR. (Since we don't have to worry about IE we can remove some code.)
I won't go into detail about this code since it is just a simplified version of the tutorial I linked above, but I think you can get an idea about just how simple this stuff is. Taking this and running it within AIR results in pretty much what you would expect:
Cool, right? XSL is a pretty intense language (syntax? framework? whatever). You can do looping (obviously) and other stuff too - even conditionals. So all of this took just a few minutes. What took me a few hours was deciding to turn this little experiment into a full fledged application.
I began by digging up an XML service. Yahoo has a nice Traffic API that spits out XML so I thought it would be a good starting point. I thought I'd design an application that would prompt you for your address and display the results. Here is a screen shot.
I'll paste the code and - like normal - walk you guys through it. There was some extremely painful parts of this application - mainly because I was unaware of some things that jQuery did for us.
Ok, so we can pretty much ignore the HTML at the bottom. It's basically a form, a div used for status messages (log), and a display area. Let's go to the fun code.
When the document loads, I begin by loading in my XSL sheet. Notice the use of File.applicationDirectory. This basically means "same place as my app". The fileRead function simply handles doing a synchronous file read using AIR File APIs. I convert my XSL text into a proper XML document. (Don't ask me how I learned that line - thank Google.) I then create my XSL processor object and pass in the XSL. Ignore the error handler for now.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <html> <body> <h2>Traffic Report</h2> <table border="1" width="100%"> <tr bgcolor="#9acd32"> <th>Type</th> <th>Incident</th> </tr> <xsl:for-each select="//*[local-name()='Result']"> <tr> <td><xsl:value-of select="@type"/></td> <td><xsl:value-of select="//*[local-name()='Title']"/></td> </tr> <tr bgcolor="yellow"> <td colspan="2"> <xsl:value-of select="//*[local-name()='Description']"/> <br/><b>Severity:</b> <xsl:choose> <xsl:when test="//*[local-name()='Severity']='1'"> Not very </xsl:when> <xsl:when test="//*[local-name()='Severity']='2'"> Mildly Annoying </xsl:when> <xsl:when test="//*[local-name()='Severity']='3'"> Annoying </xsl:when> <xsl:when test="//*[local-name()='Severity']='4'"> Pain in the Rear </xsl:when> <xsl:when test="//*[local-name()='Severity']='5'"> You ain't going nowhere </xsl:when> <xsl:otherwise> Unknown. Consult Fringe department. </xsl:otherwise> </xsl:choose> </td> </tr> </xsl:for-each> </table> </body> </html> </xsl:template> </xsl:stylesheet>
This was also a bit painful, but mainly because of the namespaces. Everywhere you see: //*[local-name() you can thank namespaces. I wish there was a way to ignore them. If a tag is <foo> then I want my path to just use foo. Google seemed to imply that it may be possible to tell the XSL processor to ignore the namespaces but I wasn't able to get it to work. It's not horrible - but I think it makes the XSL harder to read.
Anyway - most of the above is just the loop. The only "fancy" part is my conditional. As you can see - I do some basic branching based on the severity of the traffic report. I could have used graphics instead of text, but I think you get the idea.
So... what do you think? If you want to play with this application I've attached it to the blog entry. Unfortunately Yahoo didn't seem to have traffic data for my home town, but perhaps that's because of the overabundance of swamp, vampires, and werewolves. Your results may vary.