Friday, May 25, 2007

Bindows Wonderful dispose() Method

Almost anything useful you do with Bindows requires you to add components to other components. This is because you build visual components by creating parent child relationships.

So, if you wanted to build a custom dialog, MyDialog, you might start off with a BiComponent and use that as a container. Then add a BiToolBar to the BiComponent. Of course, you'll need toolbar buttons and menus so you add BiToolBarButtons and BiToolBarMenuButtons to the BiToolBar, etc.

When you're done, you add the BiComponent
container to the BiApplicationWindow.

We've effectively created a hierarchy where every component with the exception of the BiApplicationWindow is a child to another.

One reason why this is significant is when you dispose MyDialog.

All derivatives of BiComponents have access to a dispose() method. It may come directly from the component ( for example BiLabel has its own dispose method ) or directly from BiComponent or from BiObject ( read about dispose() ). As a front-end developer, you don't really care. You just have to remember to call it when you want to mark MyDialog for garbage collection.

So, calling dispose() traverses the children, removes their properties and sets them to null ( actually BiComponent's dispose() calls another method, disposeFields() which makes this happen, but it's all nicely abstracted for you ).

In cases where you only have a hierarchical relationship between your components, you don't need to implement dispose(). This is cool because all you do is call dispose() and the world is your oyster. Most of the time this is all you need to do.

However, if you create new objects in a custom component like MyDialog ( perhaps, we have an array of strings in MyDialog that we're using to temporarily store information ), then we'll need to implement our own dispose().

If we do, we need to make sure that we --

  • Call dispose() in the superclass ( for MyDialog, the superclass is BiComponent and can be done like this - BiComponent.prototype.dispose.call(this) ); this effectively propagates the disposal up the prototype chain
  • Call disposeFields(); disposeFields accepts a variable number of string arguments where each string is a property name; calling disposeFields removes ( i.e. deletes ) the property and disposes them if it's a Bindows objects; you can read about it here.
That's it. dispose() is pretty wonderful.
Have fun!

Saturday, May 19, 2007

Simple Fun Faders

If you're using pre-Bindows 3.0 Beta, here's a simple way to do component fading in Bindows.

When Bindows 3.0 is released, it'll do a lot more including resize and relocation animations. Hopefully, this little fader will wet your appetite for that release.

For a preview of the animation capabilities found in Bindows 3.0, check out my earlier writeup.

Here's an example of what the fader object, MyFader, can do. We're cycling through a six photo collection of 1/24 model cars from Tamiya --




Fading is achieved by dynamically changing a component's opacity.
One of the great things about using a toolkit like Bindows is that you don't have to worry about browser specifics like setting filter ( IE ) or -moz-opacity ( Firefox ).

All of that is abstracted for you.
With Bindows, this is easy to do by repeatedly calling a BiComponent's setOpacity() method.

You basically setup a timer and then at intervals call setOpacity() incrementing by 0.1 until you reach 1 ( "fade in" ) or decrementing by 0.1 until you reach 0 ( "fade out" ).

There's not much coding behind it. Here's what the reusable object, MyFader looks like --



MyFader isn't a component. It doesn't need to be because MyFader doesn't need to be rendered, but it does need to dispatch events.

So, we make it a BiEventTarget. When a fade starts, MyFader dispatches the "fadestart" event and when it ends, MyFader dispatches the "fadeend" event.

Use these two events to coordinate fading. This is what we're doing when we're swapping the six model cars. You can view that source here.

Not all fading is aesthetic. We can also use fading to provide visual feedback. Here's an example of a cascading menu with fade-ins when a list is updated. To see it in action, select an item --



Note that the cascading menus are lists and each item is a BiListItem, but we don't fade each item. Instead we fade the entire list. It's always better to animate the container/parent rather than each child ( much like event delegation where you handle events at the parent level ).

It's much easier to coordinate by fading one component rather than many. JavaScript after all uses a single threaded model.

You can get all the code here including the MyFader object.

To see the model car example, go here.
The cascading list example is here.

Have fun!

Sunday, May 06, 2007

On-demand JavaScript :: IE6/7 and Firefox 2.x

Last year, there was a lot of talk about on-demand JavaScripting. A good bit is found on Michael Mahemoff's Ajax Patterns Wiki. If you've never heard of it, you can read Michael's primer here.

Basically, on-demand JavaScripting helps you lazy load objects. When you need it, load it. It's simple.



You can run it here.

When you click on the "Load JavaScript" button, we're dynamically loading a JavaScript library by creating a script tag and then setting the src.

Then, the library loads and all the JavaScript is implicitly evaluated ( no eval is called ). You'll see an alert message appear saying that you've successfully loaded the JavaScript.

Close it and then the "Call method from new load" button is enabled. Click it and you'll see another alert message showing you the current page's HTML.

The whole exercise is to demonstrate that --

  • You can load JavaScript dynamically by creating a script tag and then setting src
  • You don't need to "eval" it

In fact, you can use the technique to "hack around" cross-domain issues and avoid using a proxy altogether. You can load JavaScript from any domain.

Though we can have lively debates on whether that's a good or bad thing, we're more interested in how the JavaScript was loaded.

From all the on-demand JavaScript examples that I've seen, they involve loading an external library, but what if we don't have an external library?

What if we just want to modify the body of the script tag to include new JavaScript? We might be loading the JavaScript from an iframe or some other external source and then slap it between script tags.

As it turns out, we can load JavaScript this way too, but we'll have to do it a specific way or it won't work in IE6 or IE7 ( I didn't try it for earlier versions of IE ) and like using "src", you don't need to explicitly evaluate the JavaScript.

Naturally, you might think that this works ( especially, since there's a general agreement that using innerHTML is the best and fastest way to dynamically update HTML ) --



Here's the example.

Try it in Firefox 2.x and it'll work. Then, try it in IE. We'll get the fabulously terse error message, "Unknown runtime error."

So, we try something along the same lines, but slightly different. Instead of updating with innerHTML, we'll create a text node using the string of JavaScript and then append that node as a child of the script element.



Here's the example.

IE chokes again, but this time with a bit more detail, "Unexpected call to method or property access". Sigh. It's beginning to look like one of those "many hours to solve" IE problems.

Fortunately, after a few more efforts, there's a solution. We'll use the "text" property found in the script node. Apparently, this is a Microsoft extension to the W3C DOM which works in Firefox 2.x. The best reference that I could find was here.



Here's the example that works in IE6/7 and Firefox 2.x.

We're done!

By the way, it's stuff like this that make using a JavaScript toolkit like Bindows worthwhile.

Have fun!