-
Get a monthly update on best practices for delivering successful software.
It's Wednesday, and you have had it up to here with the weather. There's nothing on TV and you've gone out for so many walks, you can't even get your dog to look you in the eye without him hiding under the kitchen table. You have time on your hand, so you decide you want to see how easy it is to set the status bar text when hovering over a link in GWT (why? don't ask why, let's just pretend for a second). You settle on three possible approaches
1) Extend GWT's 'HyperLink' class. This first approach requires registering the 'mouseover' event and writing a bit of Javascript to be invoked when the event fires:
class MyHyperlink extends Hyperlink { public MyHyperlink { super(); // Register my interest in 'mouseover' events sinkEvents(Event.ONMOUSEOVER);
addClickListener(new ClickListener() { public void onClick(Widget sender) { // Some action... foo.submitSomething(); } }); } // Overrides base class method public void onBrowserEvent(Event event) { if (DOM.eventGetType(event) == Event.ONMOUSEOVER) { setStatusText("my custom text"); } else { // Make sure you invoke 'super', as mouse down events travel through here as well. super.onBrowserEvent(event); } }
// JSNI call required to execute whatever we're interested in.. // In this case, setting status text, but it could be anything else private static native void setStatusText(String text) /*-{ $wnd.status=text; }-*/;}
The problem with this option is that it doesn't exactly work as intended. 'setStatusText()' gets invoked at the right time, but if you look at the status bar in Firefox, the status bar text doesn't update. I'm not sure why exactly- it might just be my quick & dirty JavaScript, but even if everything worked as intended I'm still not happy with this solution. I mean, look at the code-- I have to pull in many ingredients just to complete a simple task-- DOM.eventGetType()? Writing a JSNI method definition? Registering the right Event type, overriding default behavior, etc.? Not the most readable code and not the kind of code I would lightly hand off to someone just getting their feet wet with GWT.
2) Another approach. Let's try to wrap Hyperlink in a FocusPanel. You still need a JSNI method to trigger the JavaScript code (see above), but the Hyperlink stays the same while the dynamic behavior is contained entirely in the FocusPanel. Now one good thing about FocusPanel is that it allows one to easily add a MouseListener to define the behavior you're interested in (in this case, 'onMouseEnter()'). This means we don't need to explicitly register the Event type we're interested in.
The only problem with the FocusPanel approach is... that it doesn't really give us what we want. The FocusPanel instance has dimensions around the link, so the area of interest can actually be larger than the link itself. Firebug verifies this. You can set or tweak widths to get the FocusPanel's area to match the link area, but I won't bother to include a code sample because, well, if you haven't been able to tell already, it just isn't worth it. Time is money. Moving on...
3) Hey I got an idea! How about we ditch 'Hyperlink' completely & roll our own? The dog just got up to scratch the front door and you don't have much time. C'mon, it'll be fun! As it turns out, the direct approach turns out to be both the most expedient, as well as the shortest to implement in code:
class MyHyperlink extends HTML { public MyHyperlink(String name) { setHTML( "<a onmouseover=\"status='';return true;\" href=\"#x\">" + name + "</a>" ); addClickListener(new ClickListener() { public void onClick(Widget sender) { // Whatever you would have done in Hyperlink's clickListener... foo.submitSomething(); } }); }}
Done. Everything works.
Why did GWT's Hyperlink let us down in this case? This is where things start to break down in usefulness for me as far as GWT is concerned. It's nice and seamless until it stops being so-- and then it will remind us of just how painful it is to step behind the velvet curtain-- you get lost in the abstractions. Now granted, I'm not crazy about any of these approaches, and as someone will probably suggest, there's nothing stopping me from taking option (1) or (2) and creating an abstract class on top of what GWT provides and inheriting that base class in my own custom Hyperlink instances... but how annoying would that be to debug in the event that my custom JavaScript stops working? No thanks. Option (3) tells me everything I need to know without traversing up and down an additional class hierarchy. It's also easier to communicate to others, even if it is a bit counterintuitive to extend 'HTML' instead of 'Hyperlink' just to make this particular link.
UPDATE (11/29)
There turns out to be a better alternative to (2), using a 'Label' and styling it to look like a Hyperlink. The benefit here is that, unlike Hyperlink, 'Label' allows you to capture MouseListener events. Still not great, but at least it avoids the issues with FocusPanel. You're still left using JSNI to actually implement your JS however..
Related posts:
Topics: GWT