Pages

Showing posts with label the jsf cookbook. Show all posts
Showing posts with label the jsf cookbook. Show all posts

2008-12-22

Hacking the rich:pickList using jQuery

Another post about the JSF 1.2 upgrade.

Short description of the problem:

We had hacked together a custom pick list from before. After upgrading to newest JSF and newest RichFaces, we could use a standard pick list, except that the standard pickList didn't support any means of showing details about the selected item.

We could of course tried to subclass the RichFaces pick list component, but then we would be stuck with maintaining it. So instead we decided to just throw in a quick jQuery script. Of course, it would also be a good opportunity to learn a little more jQuery. (Yes, I've just started learning jQuery, so if you think you've got a better way to solve the problem, please leave a comment.)

Getting an event when somebody selects a cell

<ui:define name="perPageJavaScript">
<script>
jQuery(document).ready(function(){
                jQuery(".rich-picklist-source-cell").click(function () {
                               updateFooBar(jQuery(this).text());
                });
    });</script>
</ui:define>

Using the event to update a part of the page

<a4j:form>
    <a4j:jsFunction name="updateFooBar"
        reRender="fooBarTbl,fooBarBazTbl">
    <a4j:actionparam name="param1" 
        assignTo="#{fooBarBean.selectedFoo}" />
    </a4j:jsFunction>
</a4j:form>

the perPageJavaScript is just a way to insert a custom JavaScript into the header of one particular page.

Hope this helps someone. I guess I'm not the only one who needs this construct.

2008-12-16

Upgrading a JSF 1.1 project to JSF 1.2

I've just upgraded a project from JSF 1.1 (MyFaces) with tomahawk to JSF 1.2 (Mojarra) with Facelets. Halfway through the process I realized it would be worth sharing, since I would have appreciated if somebody else had done that before me. (There is a description about how to make a new project with JSF 1.2 on http://forums.sun.com/thread.jspa?threadID=5156242, but I wanted to upgrade an old project.)

Facelets is XML

Because facelets are xml documents you need to change && in your expression language statements to &amp;&amp; (I found the tip here: http://saloon.javaranch.com/cgi-bin/ubb/ultimatebb.cgi?ubb=get_topic&f=82&t=001835.)

Update 2008-12-23: If you're smart and read the documentation, you might also find out that you can just write and instead of both && and &amp;&amp;.

Also remember that xml comments are written as <!-- --> instead of <%-- --%>

We don't need the tomahawk taglib any more!

While I appreciate the effort that people has put into MyFaces/tomahawk, it also became a major hurdle in the process of upgrading the application. The reason is that it took very long time before the developers announced a JSF 1.2 compatible version, and by that time, I was already decided to get rid of that taglib.

We mainly used to use the tomahawk library for 4 different things:

t::updateActionListener

<t:updateActionListener value="#{fooBean.bar}" property="#{bazBean.bar}" /> can now be substituted with <f:setPropertyActionListener value="#{fooBean.bar}" target="#{bazBean.bar}" /> in the standard f: taglib.

t:dataList

t:dataList can be substituted with rich:dataList

forceId

The components in the tomahawk taglib has an attribute called forceId which overrides JSF's id generation, allowing you to specify the precise id you want for a tag.

There's no such things as a forceId attribute in any of the other taglibs we use, but luckily, with facelets you can now use plain html tags if that is what you want. Also, much of the reason for using forceId was for selenium testing. Later on, I learned that we could use xPaths with selenium to pin down a page element without resorting to the id. (If you decide to try, there is at least two different Firefox plugins that you can use to find the xpaths, and at least one of them is useable, I just can't remember the name.

The last reason for using tomahawk was either the sortable table or the fileUpload component. Luckily, both are easily available in the rich taglib.

http://www.google.com/search?complete=1&hl=en&q=Bean+or+property+class+javax.faces.model.SelectItem[]+for+managed+bean++cannot+be+found.+&btnG=Google+Search&aq=f&oq=

Using arrays in faces-config.xml doesn't work

At least not i mojarra. Here's my changes:

- public SelectItem[] getFooBarOptionList() {
+ public List getFooBarOptionList() {
      List optionList = new ArrayList(FooBazFooBarEnum.values().length);
      for (FooBazFooBarEnum fooBar : FooBazFooBarEnum.values()) {
          optionList.add( new SelectItem(fooBar.getValue(), fooBar.getValue()));
      }
-     return optionList.toArray(new SelectItem[optionList.size()]);
+     return optionList;
  }
  <managed-property>
-   <property-name>fooBarOptionList</property-name>
-   <property-class>
-    javax.faces.model.SelectItem[]
-   </property-class>
-   <value>#{fooBazDetailFilterBean.fooBarOptionList}</value>
-  </managed-property>


<managed-property>
+   <property-name>fooBarOptionList</property-name>
+   <property-class>java.util.List</property-class>
+   <value>#{fooBazDetailFilterBean.fooBarOptionList}</value>
+  </managed-property>

One last thing:

Here's some code to fetch whatever managed bean you need without injection. I don't suggest you should do anything like this, but if you have already done it, and need to upgrade to JSF 1.2, here is how:

From:

public static Object getBean(String beanName) {
    return getContext().getApplication().getVariableResolver().resolveVariable(getContext(), beanName);
}

To:

public static Object getBean(String beanName) {
    return getContext().getApplication().getELResolver().getValue(getContext().getELContext(), null, beanName);
}

Remember: use with caution, as the result may be hard to unit test.

2008-11-18

Making a "selectable" list component using facelets

Disclaimer: This is not a defense of jsf. If markup were an animal specie, then using jsf would get you arrested for mistreatment. I'm hoping that JavaFX wil give us a better way to make RIAs.

in web.xml

<context-param>
    <param-name>facelets.LIBRARIES</param-name>
    <param-value>/WEB-INF/somewhere/you/decide/whatever.tablib.xml</param-value>
</context-param>

in whatever.taglib.xml

<?xml version="1.0"?>
<!DOCTYPE facelet-taglib PUBLIC
  "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
  "facelet-taglib_1_0.dtd">

<facelet-taglib>
  <namespace>http://www.yourdomain.com/anothernameyoudecide</namespace>
  <tag>
    <tag-name>li</tag-name>
    <source>li.xhtml</source>
  </tag>
</facelet-taglib>

in li.xhtml

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:c="http://java.sun.com/jstl/core">
    <c:choose>
        <c:when test="#{selectedPageName == pageName}">
            <li class="#{class} menuItemSelected"><ui:insert /></li>
        </c:when>
        <c:otherwise>
            <li class="#{class} menuItemNotSelected"><ui:insert /></li>
        </c:otherwise>
    </c:choose>
</ui:composition>

(I was particularily impressed to see that I could just add the existing classes as easy as it is done above. It's one of those rare moments when I think that it would be wonderful if it was this simple, -just to find out that it actually is!)

making the menu

<ui:component xmlns:jsp="http://java.sun.com/JSP/Page"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:y="http://www.yourdomain.com/anothernameyoudecide"
    xmlns:c="http://java.sun.com/jstl/core">
<ul class="horisontalMenu" id="topMenu">
        <li jsfc="y:li" pageName="home"><a href="Home.faces">#{bundle['topmenu.home']}</a></li>
        <li jsfc="y:li" pageName="search"><a href="Search.faces">#{bundle['topmenu.search']}</a></li>
        <li jsfc="y:li" pageName="reports"><a href="Reports.faces">#{bundle['topmenu.reports']}</a></li>
    </ul>
</ui:component>

setting the selected top menu

easy as just dropping this into the ui:composition of the facelet your displaying:
<ui:param name="selectedPageName" value="home"/>