The LoginForm
component is a login form that allows a
password manager in the web browser to remember and later automatically fill
in the username and password. This commonly used functionality does not work
with regular Vaadin components and is a common problem with Ajax
applications.
// A wrapper with a caption for the login form Panel loginPanel = new Panel("Login"); loginPanel.setWidth("250px"); LoginForm login = new LoginForm(); loginPanel.addComponent(login);
The resulting form is shown in Figure 5.69, “The LoginForm
Component”.
The LoginForm
uses static HTML inside an
iframe
element to enable the functionality. The component
provides a default implementation of the static HTML; if you want to change
the layout, you need to reimplement the
getLoginHtml()
method.
The login form has by default 100%x100% relative size, taking all the space
given by the containing layout. You may set the size to fixed values, but not
undefined in either direction, because the contained iframe
element takes all of this size (it also has 100%x100% size). How the actual
form uses this space depends on the static HTML. Giving too little space for
the form results in scroll bars.
Customization of the login form is necessary, for example, if you need to
change the layout or internationalize the form. Customization is done by
overriding the getLoginHtml()
method, which
returns the static HTML of the form. The customization layer is very
"unvaadin"-like, and at best hack-ish, but dictated by the form management
in browsers.
Let us look at a custom login form that lets the user of the form to give the field captions:
class MyLoginForm extends LoginForm { String usernameCaption; String passwordCaption; String submitCaption; public MyLoginForm(String usernameCaption, String passwordCaption, String submitCaption) { this.usernameCaption = usernameCaption; this.passwordCaption = passwordCaption; this.submitCaption = submitCaption; }
Then we override the method that generates the static HTML for the form:
@Override protected byte[] getLoginHTML() { // Application URI needed for submitting form String appUri = getApplication().getURL().toString() + getWindow().getName() + "/"; String x, h, b; // XML header, HTML head and body
The XML header is needed for the validity of the XHTML page:
x = "<!DOCTYPE html PUBLIC \"-//W3C//DTD " + "XHTML 1.0 Transitional//EN\" " + "\"http://www.w3.org/TR/xhtml1/" + "DTD/xhtml1-transitional.dtd\">\n";
Notice that it is important to have a newline (\n
) at
the end of the XML header line.
The HTML header part contains JavaScript definitions that handle submitting the form data. It also copies the style sheet references from the parent window.
h = "<head><script type='text/javascript'>" + "var setTarget = function() {" + " var uri = '" + appUri + "loginHandler';" + " var f = document.getElementById('loginf');" + " document.forms[0].action = uri;" + " document.forms[0].username.focus();" + "};" + "" + "var styles = window.parent.document.styleSheets;" + "for(var j = 0; j < styles.length; j++) {\n" + " if(styles[j].href) {" + " var stylesheet = document.createElement('link');\n" + " stylesheet.setAttribute('rel', 'stylesheet');\n" + " stylesheet.setAttribute('type', 'text/css');\n" + " stylesheet.setAttribute('href', styles[j].href);\n" + " document.getElementsByTagName('head')[0]" + " .appendChild(stylesheet);\n" + " }" + "}\n" + "function submitOnEnter(e) {" + " var keycode = e.keyCode || e.which;" + " if (keycode == 13) {document.forms[0].submit();}" + "}\n" + "</script>" + "</head>";
The HTML body element contains the actual form. Notice that it is contained
within an inner iframe
. The form and the button must
have JavaScript calls to submit the form content.
b = "<body onload='setTarget();'" + " style='margin:0;padding:0; background:transparent;'" + " class='" + ApplicationConnection.GENERATED_BODY_CLASSNAME + "'>" + "<div class='v-app v-app-loginpage'" + " style='background:transparent;'>" + "<iframe name='logintarget' style='width:0;height:0;" + "border:0;margin:0;padding:0;'></iframe>" + "<form id='loginf' target='logintarget'" + " onkeypress='submitOnEnter(event)'" + " method='post'>" + "<table>" + "<tr><td>" + usernameCaption + "</td>" + "<td><input class='v-textfield' style='display:block;'" + " type='text' name='username'></td></tr>" + "<tr><td>" + passwordCaption + "</td>" + " <td><input class='v-textfield'" + " style='display:block;' type='password'" + " name='password'></td></tr>" + "</table>" + "<div>" + "<div onclick='document.forms[0].submit();'" + " tabindex='0' class='v-button' role='button'>" + "<span class='v-button-wrap'>" + "<span class='v-button-caption'>" + submitCaption + "</span>" + "</span></div></div></form></div></body>";
Then combine and return the page as a byte array.
return (x + "<html>" + h + b + "</html>").getBytes(); }
We can use the custom login form as follows:
MyLoginForm loginForm = new MyLoginForm("Name of the User", "A passing word", "Login Me Now");
The customized LoginForm
is shown in Figure 5.70, “Customizing the LoginForm
”.
.v-customcomponent {} .v-customcomponent .v-embedded {} .v-app-loginpage {} .v-app-loginpage .v-textfield {} .v-app-loginpage .v-button {}
The LoginForm
component is a purely server-side
component that extends CustomComponent
and
therefore has a v-customcomponent
base style. If you
wish to do any styling for the component, you should give it a custom
style name to distinguish it from the regular
CustomComponent
.
The component contains an iframe
in an element with
v-embedded
style. The other styles are defined in the
static HTML code returned by the getLoginHTML()
method. The default implementation reuses the styles of the
TextField
and Button
components for the input fields and the button, that is,
v-textfield
and v-button
. The root
element has the same v-app
style as a regular Vaadin
application would have, and an additional
v-app-loginpage
style.
... + "<div class='v-app v-app-loginpage' style=\"background:transparent;\">" ... + "<input class='v-textfield' ... ... + "<div><input class='v-textfield' ... ... <div ... class='v-button' role='button'>