Thursday, March 1, 2012

The OmniFaces project has started

Together with my colleague Arjan Tijms, who you shall probably also know from stackoverflow.com and from the majority of the JSF related blogs at jdevelopment.nl, the project OmniFaces has been started.

OmniFaces is a library for JSF 2.x that focusses on utilities that ease everyday tasks. An important design goal will be to have as few dependencies as possible (so far, it only requires JSF 2.0, EL 2.1 and Servlet 2.5 APIs which is already minimally available in a bit modern container serving a JSF 2.0 web application) and to be minimally invasive. As such, OmniFaces should principally integrate perfectly well with most other JSF libraries. Characteristic of OmniFaces will be that it will not just be about components, but instead will have an equally strong focus on providing utility classes for working with the JSF API from Java code.

OmniFaces is still under development, but so far among others the following components are available:

Tree

The <o:tree> allows you to render a markup-less tree wherein you have the freedom to declare markup, such as <ul><li>. You can give every individual node level its own markup. Here's an example of a menu wherein the first level (with a value of 0) is been rendered as a <h3> and all of its children are rendered as nested <ul><li>.


<o:tree value="#{treeDemo.menu}" var="page">
    <o:treeNode level="0">
        <o:treeNodeItem>
            <h3><a href="#{page.url}">#{page.title}</a></h3>
            <o:treeInsertChildren />
        </o:treeNodeItem>
    </o:treeNode>
    <o:treeNode>
        <ul>
            <o:treeNodeItem>
                <li>
                    <a href="#{page.url}">#{page.title}</a>
                    <o:treeInsertChildren />
                </li>
            </o:treeNodeItem>
        </ul>
    </o:treeNode>      
</o:tree>

The <o:treeNode> gives you the space to write markup around a single tree node. The level attribute specifies for which level the tree node should be rendered. In the above example, the content of <o:treeNode level="0"> will only be rendered for all tree nodes of the first level and the content of the level-less <o:treeNode> will be rendered for tree nodes of all other levels. The <o:treeNodeItem> gives you the space to write markup around every single child of the current tree node. The <o:treeInsertChildren> indicates the insertion point where the <o:treeNode> associated with the child's level must be rendered.

Here's what the Page bean look like:

public class Page {

    private String url;
    private String title;

    public Page(String url, String title) {
        this.url = url;
        this.title = title;
    }

    // Getters/setters.
}

Here's what the backing bean look like:

public class TreeDemo {

    private TreeModel<Page> menu;

    @PostConstruct
    public void init() {
        menu = new ListTreeModel<Page>();
        TreeModel<Page> general = menu.addChild(new Page("general.xhtml", "General"));
        TreeModel<Page> components = menu.addChild(new Page("components.xhtml", "Components"));
        TreeModel<Page> help = menu.addChild(new Page("help.xhtml", "Help"));

        TreeModel<Page> aboutUs = general.addChild(new Page("aboutus.xhtml", "About us"));
        TreeModel<Page> location = general.addChild(new Page("location.xhtml", "Location"));
        TreeModel<Page> contact = general.addChild(new Page("contact.xhtml", "Contact"));

        TreeModel<Page> tree = components.addChild(new Page("tree.xhtml", "Tree"));
        TreeModel<Page> validators = components.addChild(new Page("validators.xhtml", "Validators"));

        TreeModel<Page> allOrNone = validators.addChild(new Page("allOrNone.xhtml", "All or None"));
        TreeModel<Page> oneOrMore = validators.addChild(new Page("oneOrMore.xhtml", "All or None"));
        TreeModel<Page> allEqual = validators.addChild(new Page("allEqual.xhtml", "All equal"));
        TreeModel<Page> allUnique = validators.addChild(new Page("allUnique.xhtml", "All unique"));
    }

    public TreeModel<Page> getMenu() {
        return menu;
    }

}

Here's what the generated HTML output look like:


<h3><a href="general.xhtml">General</a></h3>
<ul>
    <li>
        <a href="aboutus.xhtml">About us</a>
    </li>
    <li>
        <a href="location.xhtml">Location</a>
    </li>
    <li>
        <a href="contact.xhtml">Contact</a>
    </li>
</ul>

<h3><a href="components.xhtml">Components</a></h3>
<ul>
    <li>
        <a href="tree.xhtml">Tree</a>
    </li>
    <li>
        <a href="validators.xhtml">Validators</a>
        <ul>
            <li>
                <a href="allOrNone.xhtml">All or None</a>
            </li>
            <li>
                <a href="oneOrMore.xhtml">All or None</a>
            </li>
            <li>
                <a href="allEqual.xhtml">All equal</a>
            </li>
            <li>
                <a href="allUnique.xhtml">All unique</a>
            </li>
        </ul>
    </li>
</ul>

<h3><a href="help.xhtml">Help</a></h3>

For the fans of chaining, it's also possible to create the tree model by chaining:


@PostConstruct
public void init() {
    menu = new ListTreeModel<Page>();
    menu.addChild(new Page("general.xhtml", "General"))
            .addChild(new Page("aboutus.xhtml", "About us")).getParent()
            .addChild(new Page("location.xhtml", "Location")).getParent()
            .addChild(new Page("contact.xhtml", "Contact")).getParent().getParent()
        .addChild(new Page("components.xhtml", "Components"))
            .addChild(new Page("tree.xhtml", "Tree")).getParent()
            .addChild(new Page("validators.xhtml", "Validators"))
                .addChild(new Page("allOrNone.xhtml", "All or None")).getParent()
                .addChild(new Page("oneOrMore.xhtml", "All or None")).getParent()
                .addChild(new Page("allEqual.xhtml", "All equal")).getParent()
                .addChild(new Page("allUnique.xhtml", "All unique")).getParent().getParent().getParent()
        .addChild(new Page("help.xhtml", "Help"));
}

Multi field validators

There are five special validators for multiple input fields:

Here is an extract of relevance from the ValidateMultipleFields javadoc:

General usage of all multiple field validators

This validator must be placed inside the same UIForm as the UIInput components in question. The UIInput components must be referenced by a space separated collection of their client IDs in the components attribute. This validator can be placed anywhere in the form, but keep in mind that the components will be validated in the order as they appear in the form. So if this validator is been placed before all of the components, then it will be executed before any of the component's own validators. If this validator fails, then the component's own validators will not be fired. If this validator is been placed after all of the components, then it will be executed after any of the component's own validators. If any of them fails, then this validator will not be exeucted. It is not recommended to put this validator somewhere in between the referenced components as the resulting behaviour may be confusing. Put this validator either before or after all of the components, depending on how you would like to prioritize the validation.

<o:validateMultipleFields id="myId" components="foo bar baz" />
<h:message for="myId" />
<h:inputText id="foo" />
<h:inputText id="bar" />
<h:inputText id="baz" />

In an invalidating case, all of the referenced components will be marked invalid and a faces message will be added on the client ID of this validator component. The default message can be changed by the message attribute. Any "{0}" placeholder in the message will be substituted with a comma separated string of labels of the referenced input components.

<o:validateMultipleFields components="foo bar baz" message="{0} are wrong!" />

The faces message can also be shown for all of the referenced components using showMessageFor="@all".

<o:validateMultipleFields components="foo bar baz" message="This is wrong!" showMessageFor="@all" />
<h:inputText id="foo" />
<h:message for="foo" />
<h:inputText id="bar" />
<h:message for="bar" />
<h:inputText id="baz" />
<h:message for="baz" />

The showMessageFor attribute defaults to @this. Other values than @this or @all are not allowed.

Let's look how <o:validateEqual> is useful for password confirmation validation, for example:


<h:panelGrid columns="3">
    ...

    <h:outputLabel for="password" value="Enter password" />
    <h:inputSecret id="password" value"#{register.user.password}" redisplay="true" required="true"
        requiredMessage="Please enter password" />
    <h:panelGroup>
        <h:message for="password" />
        <h:message for="equal" />
    </h:panelGroup>
    
    <h:outputLabel for="confirm" value="Confirm password" />
    <h:inputSecret id="confirm" redisplay="true" required="true"
        requiredMessage="Please confirm password" />
    <h:panelGroup>
        <h:message for="confirm" />
        <o:validateEqual id="equal" components="password confirm" 
            message="Passwords are not equal" />
    </h:panelGroup>

    ...
</h:panelGrid>

<h:commandButton value="submit" action="#{register.submit}">
    <f:ajax execute="@form" render="@form" />
</h:commandButton>

If none of the fields are filled out, the component's own required validators will fire and the equal validator will not fire. If both fields are filled out and not equal, then the equal validator will fire and show the message on the <h:message> associated with its own ID.

Here's another example how the <o:validateOrder> is useful for start date - end date validation, for example:


<h:panelGrid columns="3">
    ...

    <h:outputLabel for="startDate" value="Start date" />
    <h:inputText id="startDate" value"#{booking.reservation.startDate}" required="true"
        requiredMessage="Please enter start date"
        converterMessage="Please enter format yyyy-MM-dd">
        <f:convertDateTime pattern="yyyy-MM-dd" />
    </h:inputText>
    <h:message for="startDate" />
    
    <h:outputLabel for="endDate" value="End date" />
    <h:inputText id="endDate" value"#{booking.reservation.endDate}" required="true"
        requiredMessage="Please enter end date"
        converterMessage="Please enter format yyyy-MM-dd">
        <f:convertDateTime pattern="yyyy-MM-dd" />
    </h:inputText>
    <h:panelGroup>
        <h:message for="endDate" />
        <h:message for="order" />
        <o:validateOrder id="order" components="startDate endDate" 
            message="End date must be after start date" />
    </h:panelGroup>

    ...
</h:panelGrid>

<h:commandButton value="submit" action="#{booking.submit}">
    <f:ajax execute="@form" render="@form" />
</h:commandButton>

If none of the fields are filled out or filled out with invalid date format, then the component's own converter and required validators will fire and the order validator will not fire. If both fields are filled out with valid date formats but not in order, then the equal validator will fire and show the message on the <h:message> associated with its own ID.

Select items converter

The SelectItemsConverter, written by Arjan Tijms, allows the developer to use complex objects like entities as the value of <f:selectItems> without the need to implement a custom converter which calls some DAO/service method to obtain the associated entity by for example its ID as submitted string value. The converter converts the submitted value based on the already-available values in the <f:selectItems>. The conversion is by default based on the toString() representation of the entity which is supposed to already return an unique enough representation.

All you need to do is to specify the converter as converter="omnifaces.selectItemsConverter".


<h:selectOneMenu value="#{bean.selectedEntity}" converter="omnifaces.SelectItemsConverter">
    <f:selectItems value="#{bean.availableEntities}" var="entity" 
        itemValue="#{entity}" itemLabel="#{entity.someProperty}" />
</h:selectOneMenu>

Where the bean can look like this:


private Entity selectedEntity;
private List<Entity> availableEntities;

// Getters+setter.

You can always extend the SelectItemsConverter class to offer a custom getAsString() implementation which returns for example entity.getId() instead of the default entity.toString() which may in case of some entities be unnecessarily long.

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.FacesConverter;

import org.omnifaces.converter.SelectItemsConverter;

@FacesConverter("entitySelectItemsConverter")
public class EntitySelectItemsConverter extends SelectItemsConverter {

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value) {
        Long id = ((Entity) value).getId();
        return (id != null) ? String.valueOf(id) : null;
    }

}

Note that you do not need to override getAsObject()!

To get it to work, just change the converter attribute in the above example to point to your custom converter in question.


<h:selectOneMenu value="#{bean.selectedEntity}" converter="entitySelectItemsConverter">
    <f:selectItems value="#{bean.availableEntities}" var="entity" 
        itemValue="#{entity}" itemLabel="#{entity.someProperty}" />
</h:selectOneMenu>

Notice

The OmniFaces component library is still in early development! Although we strive to full backwards compatibility, there will until the first stable 1.0 release be no guarantee that no changes will be made to the class names, tag names, attibute names, EL function names, the general behaviour and so forth.

This blog is purely informal so that you can check/try them out and/or leave feedback. Important issues can be reported to the OmniFaces issue list.

12 comments:

Oleg Varaksin said...

Congrats to a new project! Very useful stuff. Unfortunately, but a dependency to Servlet 3.0 is a main success killer. thousands+ of apps are running in "old" containers yet by reason of customers environment. Where this dependency is needed exactly?

BalusC said...

The project is currently built against Java EE 6 Web profile. As far Servlet 3.0 is concerned, only the constant field values of RequestDispatcher are been used in the full ajax exception handler. It's in theory possible to hardcode them ourself, so that we can continue with a Servlet 2.5 requirement. I'd have to discuss about this with Arjan first.

Oleg Varaksin said...

Yes, discuss this with Arjan please. This is important IMHO, that Servlet 2.5 is supported as well. Also Java 5 support is required (build with java target 1.5.) because JSF standard (and PrimeFaces of course :-)) support this.

Thanks in advance.

BalusC said...

Servlet 3.0 dependency has been removed and it is Servlet 2.5 now.

Gimby said...

Neat project, I see plenty of potential in this.

Thang Pham said...

Hi BalusC: I am hacking at OmniFaces sources now, and I have a question. I am a bit new in JSF, so please forgive me if my question is rather silly. So I am looking at org.omnifaces.component.tree.Tree, and using the exact JSF code you provide for o:tree on your blog. I put break points in Tree.java, and I see it stop at

public void setVar(String var) {
getStateHelper().put(PropertyKeys.var, var);
}

the value of the parameter var is "page", which is what we specify in attribute "var". So I wonder how does this method get called. Is it get called because of Tree is extends NamingContainer, and since the attribute is "var", JSF automatically call "setVar" and "getVar"? Or is there some special controller somewhere in your code that you handle this? Thank you very much BalusC. Again, fascinating neat project, I plan to read through all the sources.

BalusC said...

@Thang: JSF/Facelets handles this implicitly and transparently. When you specify a tag attribute in the view, then JSF/Facelets will look for a setter on the component class and if it is available then invoke it. If there wasn't a setter, then the attribute will just end up in the getAttributes() map (the "real" attributes also, but you get the point).

By the way, this also applies to standard components. All custom attributes (also the ones added by f:attribute tag) end up in the getAttributes() map. The component will however do nothing special with it.

Thang Pham said...

Thank you for your answer BalusC. By the way, sick sick awesome implementation on the ajax exception handler. I tried it today, and it is great. The ability to show the error without redirect, so if the user click "back", it still retain the form attribute. Great thinking man.

Unknown said...

Looking forward to testing it. Congratulations BalusC.

Naresh Kavala said...

Hi BaluC,

Here I have requirement of validation for a double value input text.

I need to validate a range of double values like from 0.0 to 20.5 etc.

But if the application runs in dutch user enters it as 0,0 to 20,5. So depending upon the locale, format of the digits will be changed and validation should be applied accordingly.
Could you please suggest, can we achieve this using omnifaces using following syntax

super said...

I have two selectOneRadio button in a JSF page and i should select either of one. So i have used id attribute in selectOneRadio button to identify those but it gives "illegalestate" Exception with "Duplicate component ID" and "com.sun.faces.util.Util.checkIdUniqueness" message.

What i have to do i tried with "id" and "binding" attributes but it doesn't display the component itself.

My web.xml contains, debugDOMUpdate = false, STATE_SAVING_METHOD = server, PROJECT_STAGE = Development and developmentMode = true.

So what should i do to use "id" attribute or dynamically how to get id of jsf component in JQuery and use validation for those components.

Please reply back.

super said...

I have two selectOneRadio button in a JSF page and i should select either of one. So i have used id attribute in selectOneRadio button to identify those but it gives "illegalestate" Exception with "Duplicate component ID" and "com.sun.faces.util.Util.checkIdUniqueness" message.

What i have to do i tried with "id" and "binding" attributes but it doesn't display the component itself.

My web.xml contains, debugDOMUpdate = false, STATE_SAVING_METHOD = server, PROJECT_STAGE = Development and developmentMode = true.

So what should i do to use "id" attribute or dynamically how to get id of jsf component in JQuery and use validation for those components.

Please reply back.