tags:

views:

473

answers:

1

I was just wondering if I could display errors on a separate jsp fragment.

I have a requirement where I have to display hyperlink errors (i.e clicking any error would focus to respective input field)

For example, if I have a form and there are 20 fields, then I am suppose to use 20 times <t:message> tag. I know I can use single <t:messages> tag for all errors, but I have a requirement to focus individual field and I am not sure How we implement this using <t:messages>. However onclick event from <t:message> tag solves my issue but using 20 tags leave a big space under heading and page does not look nice.

So I was thinking if I could put all the errors tag on separate jsf fragment and later do jsp include on the main page.

I tried, but error messages are not being displayed.

Does any body have any idea, how do I accomplish this?

+1  A: 

I take it that t:message is the Tomahawk control.


Perhaps a custom JSF control would fit the bill - sometimes you can't manipulate the existing tags to get the exact output you want.

You can get the messages from the FacesContext. The control's renderer writes the HTML to the page:

package custom;    /*imports omitted*/

public class CustomErrorRenderer extends Renderer {

  @Override @SuppressWarnings("unchecked")
  public void encodeEnd(FacesContext context,
      UIComponent component) throws IOException {
    ResponseWriter writer = context.getResponseWriter();
    writer.startElement("div", component);
    writer.writeAttribute("id", component.getClientId(context), "id");
    writer.writeAttribute("style", "color: red", null);
    writer.startElement("ul", null);
    Iterator<String> clientIds = context.getClientIdsWithMessages();
    while (clientIds.hasNext()) {
      String clientId = clientIds.next();
      Iterator<FacesMessage> messages = context.getMessages(clientId);
      if (!messages.hasNext()) { continue; }
      String javaScript = "var field = document.getElementById('"
          + clientId + "');" + "if(field == null) return false;"
          + "field.focus(); return false;";
      writer.startElement("li", null);
      writer.startElement("a", null);
      writer.writeAttribute("onclick", javaScript, null);
      writer.writeAttribute("href", "#", null);
      while (messages.hasNext()) {
        writer.writeText(messages.next().getSummary(), null);
      }
      writer.endElement("a");
      writer.endElement("li");
    }
    writer.endElement("ul");
    writer.endElement("div");
  }
}

This renderer is defined in faces-config.xml:

  <render-kit>
    <description>add to base HTML_BASIC renderkit</description>
    <display-name>HTML_BASIC</display-name>
    <render-kit-id>HTML_BASIC</render-kit-id>
    <renderer>
      <display-name>CustomErrorRenderer</display-name>
      <component-family>javax.faces.Output</component-family>
      <renderer-type>custom.CustomErrorRenderer</renderer-type>
      <renderer-class>custom.CustomErrorRenderer</renderer-class>
    </renderer>
  </render-kit>

Since the sample component doesn't require any special attributes or logic, it can be defined using the existing UIOutput component:

  <component>
    <display-name>CustomErrorMessages</display-name>
    <component-type>custom.Errors</component-type>
    <component-class>javax.faces.component.UIOutput</component-class>
  </component>

To integrate the control with JSPs, you also need to provide a tag class:

package custom;

import javax.faces.webapp.UIComponentELTag;

public class CustomErrorTag extends UIComponentELTag {

  @Override
  public String getComponentType() {
    return "custom.Errors";
  }

  @Override
  public String getRendererType() {
    return "custom.CustomErrorRenderer";
  }

}

This is defined in a TLD file:

<?xml version="1.0" encoding="UTF-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
  version="2.1">
  <tlib-version>1.0</tlib-version>
  <short-name>custom</short-name>
  <uri>http://custom&lt;/uri&gt;
  <tag>
    <name>errors</name>
    <tag-class>custom.CustomErrorTag</tag-class>
    <body-content>empty</body-content>
  </tag>
</taglib>

If you wish to include this via a separate page, this sample JSP holds the control:

<%@ taglib prefix="custom" uri="http://custom"%&gt;
<custom:errors />

This JSP includes it:

<%@taglib uri="http://java.sun.com/jsf/html" prefix="h"%><%@taglib
  uri="http://java.sun.com/jsf/core" prefix="f"%>
<html>
<body>
<f:view>
  <h:form>
    <h:inputText>
      <f:validateLength minimum="6" maximum="10" />
    </h:inputText>
    <h:inputText>
      <f:validateLength minimum="4" maximum="6" />
    </h:inputText>
    <h:commandButton />
  </h:form>
  <f:subview id="errs">
    <jsp:include page="customerrors2_inc.jsp" />
  </f:subview>
</f:view>
</body>
</html>


Yep, it all seems too complicated, but at least you could use this to create a JAR with a reusable custom control that can be dropped into any JSF application.

You could probably achieve similar results using a managed bean and a repeating control, but then you have to start worrying about delaying the list creation until after the validation phase (which will probably involve a phase listener).

McDowell
Thanks for your time. This code works great. But if I put some validation in my update form and click my update method, then <custom:errors> gives me the error with hyper link(This is what I wanted) but at the mean time it clears all the vaue of my update form. For e.g if there are 20 update input fields then it clears all of them.But if I replace the <custom:errors> with <h:messages> then however its gives similar error but the form doesnot gets cleared. Could you please fix this.Thanks