Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
19 lines (13 sloc) 6.82 KB
layout title date comments permalink tags
post
Richfaces. How-to change skin via javascript. Dropdown combo.
2008-05-28
false
2008/05/richfaces-how-to-cahnge-skin-dropdown.html
java
jsf
javascript

As we all know - JBoss has acquired wonderful Exadel solutions and we are looking onto JBoss Tools and Richfaces. I am trying to build working application from the scratch using Seam/JSF/Richfaces/Facelets. At the moment I am concentrating onto standard Look-n-Feels and standard skins (with some minor adjustments). So, the task I wiled to solve was - to provide myself the ability to switch between RichFaces skins in the runtime.

RichFaces Live Demo illustrates this in ActionParam Demo. This solution didn't suit me, because amongst switching I had following requirements as well:

  • Switch using DropDown combobox.
  • User just should be able to select Skin by name and see the result, it's all. It 1-step procedure, 1-time click. It's not like that: user selects Skin, After that it presses some magic "Apply Skin" button. Things just don't work like that.
  • A possibility to set-up default skin is MUST-HAVE requirement.
Last, but not least: It was completely unclear to me how to set-up my environment to make my application to use code snippets proposed (there the value of the "#{skinBean.skin}" comes to etc). So, I had to download Live Demo sources and dig into source code to grep all the skinBean references I could find.

So, in conclusion:
  1. I didn't like the rich:sugessionbox, because I wanted to have the thing simple and I didn't like the idea of rendering some table-like structures just to display skin name.
  2. rich:combobox provides only the way to define <f:selectitem>elements. So, what about actioning, refreshing etc. what the functionality is all bout. Only onclick="javascript: code()" will suit my needs.</f:selectitem>
After a little investigation and wrapping my head around JSF/Richfaces I decided to go with the Seam remoting for "javascript: code()" and to use <h:selectonemenu></h:selectonemenu>just to provide the combo with the standard Look-n-Feel.

Step-by-Step solution

To make Richfaces use custom skin you could insert the following lines into your web.xml file:

<context-param>
<param-name>org.richfaces.SKIN</param-name>
<param-value>#{skinBean.skin}</param-value>
</context-param>

What does what mean? It means You have a component named skinBean binded somewhere in the context, which provides you a field skin - the current skin name.

I suggest skin to be session-scoped component, since it is that the end-user is waiting for.
Here is the code for the skinBean component himself:
 
@Name("skinBean")
@Scope(ScopeType.SESSION)
public class SkinBean implements SkinSelector, Serializable {
private static final long serialVersionUID = 7477768262527797286L;

@Logger
Log log;
private String skin;

@Create
public void create() {
log.info("Created");
}

@WebRemote
public String getSkin() {
return skin;
}

@Remove
@Destroy
public void remove() {
log.info("Removed");
}

@WebRemote
public void setSkin(String skinName) {
log.info("Setting skin #0", skinName);
this.skin = skinName;
}
}

Comments:
  • SkinBean implements business interface SkinSelector, which is @Local interface and (SHOULD) contain @WebRemote annotation for remoting, but.. InterfaceGenerator class doesn't understand business Beans if they are not session beans (@Stateless or @Stateful). In my case I couldn't make it stateless and for some reason I couldn't make it @Statefull (It failed during startup). So, InterfaceGeneratorrecognizes the component as the JAVA_BEAN. //SkinSelector just contains get/setSkin method declaration.
  • You can see thereis no default value present in the code. That to do and what way to go? I decided to provide the default value in the components.xnl - just to allow to change default without recompiling. See the snippet:
 
<component name="skinBean">
<property name="skin">japanCherry</property>
</component>

So.. All the things are done. The UI is the last item in the list.
 
<s:remote include="skinBean" />
<script type='text/javascript'>
//<![CDATA[
function setSkinValue(skinName){
Seam.Component.getInstance('skinBean').setSkin(skinName, function(){
Seam.Remoting.log('reloading window');
window.location.reload(false);
});
}
//]]>
</script>
<h:selectOneMenu id="skinSelector" value="#{skinBean.skin}"
onchange="javascript: setSkinValue(this.value);">
<f:selectItem itemValue="DEFAULT" itemLabel="Default" />
<f:selectItem itemValue="plain" itemLabel="Plain" />
<f:selectItem itemValue="blueSky" itemLabel="Blue Sky" />
<f:selectItem itemValue="classic" itemLabel="Classic" />
<f:selectItem itemValue="deepMarine" itemLabel="Deep Marine" />
<f:selectItem itemValue="emeraldTown" itemLabel="Emerald Town" />
<f:selectItem itemValue="japanCherry" itemLabel="Japan Cherry" />
<f:selectItem itemValue="ruby" itemLabel="Ruby" />
<f:selectItem itemValue="wine" itemLabel="Wine" />
</h:selectOneMenu>


As You can see - All i do is passing value to the component and initiate graceful window refresh.

Vuala, work is done. All is working greatly.