Tuesday, April 29, 2008
Making the Web Flexible
Unless you have been living under a rock, or have never touched the source code of a web application you have likely heard of Flex or Silverlight.
I am not going to talk about the differences, in reality, I have never used Silverlight, nor have I ever written production code in Flex. But I tell you, from my research and R&D, this is the way of the future for web applications.
Now, the more astute of you will have noticed that I am saying web applications, not sites. This is because Flex and Silverlight are useful for making what is known as Rich Internet Applications.
Using these technologies for your blog site is like using a 30 pound sledge to hammer in finishing nails. Sure you can do it, but you are going to dent a lot of wood.
Personally, when I want to read about the latest souffle cooking recipe from my favourite blog, I don't want to wait for the site to load.
There is no reason to use Flex or similar technologies for simple web sites. The vast majority of sites don't require the vast level of interactivity that Flex provides. Using Flex will limit you to users that have the Flash player installed, which is most, but not all. Some web purists will also not visit your site if it uses Flash.
Then why use Flex to build RIA? Because the value of your application will outweigh any of the negatives that some people will associate with Flash based web applications. And the time you will save in development will allow you to add more content and value then you could with the standard HTML/CSS/JavaScript approach.
So let's get to the point, and talk about the benefits of using Flex.
Live and learn.....
I am not going to talk about the differences, in reality, I have never used Silverlight, nor have I ever written production code in Flex. But I tell you, from my research and R&D, this is the way of the future for web applications.
Now, the more astute of you will have noticed that I am saying web applications, not sites. This is because Flex and Silverlight are useful for making what is known as Rich Internet Applications.
Using these technologies for your blog site is like using a 30 pound sledge to hammer in finishing nails. Sure you can do it, but you are going to dent a lot of wood.
Personally, when I want to read about the latest souffle cooking recipe from my favourite blog, I don't want to wait for the site to load.
There is no reason to use Flex or similar technologies for simple web sites. The vast majority of sites don't require the vast level of interactivity that Flex provides. Using Flex will limit you to users that have the Flash player installed, which is most, but not all. Some web purists will also not visit your site if it uses Flash.
Then why use Flex to build RIA? Because the value of your application will outweigh any of the negatives that some people will associate with Flash based web applications. And the time you will save in development will allow you to add more content and value then you could with the standard HTML/CSS/JavaScript approach.
So let's get to the point, and talk about the benefits of using Flex.
- Ideal for creating web applications
- No cross platform support issues
- Completely portable to any platform that supports Flash (Firefox, IE, QNX, Linux)
- Allows for a high degree of interactivity very cheaply
- Flex has a comprehensive visual designer that allows you to drag and drop controls onto the form
- SDK is free
- Faster development with their IDE
- Better debugging capabilities
- Memory and performance profilers
- Flex is overkill for simple things
- If you are building UI that interacts with exposed web services, you need a strong middle tier to validate data
- The IDE is not free
- The relationship between MXML and ActionScript is absolutely not the same as the relationship between HTML and JavaScript
Live and learn.....
Labels: Flex, web development
Monday, April 21, 2008
Struts 2 doubleselect: A Detailed Example
Struts 2 provides a fantastic doubleselect tag, which allows you to specify two HTML select lists in such a way that the contents of the second list is dependent on the selection of the first (1). About a month ago, I came across a situation where this type of relationship was very advantageous, and set off towards the relevant Struts 2 documentation to learn how this mysterious tag worked. I found the list of attributes useful, but the examples at the bottom of the page left much to be desired (they're completely hard-coded). In fact, even after a good hour of searching, I couldn't find a solid example of the doubleselect tag that didn't hard-code both lists! Obviously it's much more useful to take advantage of the java back-end, and it took me two full hours of trial and error to finally figure out how to get my particular scenario working. What follows is a detailed example showing how to use the Struts 2 doubleselect tag to its fullest, for the next time I or anyone else wishes to make good use of it.
For our example, suppose you run some sort of automotive website, and would like to let the use specify the Make and Model of a car. The list of Makes should contain the car company, like Porsche, Ferrari or Lamborghini. The lists of Models should be the specific cars those companies make, like "911 Turbo" and "Boxster S" for the Porsche, "F430 Spyder" and "FXX" for Ferrari, etc. So when Porsche is selected in the first list, it's set of models will be shown in the second list. If we change the selection in the first list to Ferrari, the second list should update to contain only the Ferrari models. We also want to know what Make and Model the user has selected, and be able to set default selections in each list. This can actually be done with very little JSP syntax using the doubleselect tag:
Of course, in addition to the jsp shown above, we need a struts.xml file, the struts action, a Make class and a Model class. It is assumed that you already know how to set up the struts.xml file and the basics of the struts action. Here's what we need in the other files:
So that's all there is to it! I hope this has been helpful, and saves a bit of time for the next person trying to implement a complex double-list system with back-end support.
(1) This functionality is provided using javascript which is automatically generated when Struts interprets the doubleselect tag. One downside to this approach is that it will clutter up your source quite a bit if you're using a large dataset, but this is unavoidable while using this tag.
For our example, suppose you run some sort of automotive website, and would like to let the use specify the Make and Model of a car. The list of Makes should contain the car company, like Porsche, Ferrari or Lamborghini. The lists of Models should be the specific cars those companies make, like "911 Turbo" and "Boxster S" for the Porsche, "F430 Spyder" and "FXX" for Ferrari, etc. So when Porsche is selected in the first list, it's set of models will be shown in the second list. If we change the selection in the first list to Ferrari, the second list should update to contain only the Ferrari models. We also want to know what Make and Model the user has selected, and be able to set default selections in each list. This can actually be done with very little JSP syntax using the doubleselect tag:
<%@ taglib prefix="s" uri="/struts-tags"%>
<s:form action="selectCar" method="POST">
<s:doubleselect
name="makeId" doubleName="modelId"
list="allMakes" doubleList="models"
listKey="id" doubleListKey="id"
listValue="name" doubleListValue="name"
value="defaultItem" doubleValue="doubleDefaultItem" />
</s:form>
Of course, in addition to the jsp shown above, we need a struts.xml file, the struts action, a Make class and a Model class. It is assumed that you already know how to set up the struts.xml file and the basics of the struts action. Here's what we need in the other files:
- The struts action must contain a list of Makes - the car companies, like Porsche, Lamborghini and Ferrari - and properties called makeId and modelId (which should match the type of id in the Make class) and defaultItem and doubleDefaultItem (which should match the type of name in the Make class).
- The Make class must have a name, an id and a list of Models - this is the set of cars made by each company, so things like "911 Turbo" and "Boxster S" would be in the Porsche's model list, but the Ferrari model list would contain different things like "F430 Spyder" and "FXX".
- The Model class needs only a name and an id.
- The list attribute will call getAllMakes in the struts action, and use that list as the primary list. Similarly, doubeList calls getModels on each Make, and uses the resulting lists as the secondary lists.
- The listValue attribute will call getName on each entry in the primary list, and use the resulting list of names to show on screen in the first menu. Similarly, doubleListValue calls getName on each entry in the secondary list, and stores them in memory for showing in the second menu.
- The value and doubleValue attributes will call getDefaultItem and getDoubleDefaultItem in the action, and use the results to pre-select entries in each list. For example, if you wanted the default Make to be Ferrari, you would make sure getDefaultItem returns the Ferrari object.
So that's all there is to it! I hope this has been helpful, and saves a bit of time for the next person trying to implement a complex double-list system with back-end support.
(1) This functionality is provided using javascript which is automatically generated when Struts interprets the doubleselect tag. One downside to this approach is that it will clutter up your source quite a bit if you're using a large dataset, but this is unavoidable while using this tag.
A quick look at Apple's iPhone SDK - Barcamp style!
The latest Macadamian Barcamp session focused on Apple's iPhone SDK. We found that the iPhone SDK shined by the quality of the tools it bundled. Instruments, the included profiler was rather impressive. This seems to be one of the framework's strong points. Templates to start building applications out of the box are also provided in Xcode.
We agreed that since the iPhone framework requires the use of Objective-C, it will have a fairly extensive learning curve for any person not used to native OSX development. In comparison, Google's Android is based on technologies we are more familiar with, but still has some rough edges. It will be nice to see how the two SDKs stack up, once the Android platform becomes more mature.
Below's a Barcamp style comparison chart of the smartphone SDKs we looked at:
We agreed that since the iPhone framework requires the use of Objective-C, it will have a fairly extensive learning curve for any person not used to native OSX development. In comparison, Google's Android is based on technologies we are more familiar with, but still has some rough edges. It will be nice to see how the two SDKs stack up, once the Android platform becomes more mature.
Below's a Barcamp style comparison chart of the smartphone SDKs we looked at:
Contributors - Aaron, JCB, Tony H, Dan M, Mark K, NOT Oren.
Labels: Android, Barcamp, iPhone, Smartphones
Thursday, April 10, 2008
BarCamps or: How I Learned To Let Go, And Love The Self Organizing Workshop
Macadamian is made up of many different people with a very large space of interests. Any given trip across the office can net one a variety of lessons, covering anything from how extremely large microwave dishes work and where not to point them, to car operating systems, and how to tie gmail into an extremely effective and professional GTD setup with a couple of greasemonkey scripts. It is to capitalize on this aggregation of knowledge that I have decided to start organizing Barcamps at our Ottawa office. What better way to work through ideas, practical or "what if?", than to lure a bunch of engineers together in a room and change some lightbulbs.
Barcamps were traditionally intended as "hack-a-thons", or "open source workshops". It is the latter definition I have chosen to concentrate on. A workshop without a plan, also known as "winging it", will put fear in the hearts of anybody who has ever organized any presentation. But this is where the strength of the Barcamp lies. The organizer of Barcamp needs only to set the meeting in motion, and it takes on life of it's own. The immediate disadvantage would appear to be that without organization, the members will not stay on topic and the collective attention will wander off topic. In practice, however, the common interest of all attendees will steer the workshop on a relatively tangent topic. And this is the key: when the attendees walk away from a completed Barcamp, they have participated in an active, hands-on, discussion of the primary topic and relevant related topics.
What starts as a Barcamp to learn about Google Android's architecture, will also cover the history of open mobile platforms, wireless signaling, carrier business practices, and competitor behavior. This sort of many-to-many learning ensures that all questions are raised and answered. Compare that with a one-to-many presentation that might have to rush through certain topics that are not immediately relevant to the stated topic, and you start to see how
a Barcamp can become an extremely powerful tool. Instead of following the presenter's ideas, the workshop will follow at the pace set by its attendees.
So how does one organize such an ad-hoc workshop?
Step 1: One Foundation
Imagination sparks innovation. Not all attendees are going to be aware of Google Android in detail, or mobile platforms at all. Prepare to have several high level introductory items to get the discussion going. Google's original announcement of the Android SDK is a good start. It doesn't go into heavy detail and establishes context. More importantly, instead of showing code or
documentation, it shows practical uses and what is possible with their new platform.
Step 2: Punky Reggae Party
Discussion ensues. This is the event horizon of the Barcamp. What starts as a discussion about Google Android can cover any hundred different topics, from UI design, JVM design, hardware design, or even be a practical walkthrough installing eclipse, creating applications, and attempting to install them in the Android VM (which we never did get to work properly, for the record).
Step 3: Pass It On
A Barcamp should have some tangible output, if only notes. Attendees should be able to look at minutes or resulting code to review what was covered as a refresher, or a base for further research. What's more, those that couldn't attend would also have something to look at, and would be able to follow along on their own.
Barcamps were traditionally intended as "hack-a-thons", or "open source workshops". It is the latter definition I have chosen to concentrate on. A workshop without a plan, also known as "winging it", will put fear in the hearts of anybody who has ever organized any presentation. But this is where the strength of the Barcamp lies. The organizer of Barcamp needs only to set the meeting in motion, and it takes on life of it's own. The immediate disadvantage would appear to be that without organization, the members will not stay on topic and the collective attention will wander off topic. In practice, however, the common interest of all attendees will steer the workshop on a relatively tangent topic. And this is the key: when the attendees walk away from a completed Barcamp, they have participated in an active, hands-on, discussion of the primary topic and relevant related topics.
What starts as a Barcamp to learn about Google Android's architecture, will also cover the history of open mobile platforms, wireless signaling, carrier business practices, and competitor behavior. This sort of many-to-many learning ensures that all questions are raised and answered. Compare that with a one-to-many presentation that might have to rush through certain topics that are not immediately relevant to the stated topic, and you start to see how
a Barcamp can become an extremely powerful tool. Instead of following the presenter's ideas, the workshop will follow at the pace set by its attendees.
So how does one organize such an ad-hoc workshop?
Step 1: One Foundation
Imagination sparks innovation. Not all attendees are going to be aware of Google Android in detail, or mobile platforms at all. Prepare to have several high level introductory items to get the discussion going. Google's original announcement of the Android SDK is a good start. It doesn't go into heavy detail and establishes context. More importantly, instead of showing code or
documentation, it shows practical uses and what is possible with their new platform.
Step 2: Punky Reggae Party
Discussion ensues. This is the event horizon of the Barcamp. What starts as a discussion about Google Android can cover any hundred different topics, from UI design, JVM design, hardware design, or even be a practical walkthrough installing eclipse, creating applications, and attempting to install them in the Android VM (which we never did get to work properly, for the record).
Step 3: Pass It On
A Barcamp should have some tangible output, if only notes. Attendees should be able to look at minutes or resulting code to review what was covered as a refresher, or a base for further research. What's more, those that couldn't attend would also have something to look at, and would be able to follow along on their own.
Labels: Android, Barcamp, Google, MacCamp
Monday, April 07, 2008
Operation Aborted Hell
Do you ever get the dreaded “Operation Aborted” errors on IE6 and IE7? If you have, you have likely spent hours trying to figure out the cause, staring at code, commenting out code and searching the internet looking for solutions.
On my last project I hit these errors too, and here are some of the causes and their solutions:
Cause 1:
You are attempting to modify the DOM before the DOM is ready.
Possible Solution 1:
Move all your code that modifies the DOM to the bottom of the page. Just before the closing BODY tag.
Possible Solution 2:
Defer all your JavaScript execution until after the page has loaded.
A word of warning with this approach, defer is handled differently with different browsers (isn’t this always the case) and the differences are very subtle.
Possible Solution 3:
Use code to determine when the DOM is ready, and then modify the DOM at that point. More on this topic in a follow up post.
Possible Solution 4:
Use the onLoad event to run the code that will modify the DOM. The onLoad event is executed the same across all browsers.
This solution is the cleanest, though not always possible depending on what you want to do.
Cause 2:
You are attempting to modify a parent node of the DOM element you are contained in.
Solution 1:
You cannot modify parent DOM elements from one of its child nodes. Move the JavaScript code up to the same level of the node you are attempting to modify.
On my last project I hit these errors too, and here are some of the causes and their solutions:
Cause 1:
You are attempting to modify the DOM before the DOM is ready.
Possible Solution 1:
Move all your code that modifies the DOM to the bottom of the page. Just before the closing BODY tag.
Possible Solution 2:
Defer all your JavaScript execution until after the page has loaded.
<script type="text/JavaScript" defer="defer">
<!—JavaScript code here -->
</script>
A word of warning with this approach, defer is handled differently with different browsers (isn’t this always the case) and the differences are very subtle.
Possible Solution 3:
Use code to determine when the DOM is ready, and then modify the DOM at that point. More on this topic in a follow up post.
Possible Solution 4:
Use the onLoad event to run the code that will modify the DOM. The onLoad event is executed the same across all browsers.
This solution is the cleanest, though not always possible depending on what you want to do.
Cause 2:
You are attempting to modify a parent node of the DOM element you are contained in.
Solution 1:
You cannot modify parent DOM elements from one of its child nodes. Move the JavaScript code up to the same level of the node you are attempting to modify.
Labels: DOM, HTML, IE, Javascript
Friday, April 04, 2008
Creating Dynamic HTML Content
For the uninitiated, the whole concept of generating new HTML through JavaScript can be pretty intimidating, but it certainly remains a powerful and easy way to alter a web page on the fly, especially since all the popular browsers support it nowadays.
The HTML DOM
The Document Object Model is an API which allows you to access and modify the contents of an HTML document through a client-side script such as JavaScript. The current content of the page, as stored in the browser, is represented by a tree of nodes. Any element found within the page (such as a paragraph block ) will correspond to an object (like HTMLParagraphElement), and in turn anything contained within that element (even plain text) will be accessible as childs of that node.
There have been DOMs ever since web browsers started supporting Javascript, but the one used here (and the standard nowadays) is the W3C DOM that is supported (albeit not quite perfectly) by IE 5+ and Mozilla.
One very important thing to understand right off the bat is that the DOM is an interface model. The objects you'll be invoking in your scripts are ways to access the document data, but they're not actual containers. This means you're not going to create them like you create Java objects, and that you'll be able to work with any kind of node on a general level without having to use the specific object for it.
Creating new HTML objects on the fly
The simplest way to generate content is to invoke the createElement() method of your document object.
Simple as that! A new node of type paragraph is created out of thin air, just as if you had entered "<p></p>" in your document source. But where will this new element end up? You'll have to specify that by "inserting" it into your document. For instance:
As you can see, there are several ways to get to the proper insertion point. The methods supported by a parent node are appendChild(), removeChild(), replaceChild() and insertBefore(). Any given node also has the following properties to help you traverse the data tree: firstChild, lastChild, nextSibling, previousSibling and parentNode. What more can you ask for?
Altering properties
Adding new elements is all well and good, but what about setting attributes? The easiest way to do so would be with the following "generic" call:
The setAttribute() method works pretty much as if the text "myAttribute=myValue" had been written inside the element's tag. I say "pretty much" because I've experienced problems with it when I tried formatting everything that way. Style classes, notably, never worked quite right. Because of this, I'd suggest doing all your attribute settings through object properties, like so:
Any standard HTML attribute should have a corresponding object property, although the names don't always match - you might need to do a bit of research to find the right property.
Taking shortcuts
By now you're probably annoyed at the idea that you'll have to write several lines of code for each and every HTML element you need to create. Well, you're not the only one. Several shortcuts are supported by the standard browsers. For instance, the following should work just fine:
Of course, the following approach requires the browser to do some parsing, and is therefore much slower than the official one. But then again, on a modern machine, you're not likely to notice it.
Another gift from Heaven is the "innerHTML" property, which allows you to not only see the HTML representation of what an element contains, but also to edit it on the fly, having it parsed in a similar manner:
There is also an outerHTML property which shows the whole tag corresponding to its element, not just what's contained in it. You can even set its content, just like innerHTML, but I wouldn't touch that with a 10-foot mouse.
Tables
Tables elements (TABLE, TR, TH, TD) are trickier than most elements. For one thing, their inner/outerHTML properties are read-only. They should also be added and removed with a different (but overall nicer) syntax:
insertRow() and insertCell() can take an index stating where the new element goes, or it will simply add it "at the end". Note that you can work with rows at both the table and section levels (deleting the Nth row of the table vs deleting the Nth row of the header), so be careful and know the different between the rowIndex and sectionRowIndex properties.
Final notes and warnings
When dynamically altering your page, be prepared to do a lot of trial and error, and don't forget to test your code on every platform you intend to support.
Finally, if something doesn't work, try a different syntax and/or approach. There's more than one JavaScript way to skin an HTML cat...
The HTML DOM
The Document Object Model is an API which allows you to access and modify the contents of an HTML document through a client-side script such as JavaScript. The current content of the page, as stored in the browser, is represented by a tree of nodes. Any element found within the page (such as a paragraph block ) will correspond to an object (like HTMLParagraphElement), and in turn anything contained within that element (even plain text) will be accessible as childs of that node.
There have been DOMs ever since web browsers started supporting Javascript, but the one used here (and the standard nowadays) is the W3C DOM that is supported (albeit not quite perfectly) by IE 5+ and Mozilla.
One very important thing to understand right off the bat is that the DOM is an interface model. The objects you'll be invoking in your scripts are ways to access the document data, but they're not actual containers. This means you're not going to create them like you create Java objects, and that you'll be able to work with any kind of node on a general level without having to use the specific object for it.
Creating new HTML objects on the fly
The simplest way to generate content is to invoke the createElement() method of your document object.
var myNewParagraph = document.createElement("p");
Simple as that! A new node of type paragraph is created out of thin air, just as if you had entered "<p></p>" in your document source. But where will this new element end up? You'll have to specify that by "inserting" it into your document. For instance:
// Appends the paragraph at the end of the form
document.myForm.appendChild(myNewParagraph);
// Appends it to the beginning of the form
document.myForm.insertBefore(myNewParagraph, document.myForm.firstChild);
// Replaces another element altogether
document.myForm.replaceChild(myNewParagraph, document.getElementById("MyPlaceholder"));
As you can see, there are several ways to get to the proper insertion point. The methods supported by a parent node are appendChild(), removeChild(), replaceChild() and insertBefore(). Any given node also has the following properties to help you traverse the data tree: firstChild, lastChild, nextSibling, previousSibling and parentNode. What more can you ask for?
Altering properties
Adding new elements is all well and good, but what about setting attributes? The easiest way to do so would be with the following "generic" call:
newCell.setAttribute("width", "25px");
newCell.setAttribute("align", "right");
newCell.setAttribute("myAttribute", "myValue");
The setAttribute() method works pretty much as if the text "myAttribute=myValue" had been written inside the element's tag. I say "pretty much" because I've experienced problems with it when I tried formatting everything that way. Style classes, notably, never worked quite right. Because of this, I'd suggest doing all your attribute settings through object properties, like so:
newInput.type = "text";
newInput.style.width = "100%";
newInput.className = "myClass";
newInput.name = "MyUserBox::" + myIndexValue;
newInput.id = newInput.name;
newInput.value = "[Please enter a value]";
Any standard HTML attribute should have a corresponding object property, although the names don't always match - you might need to do a bit of research to find the right property.
Taking shortcuts
By now you're probably annoyed at the idea that you'll have to write several lines of code for each and every HTML element you need to create. Well, you're not the only one. Several shortcuts are supported by the standard browsers. For instance, the following should work just fine:
var myNewParagraph = document.createElement('<p style="text-align: right;">I don\'t see <b>WHY%lt;/b> I should set everything manually' +
'if this approach actually <a href="http://www.macadamian.com/">works</a>!</p>');
Of course, the following approach requires the browser to do some parsing, and is therefore much slower than the official one. But then again, on a modern machine, you're not likely to notice it.
Another gift from Heaven is the "innerHTML" property, which allows you to not only see the HTML representation of what an element contains, but also to edit it on the fly, having it parsed in a similar manner:
var myNewParagraph = document.createElement("p");
myNewParagraph.style.textAlign = "right";
myNewParagraph.innerHTML = 'This can be even <b>COOLER</b> because you don\'t have to create a new object. You could just ' +
'change the property of an old one and it should still <a href="http://www.macadamian.com/">work</a>!';
There is also an outerHTML property which shows the whole tag corresponding to its element, not just what's contained in it. You can even set its content, just like innerHTML, but I wouldn't touch that with a 10-foot mouse.
Tables
Tables elements (TABLE, TR, TH, TD) are trickier than most elements. For one thing, their inner/outerHTML properties are read-only. They should also be added and removed with a different (but overall nicer) syntax:
var myNewRow = myTable.insertRow(); // Add a row at the bottom of the table
var myNewCell = myNewRow.insertCell(); // Add a cell at the end of the row
var myNewFooterRow = myTable.tFoot.insertRow(2); // Add a row before the 3rd one, within the footer
myTable.tBodies[0].rows[0].deleteRow(0); // Delete the 1st cell of the 1st row of the 1st block
insertRow() and insertCell() can take an index stating where the new element goes, or it will simply add it "at the end". Note that you can work with rows at both the table and section levels (deleting the Nth row of the table vs deleting the Nth row of the header), so be careful and know the different between the rowIndex and sectionRowIndex properties.
Final notes and warnings
When dynamically altering your page, be prepared to do a lot of trial and error, and don't forget to test your code on every platform you intend to support.
Finally, if something doesn't work, try a different syntax and/or approach. There's more than one JavaScript way to skin an HTML cat...
Labels: DOM, HTML, Javascript