When working with the layout
component, we should be aware of the pitfalls of menus and dialogs inside layout units. Beginners often face overlap issues and try to find several workarounds. In fact, there are easy solutions available.
In this recipe, we will show how to overcome these issues. We will integrate p:menubar
and p:dialog
into layout units.
Let's assume that we have a full-page layout with two layout units, center
and north
. The north
unit contains a menubar
component with quite normal options:
<p:layout fullPage="true"> <p:layoutUnit position="center"> Center </p:layoutUnit> <p:layoutUnit position="north" size="80" resizable="false"> <h:form> <p:menubar> <p:submenu label="JavaScript Libraries"> <p:menuitem value="jQuery" url="#"/> <p:menuitem value="Yahoo UI" url="#"/> <p:menuitem value="Prototype" url="#"/> </p:submenu> <p:menuitem value="Go Back" url="#"/> </p:menubar> </h:form> </p:layoutUnit> </p:layout>
If we try to open the menu, it gets partially hidden by the layout unit, as shown in the following screenshot. We can see scrollbars on the right-hand side as well.
To overcome this wrong appearance, we will set overflow
to visible
for the content container (the div
element) of the north
layout unit. This CSS setting can be placed within h:head
. The following code encapsulates this discussion:
<style type="text/css"> .ui-layout-pane-north .ui-layout-unit-content { overflow: visible; } </style>
The menu now appears correctly, as shown in the following screenshot. It overlaps the layout unit when we click on it.
The second potential problem is the modal dialog inside a layout unit. Let's assume we place such a dialog with default options inside the center
layout unit. The semitransparent layer of the modal dialog overlaps the dialog and prevents any interactions with it. This is shown in the following screenshot:
To overcome this issue, we need to set the appendTo
attribute to the @(body)
value, as shown here:
<p:layoutUnit position="center"> <p:dialog header="Dialog in layout" modal="true" widgetVar="dlgWidget" appendTo="@(body)"> <h:form> <h:inputText/> <p:commandButton value="Save" style="margin:10px;"/> </h:form> </p:dialog> <p:commandButton value="Show dialog" type="button" onclick="PF('dlgWidget').show()"/> </p:layoutUnit>
Now, the semitransparent layer is placed behind the dialog, as shown in the following screenshot:
Drop-down and pop-up menus often need to overlap adjacent layout units (panes). An ideal solution would be to append the menu elements to the body tag. In this case, no additional effort is needed to use PrimeFaces menus with layout
. When this is not possible for some reason, there is an option to handle the menus. If a menu appears in a nonscrolling layout unit, we should give the unit's content container the CSS property overflow:
visible
and ensure it is the last unit in the HTML markup. By making it the last unit, it has a naturally higher stack order.
Setting the appendTo
attribute to @(body)
appends the dialog as a child of the document body. The @(body)
value is a search expression in terms of PrimeFaces Selectors (PFS) to reference the HTML body element.
The preceding argument is valid for the overlay panel. Set appendTo="@(body)"
when overlayPanel
is in another panel component, such as layout
and/or dialog
.
Components with appendTo="@(body)"
need a h:form
tag inside when they communicate with the server. But, avoid nested h:form
tags. Nested forms bring unexpected behavior and JavaScript errors.
The Layout
component is discussed extensively in the Creating complex layouts recipe in Chapter 4, Grouping Content with Panels.
This recipe is available in the demo web application on GitHub (https://github.com/ova2/primefaces-cookbook/tree/second-edition). Clone the project if you have not done it yet, explore the project structure, and build and deploy the WAR file on application servers compatible with Servlet 3.x, such as JBoss WildFly and Apache TomEE.
The showcase for the recipe is available at http://localhost:8080/pf-cookbook/views/chapter11/layoutPitfalls.jsf
.