Sunday, August 21, 2005

Submit Query? What Query?

WARNING: This blog entry was imported from my old blog on blogs.sun.com (which used different blogging software), so formatting and links may not be correct.


You've just built a web form. Users type in a message in a text area, then
click on a Submit button to send the message. You run the application, and
.... what? The browser has changed your Submit button into "Submit Query"!
What Query? There's no query here!


We've had some bugs filed against Creator on this. In some browsers
(notably Mozilla and Firefox), submit buttons change their labels into
"Submit Query".










Form built in CreatorForm deployed and shown in Firefox







This is a symptom of a much deeper problem, which I'd like to discuss in this
blog entry.



Let's say you drop a button. This will create a button, but no label has
been set on the button - look at its text property in the property sheet.
A button component with no label set will render to this HTML:



<input type="submit" />


This will not display a blank button (which you can achieve by adding value="") -
all browsers will pick a default label! Internet Explorer, Safari, and most other browsers use the label
"Submit". Mozilla/Firefox on the other hand, uses "Submit Query" - which I think is an odd choice.
Creator has to choose something - it cannot show the "right" thing since
that will depend on which browser the application is viewed with - and
there could be a different browser for each user accessing the same web page!



There is an easy fix to this - go and set the value of the button to
a specific string, like "Submit" (or Send or Order or Lookup or whatever
makes sense on the current page.) In recent dev-builds of Creator, we've
made some changes such that you don't end up in the scenario of an unset
button as easily.



But this raises the question of why we don't set the text to some default
value automatically. For a button, we probably could (and have - see above).
But for components like Static Text, we can't. The reason for that is that
it would make it very hard to set the text value from code!



Let's say you've dropped a Static Text component on the page, and you've
set its text attribute to "Hello World", so your JSP contains this:



<ui:staticText text="Hello World" binding="#{Page1.staticText1}" />


Now you're trying to do something clever in your Java code, for example
setting the text to include the name of the logged in user:



// In Page1's constructor
staticText1.setText("Hello " + getUserName());


This won't work! Note that there is a conflict of definitions here - the
JSP says that the text should be "Hello World", and the page constructor
says that the text should be "Hello Tor". And the JSF spec says:


The JSP attribute wins.


You might try to move the above code into the new init()
method, or even the prerender() method, but that won't help -
the JSP attribute setting will still win the first time the page is displayed.



The only way to get your Java code to be able to set the static text, is
to remove the attribute setting from the JSP.



And this is precisely why, when you drop a Static Text component on a page,
we don't automatically set it to some "default" value. We could (and in
early devepment versions did), but most users are not aware of
the JSP vs Java conflict, and did not understand why their Java code which
set labels didn't work. Granted, in most cases you don't want to set the
label from code - you'll use value binding expressions instead. But it's
an important enough scenario that we don't want to get people too frustrated
when they run into this.



So, the solution we've settled on is using what we call shadow text*.
When a component is rendered at designtime, and it's missing a value that's
vital (but one we don't want to set to avoid conflicts), we render the component text in
bold italics instead, using a default label such as the type of the component.
This shows you that there is a component here (since otherwise a
static text with no actual text is rather hard to read...) and allows you manipulate
it. But it's also obvious, or at least we hope so, that the component is not
configured with normal text. As soon as you set a specific value for the property,
it's rendered in the usual way.



Here's how this looks for buttons, static texts and hyperlinks - the first row
shows components with shadow text:






*: I don't think this is "official" terminology - it was simply called that
in a recent bug report, and once I realized what they were describing I figured
it's as good a name as any. Before that I had called it "uninitialized/bold/italic text"
which is a mouthful...


2 comments:

  1. I was testing this.
    I added the statement:
    staticText1.setText("Hello " + getUserName());
    to my constructor.
    When I clicked on the Design: Preview in Browser button - I still saw the shadow text.
    I agree with you, bindings are the way to go, but I was just curious on how this worked.
    Thanks.

    ReplyDelete
  2. "Preview In Browser" is special - it does NOT do a deployment. That's why it's nearly instant. It's great for tweaking visual design stuff. But it's showing you things like shadow text, as well as "123" and "abc" in databound controls rather than actual data.
    The whole issue around JSP versus values set from the page bean constructor only comes up with actual deployment, when a true JSF lifecycle is performed.

    ReplyDelete