Implementing User Selectable Themes In JSF
- Install Maven
- Install the Knappsack Archetypes
Many web applications offer users the option to change the appearance of user interface. One of the easiest ways to implement this is by offering different page color schemes by selecting different css style sheets. This article describes how to implement themes in JSF using CDI backing beans that the user can select as their default theme.
Download project source
Each style sheet implements a different color scheme and when they user picks a theme, you simply have to provide the style sheet assigned to that theme. The application will be based on an existing archetype, so we’ll start by creating a new Maven application using the jee6-sandbox-demo-archetype
.
As it is, the application has a stylesheet called screen.css
which includes color information for the pages. We want to add another style sheet that overrides those color choices for the elements that we want to change per theme.
Of course, we could change much more css than the colors in the theme style sheet, we could change the layout and sizing information also, but the tradeoff is that the more content we put into the theme stylesheet, the more content we have to duplicate in each theme, and the more we have to change each time there is a minor change. We could resolve this problem by giving the user two different theming option, the first would select the layout stylesheet and the other would select the color stylesheet.
- In the package explorer locate the
bean
package and right click on the package folder. - Select New->Class and in the class dialog, enter
StyleBean
as the name of the class
This bean will hold the value of the current theme name and return the style sheet name to our view. It will also hold the list of available style sheets. - We’ll start by annotating the bean with the
@Named
annotation so we can reference it from our JSF view pages and make the bean@SessionScoped
so it will remember the settings between each request. - Since our bean is session scoped, it needs to be passivation capable which means adding the
Serializable
interface to the implements clause. - We add fields for the currently selected theme, and also a theme map which will contain the name of the themes and the stylesheet associated with it. Getters and setters for the
theme
have been omitted. - Because JSF cannot iterate over a
Map
orSet
we need to convert the list of theme names into aList
. We do this with another method that we annotate with the@Produces
method to indicate that this produces values. We give it the namethemes
and make it application scoped so this will be produced only once per application.@Produces @Named("themes") @ApplicationScoped public List<String> getThemes() { return new ArrayList<String>(themeMap.keySet()); }
- We have one more method to implement and that is the method that returns the name of the CSS stylesheet for the selected theme. We do this by using our theme
Map
as a lookup to get the filename
import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.SessionScoped; import javax.enterprise.inject.Produces; import javax.inject.Named; @Named("styleBean") @SessionScoped public class StyleBean implements Serializable { private String theme; private Map<String, String> themeMap; public StyleBean() { themeMap = new LinkedHashMap<String, String>(); // read this list from the db or something themeMap.put("Default", "default.css"); themeMap.put("Blue", "blue.css"); themeMap.put("Green", "green.css"); } }
public String getThemeCss() { return themeMap.get(getTheme()); }
If you get an error at this point you forgot to put in the getter/setters for the theme field.
Now we have put all our code together in one bean, given that bean a name so it can be referenced from JSF, and implemented the list of themes and converting themes to a CSS file name. It’s time to implement our options page that lists the themes available and lets the user select one.
- Create a new JSF page called
options.xhtml
and paste in the following code :<?xml version="1.0" encoding="UTF-8"?> <ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:fc="http://java.sun.com/jsf/composite/fluttercode/component" template="/WEB-INF/templates/template.xhtml"> <ui:define name="content"> <h:form> <fc:panel caption="Visual Options"> <fc:property caption="Theme"> <h:selectOneListbox value="#{styleBean.theme}" style="width : 180px;height : 180px"> <f:selectItems value="#{styleBean.themes}" /> </h:selectOneListbox> </fc:property> <h:commandButton value="Update"/> </fc:panel> </h:form> </ui:define> </ui:composition>
Most of this code is boilerplate so I’ve highlighted the relevant parts. The list box that is wrapped by the property component, takes the list of themes from our
themes
producer and is bound to thetheme
property of ourstyleBean
. When the user selects an item and clicks the Update button thetheme
property is set to the name of the selected theme.Since this is stateful, the new value holds across page requests so next time the page is rendered we can look at thethemeCss
property to get the file name of the css file for the selected theme. - To render the css style sheet we need to add it to the style sheets in the header. Open up the
template.xhtml
file and locate the line with<h:outputStylesheet name="css/screen.css" />
. Underneath it, add a new line to include our user selected style sheet.<h:outputStylesheet name="css/#{styleBean.themeCss}" />
- Now the only thing left is to create the additional stylesheets for our themes. In the
src/main/webapp/resources/css/
folder create a new stylesheet calleddefault.css
but leave it blank. - Create a new stylesheet called
blue.css
with the following content :body { background-color: #E0E0FF; color: #102040; } #header { background: #204080; color: #f0f0fF; } .panel { border: 1px solid #D0D0F0; background: #f0f0ff; } .panel h1 { background: #e0e0f0; border-bottom: solid 1px #D0D0F0; color: #102040; } .odd { background: #f0f0fF; } .even { background: #f7f7ff; } .dataTable th { background: #204080; color: #f0f0ff; }
- Add one more stylesheet called
green.css
with the following content :body { background-color: #E0FFE0; color: #104020; } #header { background: #208040; color: #f0fFF0; } .panel { border: 1px solid #D0F0D0; background: #f0fff0; } .panel h1 { background: #e0f0e0; border-bottom: solid 1px #D0F0D0; color: #104020; } .odd { background: #f0fFf0; } .even { background: #f7fFf7; } .dataTable th { background: #208040; color: #f0fff0; }
If you deploy the application now and go to the http://localhost:8080/jsfthemes/options.jsf you can pick a different theme,click update and the application will start rendering pages with the new themes.
Extending the Implementation
There are a number of ways we can improve this implementation, especially since we are keeping the list of styles and the selection information in a backing bean in the session. Chances are that the session already has some kind of User entity, and it would be a good idea to store the selected theme, or better yet, the name of the css file as part of the user profile. The list of themes is only needed when the user is on the options page and is only used to provide the list of themes and to convert from the theme name to the css file. It would be easy to re-implement these as request scoped elements once the state is moved to the user entity.
One thought on “Implementing User Selectable Themes In JSF”
Comments are closed.
It is nice to see how to implement this yourself. Thanks.
PrimeFaces has a neat theme switcher based on jQuery UI that might come in handy too.
http://www.primefaces.org/showcase/ui/themeswitcher.jsf