I'm starting a new application and I'm trying to build the main menu's structure to update the content section using AJAX. I'm using JSF 2.2 and Primefaces 6.1. The structure of my project is as follows:
template.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://xmlns.jcp.org/jsf/facelets" xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>...</h:head>
<h:body>
<div id="header">
<ui:insert name="header">...</ui:insert>
</div>
<div id="menu">
<ui:insert name="menu"></ui:insert>
</div>
<h:panelGroup layout="block" id="content" styleClass="container">
<ui:insert name="content">Default content</ui:insert>
</h:panelGroup>
<div id="footer">...</div>
</h:body>
</html>
home.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui"
template="./resources/templates/template.xhtml">
<ui:define name="menu">
<h:form id="form-menu">
<p:menubar id="menubar">
<p:menuitem id="menuitem-home" value="Home" actionListener="#{navigation.navigate}" update="@(content)" process="@(form-menu)"/>
<p:submenu id="submenu" label="Submenu">
<p:menuitem id="menuitem-option1" value="Option 1" actionListener="#{navigation.navigate}" update="@(content)" process="@(form-menu)"/>
<p:menuitem id="menuitem-option2" value="Option 2" actionListener="#{navigation.navigate}" update="@(content)" process="@(form-menu)"/>
<p:menuitem id="menuitem-option3" value="Option 3" actionListener="#{navigation.navigate}" update="@(content)" process="@(form-menu)"/>
</p:submenu>
</p:menubar>
</h:form>
</ui:define>
<ui:define name="content">
<ui:include src="#{navigation.targetPartial}" />
</ui:define>
</ui:composition>
welcome/option1/option2/option3.xhtml (partials)
This is for filling <ui:include>
. They have all the same structure so I resume it in the same place:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html">
<h:body>
<h1 class="h1">My application</h1>
<h3 class="h3">Welcome/Option1/Option2/Option3</h3>
</h:body>
</html>
home.xhtml
is located at src/main/webapp/
, template.xhtml
in src/main/webapp/resources/templates/
and partials in src/main/webapp/resources/partials/
. My bean is a simple navigation handler, made as follows:
Navigation.java
@Named("navigation")
@SessionScoped
public class Navigation implements Serializable {
private static final long serialVersionUID = 6657512604418974978L;
private static final String PARTIALS_ROOT_FOLDER = "/resources/partials/";
private static final String XHTML_EXTENSION = ".xhtml";
private String targetPartial = "welcome";
private Map<String, String> idToPartialMap;
@PostConstruct
public void init() {
idToPartialMap = new HashMap<>();
idToPartialMap.put("menuitem-home", "welcome");
idToPartialMap.put("menuitem-option1", "option1");
idToPartialMap.put("menuitem-option2", "option2");
idToPartialMap.put("menuitem-option3", "option3");
}
public String getTargetPartial() {
return PARTIALS_ROOT_FOLDER + targetPartial + XHTML_EXTENSION;
}
public void setTargetPartial(final String targetPartial) {
this.targetPartial = targetPartial;
}
public void navigate(final ActionEvent event) {
setTargetPartial(idToPartialMap.get(event.getComponent().getId()));
}
}
So, the idea is: Navigation is initialized at welcome so the home page is rendered first in <ui:include src="#{navigation.targetPartial}"/>
(that works fine). idToPartialMap
maps (forgive the redundancy) from <p:menuitem>
id to corresponding partial name.
After that, when you click any option in the menu, the <ui:define name="content">
section should be updated using an AJAX request. The objective is to optimize network traffic by keeping the fixed sections of the page and just updating the content section.
The current situation
Reading the documentation I saw Primefaces 6.1 now support jQuery selectors to target any component using ids or classes.
With the project as it is the page renders correctly but when I click any option the page blinks but doesn't change. If I reload the page it renders the optionX page, proving that Navigation#targetPartial
has been changed through navigate()
method. Inspecting the network traffic you can see that home.xhtml
is first loaded with an HTTP GET with type Document
. In subsequent clicks in the menu, the same file is loaded but with type 'XHR' (XML HTTP Request
). AFAIK, this is the AJAX request fired by the update
attribute in <p:menuitem>
, am I correct?
What am I missing here? how can I make it work? Changing update="@(content)"
to update="@all"
solves the problem but the whole page is loaded and that's not the idea.
Any ideas, suggestions and guideline is welcomed. Thanks in advance for your answers.