How to reference a CSS / JS / image resource in JSF?
Asked Answered
P

4

64

I've done tutorial about Facelets templating.

Now I've tried to create a page that isn't in same directory as the template. I've got problems with page style, because of styles are referenced with relative path like so:

<link rel="stylesheet" href="style_resource_path.css" />

I can use absolute referencing by starting with /:

<link rel="stylesheet" href="/project_root_path/style_resource_path.css" />

But this will bring me troubles when I'll be moving application to a different context.

So I'm wondering what is best way to reference CSS (and JS and image) resources in Facelets?

Pronouncement answered 3/12, 2011 at 11:47 Comment(0)
B
133

Introduction

The proper way is using <h:outputStylesheet>, <h:outputScript> and <h:graphicImage> with a name referring the path relative to webapp's /resources folder. This way you don't need to worry about the context path.

Folder structure

Drop the CSS/JS/image files in /resources folder of the public webcontent as below (just create one if not already exist at the same level as /WEB-INF and /META-INF).

src
 `-- main
      |-- java
      |-- resources
      `-- webapp
           |-- resources
           |    |-- css
           |    |    |-- other.css
           |    |    `-- style.css
           |    |-- images
           |    |    |-- background.png
           |    |    |-- favicon.ico
           |    |    `-- logo.png
           |    `-- js
           |         `-- script.js
           |-- META-INF 
           |    `-- MANIFEST.MF
           |-- WEB-INF 
           |    |-- faces-config.xml
           |    `-- web.xml
           `-- page.xhtml

Do note that it's in /main/webapp/resources and thus not /main/resources. The /main/resources is for Java resources (properties/xml/text/config files) which must end up in runtime classpath. The /main/webapp/resources is for Web resources. See also Maven and JSF webapp structure, where exactly to put JSF resources.

Referencing in Facelets

Ultimately, those resources are available as below everywhere without the need to fiddle with relative paths:

<h:head>
    ...
    <h:outputStylesheet name="css/style.css" />
    <h:outputScript name="js/script.js" />
</h:head>
<h:body>
    ...
    <h:graphicImage name="images/logo.png" />
    ...
</h:body>

The name attribute must represent the full path relative to the /resources folder. It does not need to start with /. You do not need the library attribute as long as you aren't developing a component library like PrimeFaces or a common module JAR file which is shared by multiple webapps.

You can reference the <h:outputStylesheet> anywhere, also in <ui:define> of template clients without the need for an additional <h:head>. It will via the <h:head> component of master template automatically end up in generated <head>.

<ui:define name="...">
    <h:outputStylesheet name="css/style.css" />
    ...
</ui:define>

You can reference <h:outputScript> also anywhere, but it will by default end up in the HTML exactly there where you declared it. If you want it to end up in <head> via <h:head>, then add target="head" attribute.

<ui:define name="...">
    <h:outputScript name="js/script.js" target="head" />
    ...
</ui:define>

Or, if you want it to end up at the end of <body> (right before </body>, so that e.g. window.onload and $(document).ready() etc isn't necessary) via <h:body>, then add target="body" attribute.

<ui:define name="...">
    <h:outputScript name="js/script.js" target="body" />
    ...
</ui:define>

PrimeFaces HeadRenderer

In case you're using PrimeFaces, it's important to know that it unfortunately comes along with a custom renderer for <h:head>. It will messup the standard JSF <h:head> ordering of scripts as described above. You're basically forced to force the order via PrimeFaces-specific <f:facet name="first|middle|last">, which may end up in untemplateable code (you cannot anymore safely add scripts at expected locations from your own custom components/composites). You may want to turn off the PrimeFaces HeadRenderer as described in this answer.

The proper way to add component library specific themes/scripts is using a SystemEventListener on PostAddtoViewEvent of <h:head>, not using a custom HeadRenderer. See also How to load a JavaScript file on all pages programmatically

Packaging in JAR

You can even package the resources in a JAR file. See also Structure for multiple JSF projects with shared code.

Referencing in EL

You can in EL use the #{resource} mapping to let JSF basically print a resource URL like /context/jakarta.faces.resource/folder/file.ext.xhtml?ln=library so that you could use it as e.g. CSS background image or favicon. The only requirement is that the CSS file itself should also be served as a JSF resource, otherwise EL expressions won't evaluate. See also How to reference JSF image resource as CSS background image url.

.some {
    background-image: url("#{resource['images/background.png']}");
}

Here's the @import example.

@import url("#{resource['css/other.css']}");

Here's the favicon example. See also Add favicon to JSF project and reference it in <link>.

<link rel="shortcut icon" href="#{resource['images/favicon.ico']}" />

In case you're using a SCSS compiler such as Dart, then keep in mind that the SCSS processor might interpret # as a special character. In that case you would need to escape it with \.

.some {
    background-image: url("\#{resource['images/background.png']}");
}

Referencing third-party CSS files

Third party CSS files loaded via <h:outputStylesheet> which in turn reference fonts and/or images may need to be altered to use #{resource} expressions as described in previous section, otherwise an UnmappedResourceHandler needs to be installed in order to be able to serve those using JSF. See also a.o. Bootsfaces page shows up in browser without any styling and How to use 3rd party CSS libraries such as Font Awesome with JSF? Browser can't find font files referenced in the CSS file.

Better yet is to investigate if these third party CSS files aren't already available as so-called "web jars", this way you can simply include them as a Maven dependency instead of copying the physical files into the project. Start at webjars.org site and/or org.webjars Maven group. It covers jQuery, Bootstrap, Font Awesome and many more. For example Font Awesome can simply be installed as follows:

<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>font-awesome</artifactId>
    <version>6.5.1</version>
</dependency>

And be referenced as follows:

<h:outputStylesheet library="webjars" name="font-awesome/6.5.1/css/all.min-jsf.css" />

The associated fonts/images are already correctly referenced.

Hiding in /WEB-INF

If you intend to hide the resources from public access by moving the whole /resources folder into /WEB-INF, then you can since JSF 2.2 optionally change the webcontent-relative path via a new web.xml context parameter as follows:

<context-param>
    <param-name>jakarta.faces.WEBAPP_RESOURCES_DIRECTORY</param-name>
    <param-value>/WEB-INF/resources</param-value>
</context-param>

In older JSF versions this is not possible.

See also:

Bylaw answered 3/12, 2011 at 15:34 Comment(11)
I've got it working in CSS, but if I wanted to reference a file in JavaScript, then how? The notation url("#{resource['otbofaces:date-entry/calendar.gif']}") works great in a CSS file, but I've yet to find anything that I can use within a javascript file in order to get the correct resource location.Vickivickie
Thanks @BalusC. I opted to go a completely different route and have used Font Awesome instead. :)Vickivickie
What I am missing is that I expected that a 'template.xhtml' is also a resource. So I was fully expecting to do something like <ui:composition template="/templates/report.xhtml"> with my template residing in web/resources/templates/report.xhtml, even making use of how resources are structured in libraries. It is not working that way.Wingo
@Bylaw First of all thank You for all Your help in past weeks. Sorry, but what i can do wrong if this still doesnt help and css isnt executed? In my template head i added: <h:outputStylesheet name="resources/css/common-style.css" /> . In "Web Pages" folder i created: "resources/css" folder, in which i put common-style.css. In all my pages i add: template="/templates/common/base.xhtml" and use ui:composition + ui:define ,and ui:insert in template. Pages content is created properly, only css styles isnt executed. If You Sir have any clue, thanks. Wish You health by the way.Johnnie
Answer depends on how exactly it is failing. To start, check generated HTML output and check response of the request on the CSS resource.Bylaw
@Bylaw Thanks Sir it works now, I was blind, just removed "resources/" from the begining, so <h:outputStylesheet name="css/common-style.css" /> . Interesting that for images for example I had to provide context also, so for example <img src="/devicerepair/resources/img/image1.jpg" /> instead of <img src="img/image1.jpg" /> . But thats not a problem i guess.Johnnie
<img> is not a JSF component, it's plain HTML. <h:graphicImage> is.Bylaw
Yep, sorry for distrupting You.. am just blind dumass asking just dumb questions on this site..xD but withoutt it, think wouldnt be able to do anything.. so HUGE thanks.Johnnie
Hi @BalusC, Is it ok to use <ui:insert> and <ui:define> for inserting JavaScript or CSS files in the master template instead of target="body"?Disobey
@Arash: doesn't matter as long as you keep using h:outputScript/h:outputStylesheet. Or if you actually tried to ask "is it ok to use script/link instead of h:outputScript/h:outputStylesheet?" then no that's not recommended as you will risk ending up with potential duplicates and having no control at all via a custom ResourceHandler.Bylaw
Hello @BalusC, i tried this answer without good results. And my post got closed by you with some links that i already tried. Nothing yet, i really dont know where im wrong here. I made a chat room if you can check me pls. (also, primefaces discord was helping me too at first)Tooth
C
6

Suppose that you are running the in the sub directories of the web application. You may try like this :

 <link href="${facesContext.externalContext.requestContextPath}/css/style.css" rel="stylesheet" type="text/css"/>

The '${facesContext.externalContext.requestContextPath}/' link will help you to return immediately to the root of the context.

In relative URL's, the leading slash / points to the domain root. So if the JSF page is for example requested by http://example.com/context/page.jsf, the CSS URL will absolutely point to http://example.com/styles/decoration.css. To know the valid relative URL, you need to know the absolute URL of both the JSF page and the CSS file and extract the one from the other.

Let guess that your CSS file is actually located at http://example.com/context/styles/decoration.css, then you need to remove the leading slash so that it is relative to the current context (the one of the page.jsp):

<link rel="stylesheet" type="text/css" href="styles/decoration.css" />
Crag answered 3/12, 2011 at 11:54 Comment(1)
This is not the proper Facelets way though. This is more the JSP way.Bylaw
F
2

These answers helped me to fix the same issue. Although my problem was more complex since I was using SASS and GULP.

I had to change (please note the "\" in front of the #. Probably side effect from gulp:

 <h:outputStylesheet library="my_theme" name="css/default.css"/>  

 background: $blue url("\#{resource['my_theme/images/background-homepage-h1.png']}");
Folia answered 25/6, 2017 at 3:46 Comment(0)
H
0

The resourcehandlers.UnmappedResourceHandler helps to map JSF resources on an URL pattern of /javax.faces.resource/*.

For me these 2 xml configs in faces-config.xml: org.omnifaces.resourcehandler.UnmappedResourceHandler

and in web.xml:

<servlet-mapping>
<servlet-name>facesServlet</servlet-name>
<url-pattern>/javax.faces.resource/*</url-pattern>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>

helped with css and images.

Haddington answered 25/12, 2018 at 5:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.