My application is based on JSF 2.2 and Primefaces 6.2. I need to create a menubar where submenus would be displayed in a "drop-up", instead of a drop-down. I couldn't achieve this drop-up effect in Primefaces and had to go for Bootsfaces's <b:dropMenu>
component which offers a "drop" attribute for this purpose (drop="up" creates a drop-up). More here:
https://showcase.bootsfaces.net/layout/navbars.jsf
Using <b:dropMenu>
, it gets rendered this way, which looks fine for me:
Screenshot.
However, I need to build the <b:dropMenu>
dynamically via Java code.
Is it possible to use some kind of a model attribute with the <b:dropMenu>
, similar to what Primefaces menus have?
In my current code, I have the sub-menus hard-coded this way, which I want to remove and build dynamically instead:
<b:navBar inverse="true" fluid="true" >
<b:navbarLinks >
<b:dropMenu value="Vehicle Services" drop="up" >
<b:navLink value="Repairs" href="#"></b:navLink>
<b:navLink value="Sales" href="#"></b:navLink>
<b:navLink value="Financing" href="#"></b:navLink>
<b:navLink value="Insurance" href="#"></b:navLink>
<b:navLink value="Leasing" href="#"></b:navLink>
<b:navLink value="Driving School" href="#"></b:navLink>
<b:navLink value="Legal" href="#"></b:navLink>
</b:dropMenu>
.........
.........
.........
-- EDIT --
Following the code sample by @Selaron , trying to build dynamically from <b:NavBarLinks>
in the XHTML:
<h:form>
<b:navBar inverse="true" fluid="true">
<b:navbarLinks>
<f:event listener="#{extranetController.initializeNavBarLinksChildren}" type="postAddToView" />
</b:navbarLinks>
</b:navBar>
</h:form>
In the bean:
public void initializeNavBarLinksChildren(ComponentSystemEvent e) {
UIComponent component = e.getComponent();
//assert that its a NavBarLinks
DropMenu dropMenu = new DropMenu();
dropMenu.setDrop("up");
dropMenu.setValueExpression("value", Utility.createValueExpression("Vehicle Services", String.class));
NavLink navLink1 = new NavLink();
navLink1.setValue("Free Service");
navLink1.setHref("#");
dropMenu.getChildren().add(navLink1);
NavLink navLink2 = new NavLink();
navLink2.setValue("Paid Service");
navLink2.setHref("#");
dropMenu.getChildren().add(navLink2);
component.getChildren().add(dropMenu);
}
In the Utility.createValueExpression() that I'm using...intention is to just set a string as a valueExpression:
public static ValueExpression createValuExpression(String expression, Class clazz) {
FacesContext fc = FacesContext.getCurrentInstance();
ELContext elContext = fc.getELContext();
ExpressionFactory expFactory = fc.getApplication().getExpressionFactory();
ValueExpression ret = expFactory.createValueExpression(elContext, expression, clazz);
return ret;
}
Sadly, on the UI, only the DropMenu comes up (ie, "Vehicle Services"), but none of the two NavLinks that I defined under it. I'm not very sure what I'm doing wrong. I doubt if it has anything to do with the way I'm setting the valueExpression for the DropMenu (though the value "Vehicle Services" does show up fine along with the up-ward caret icon as well.)
I did not find an attribute allowing to specify a dynamic menu model the PrimeFaces way but here is a work around:
<h:form>
<b:navBar inverse="true" fluid="true">
<b:navbarLinks>
<b:dropMenu value="Vehicle Services" drop="up">
<f:event listener="#{myBean.initializeDropMenuChildren}"
type="postAddToView" />
</b:dropMenu>
</b:navbarLinks>
</b:navBar>
</h:form>
This registers an event listener (f:event
) that is triggered once as soon as the b:dropMenu
is added to the view. You can then dynamically instantiate and add links to that menu:
package my.package;
import javax.faces.component.UIComponent;
import javax.faces.event.ComponentSystemEvent;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
import net.bootsfaces.component.dropMenu.DropMenu;
import net.bootsfaces.component.navLink.NavLink;
@Named
@RequestScoped
public class MyBean {
public void initializeDropMenuChildren(ComponentSystemEvent e) {
UIComponent component = e.getComponent();
assert component instanceof DropMenu;
NavLink navLink = new NavLink();
navLink.setValue("wow");
navLink.setHref("#");
component.getChildren().add(navLink);
}
}
EDIT:
In response to your edit: It looks like the b:navbarLinks
component does not like to be completely empty in the xhtml somehow. Try to add the f:event directly into the b:navBar
and add all children from there. Or change your markup to:
<h:form>
<b:navBar>
<b:navbarLinks>
<b:dropMenu value="dummy" drop="down"/>
<f:event listener="#{myBean.initializeDropMenuChildren}"
type="postAddToView" />
</b:navbarLinks>
</b:navBar>
</h:form>
And before adding your dropMenus
, do a component.getChildren().clear();
to remove the dummy dropMenu
.