{"id":1116,"date":"2010-07-28T11:44:36","date_gmt":"2010-07-28T16:44:36","guid":{"rendered":"http:\/\/www.andygibson.net\/blog\/?p=1116"},"modified":"2010-07-28T11:56:13","modified_gmt":"2010-07-28T16:56:13","slug":"implementing-user-selectable-themes-in-jsf","status":"publish","type":"post","link":"http:\/\/www.andygibson.net\/old-blog\/tutorial\/implementing-user-selectable-themes-in-jsf\/","title":{"rendered":"Implementing User Selectable Themes In JSF"},"content":{"rendered":"<div class=\"prereq floatRight\" >\n<ul>\n<lh>Prerequisites<\/lh><\/p>\n<li>Install Maven<\/li>\n<li><a href=\"\/tutorial\/installing-the-knappsack-archetypes\/\">Install the Knappsack Archetypes<\/a><\/li>\n<\/ul>\n<\/div>\n<p>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.<br \/>\n<!--more--><br \/>\n<a href='http:\/\/www.andygibson.net\/blog\/wp-content\/uploads\/2010\/07\/jsfthemes.zip'>Download project source<\/a><br \/>\nEach 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&#8217;ll start by creating a new Maven application using the <code>jee6-sandbox-demo-archetype<\/code>. <\/p>\n<p>As it is, the application has a stylesheet called <code>screen.css<\/code> 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. <\/p>\n<p>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.<\/p>\n<ol>\n<li>In the package explorer locate the <code>bean<\/code> package and right click on the package folder.<\/li>\n<li>Select <i>New->Class<\/i> and in the class dialog, enter <code>StyleBean<\/code> as the name of the class<br \/>\n<img src=\"img\"\/><br \/>\nThis 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.\n<\/li>\n<li>We&#8217;ll start by annotating the bean with the <code>@Named<\/code> annotation so we can reference it from our JSF view pages and make the bean <code>@SessionScoped<\/code> so it will remember the settings between each request. <\/li>\n<li>Since our bean is session scoped, it needs to be passivation capable which means adding the <code>Serializable<\/code> interface to the implements clause.<\/li>\n<li>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 <code>theme<\/code> have been omitted.<\/li>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nimport javax.enterprise.context.ApplicationScoped;\r\nimport javax.enterprise.context.SessionScoped;\r\nimport javax.enterprise.inject.Produces;\r\nimport javax.inject.Named;\r\n\r\n@Named(&quot;styleBean&quot;)\r\n@SessionScoped\r\npublic class StyleBean implements Serializable {\r\n\r\n\tprivate String theme;\r\n\r\n\tprivate Map&lt;String, String&gt; themeMap;\r\n\t\r\n\tpublic StyleBean() {\r\n\t\tthemeMap = new LinkedHashMap&lt;String, String&gt;();\r\n\t\t\/\/ read this list from the db or something\r\n\t\tthemeMap.put(&quot;Default&quot;, &quot;default.css&quot;);\r\n\t\tthemeMap.put(&quot;Blue&quot;, &quot;blue.css&quot;);\r\n\t\tthemeMap.put(&quot;Green&quot;, &quot;green.css&quot;);\t\r\n\t}\r\n\r\n}\r\n<\/pre>\n<li>Because JSF cannot iterate over a <code>Map<\/code> or <code>Set<\/code> we need to convert the list of theme names into a <code>List<\/code>. We do this with another method that we annotate with the <code>@Produces<\/code> method to indicate that this produces values. We give it the name <code>themes<\/code> and make it application scoped so this will be produced only once per application.\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n\r\n\t@Produces\r\n\t@Named(&quot;themes&quot;)\r\n\t@ApplicationScoped\r\n\tpublic List&lt;String&gt; getThemes() {\r\n\t\treturn new ArrayList&lt;String&gt;(themeMap.keySet());\r\n\t}\r\n\r\n<\/pre>\n<\/li>\n<li>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 <code>Map<\/code> as a lookup to get the filename<\/li>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n\r\n\tpublic String getThemeCss() {\r\n\t\treturn themeMap.get(getTheme());\r\n\t}\r\n\r\n<\/pre>\n<p>If you get an error at this point you forgot to put in the getter\/setters for the theme field. <\/p>\n<\/ol>\n<p>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&#8217;s time to implement our options page that lists the themes available and lets the user select one. <\/p>\n<ol>\n<li>Create a new JSF page called <code>options.xhtml<\/code> and paste in the following code :\n<pre class=\"brush: xml; highlight: [12,13,14]; title: ; notranslate\" title=\"\">\r\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;\r\n&lt;ui:composition xmlns=&quot;http:\/\/www.w3.org\/1999\/xhtml&quot;\r\n\txmlns:ui=&quot;http:\/\/java.sun.com\/jsf\/facelets&quot;\r\n\txmlns:f=&quot;http:\/\/java.sun.com\/jsf\/core&quot;\r\n\txmlns:h=&quot;http:\/\/java.sun.com\/jsf\/html&quot;\r\n\txmlns:fc=&quot;http:\/\/java.sun.com\/jsf\/composite\/fluttercode\/component&quot;\t\r\n\ttemplate=&quot;\/WEB-INF\/templates\/template.xhtml&quot;&gt;\r\n\t&lt;ui:define name=&quot;content&quot;&gt;\r\n\t\t&lt;h:form&gt;\r\n\t\t\t&lt;fc:panel caption=&quot;Visual Options&quot;&gt;\r\n\t\t\t\t&lt;fc:property caption=&quot;Theme&quot;&gt;\r\n\t\t\t\t\t&lt;h:selectOneListbox value=&quot;#{styleBean.theme}&quot; style=&quot;width : 180px;height : 180px&quot;&gt;\r\n\t\t\t\t\t\t&lt;f:selectItems value=&quot;#{styleBean.themes}&quot; \/&gt;\t\t\t\t\t\t\r\n\t\t\t\t\t&lt;\/h:selectOneListbox&gt;\r\n\t\t\t\t&lt;\/fc:property&gt;\r\n\t\t\t\t&lt;h:commandButton value=&quot;Update&quot;\/&gt;\r\n\t\t\t&lt;\/fc:panel&gt;\r\n\t\t\t\r\n\r\n\t\t&lt;\/h:form&gt;\r\n\r\n\t&lt;\/ui:define&gt;\r\n&lt;\/ui:composition&gt;\r\n\r\n<\/pre>\n<p>Most of this code is boilerplate so I&#8217;ve highlighted the relevant parts. The list box that is wrapped by the property component, takes the list of themes from our <code>themes<\/code> producer and is bound to the <code>theme<\/code> property of our <code>styleBean<\/code>. When the user selects an item and clicks the Update button the <code>theme<\/code> 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 the <code>themeCss<\/code> property to get the file name of the css file for the selected theme.<\/p>\n<li>To render the css style sheet we need to add it to the style sheets in the header. Open up the <code>template.xhtml<\/code> file and locate the line with <code>&lt;h:outputStylesheet name=\"css\/screen.css\" \/&gt;<\/code>. Underneath it, add a new line to include our user selected style sheet.\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;h:outputStylesheet name=&quot;css\/#{styleBean.themeCss}&quot; \/&gt;\r\n<\/pre>\n<\/li>\n<li>Now the only thing left is to create the additional stylesheets for our themes. In the <code>src\/main\/webapp\/resources\/css\/<\/code> folder create a new stylesheet called <code>default.css<\/code> but leave it blank.<\/li>\n<li>Create a new stylesheet called <code>blue.css<\/code> with the following content :\n<pre class=\"brush: css; title: ; notranslate\" title=\"\">\r\n\r\nbody {\r\n\tbackground-color: #E0E0FF;\r\n\tcolor: #102040;\r\n}\r\n\r\n#header {\r\n\tbackground: #204080;\r\n\tcolor: #f0f0fF;\r\n}\r\n\r\n.panel {\r\n\tborder: 1px solid #D0D0F0;\r\n\tbackground: #f0f0ff;\r\n}\r\n\r\n.panel h1 {\r\n\tbackground: #e0e0f0;\r\n\tborder-bottom: solid 1px #D0D0F0;\r\n\tcolor: #102040;\r\n}\r\n\r\n.odd {\r\n\tbackground: #f0f0fF;\r\n}\r\n\r\n.even {\r\n\tbackground: #f7f7ff;\r\n}\r\n\r\n.dataTable th {\r\n\tbackground: #204080;\r\n\tcolor: #f0f0ff;\r\n}\r\n\r\n<\/pre>\n<\/li>\n<li>Add one more stylesheet called <code>green.css<\/code> with the following content :\n<pre class=\"brush: css; title: ; notranslate\" title=\"\">\r\nbody {\r\n\tbackground-color: #E0FFE0;\r\n\tcolor: #104020;\r\n}\r\n\r\n#header {\r\n\tbackground: #208040;\r\n\tcolor: #f0fFF0;\r\n}\r\n\r\n.panel {\r\n\tborder: 1px solid #D0F0D0;\r\n\tbackground: #f0fff0;\r\n}\r\n\r\n.panel h1 {\r\n\tbackground: #e0f0e0;\r\n\tborder-bottom: solid 1px #D0F0D0;\r\n\tcolor: #104020;\r\n}\r\n\r\n.odd {\r\n\tbackground: #f0fFf0;\r\n}\r\n\r\n.even {\r\n\tbackground: #f7fFf7;\r\n}\r\n\r\n.dataTable th {\r\n\tbackground: #208040;\r\n\tcolor: #f0fff0;\r\n}\r\n<\/pre>\n<\/li>\n<p>If you deploy the application now and go to the <a href=\"http:\/\/localhost:8080\/jsfthemes\/options.jsf\">http:\/\/localhost:8080\/jsfthemes\/options.jsf<\/a> you can pick a different theme,click update and the application will start rendering pages with the new themes.<\/p>\n<h1>Extending the Implementation<\/h1>\n<p>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.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Prerequisites 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 [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0},"categories":[64],"tags":[49,72,67,32],"_links":{"self":[{"href":"http:\/\/www.andygibson.net\/old-blog\/wp-json\/wp\/v2\/posts\/1116"}],"collection":[{"href":"http:\/\/www.andygibson.net\/old-blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.andygibson.net\/old-blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.andygibson.net\/old-blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/www.andygibson.net\/old-blog\/wp-json\/wp\/v2\/comments?post=1116"}],"version-history":[{"count":6,"href":"http:\/\/www.andygibson.net\/old-blog\/wp-json\/wp\/v2\/posts\/1116\/revisions"}],"predecessor-version":[{"id":1224,"href":"http:\/\/www.andygibson.net\/old-blog\/wp-json\/wp\/v2\/posts\/1116\/revisions\/1224"}],"wp:attachment":[{"href":"http:\/\/www.andygibson.net\/old-blog\/wp-json\/wp\/v2\/media?parent=1116"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.andygibson.net\/old-blog\/wp-json\/wp\/v2\/categories?post=1116"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.andygibson.net\/old-blog\/wp-json\/wp\/v2\/tags?post=1116"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}