Coding paradigm: line by line all my opinions are belong to me

Richfaces. How-to change skin via javascript. Dropdown combo.

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:
<ul><li>Switch using DropDown combobox.</li><li>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.</li><li>A possibility to set-up default skin is MUST-HAVE requirement.</li></ul>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:
<ol><li>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.
</li><li>rich:combobox provides only the way to define elements. So, what about actioning, refreshing etc. what the functionality is all bout. Only onclick="javascript: code()" will suit my needs.</li></ol>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 just to provide the combo with the standard Look-n-Feel.
<h2>Step-by-Step solution</h2>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.


NB. If you've found typos or errors, please suggest a correction or edit on github.
comments powered by Disqus