0

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.)

Tatha
  • 131
  • 1
  • 13

1 Answers1

1

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.

Selaron
  • 6,105
  • 4
  • 31
  • 39
  • Thanks a lot @Selaron. Your code sample worked great. Following it, I was trying to dynamically build the menus from one level up..I mean from the `` level itself, instead of the `` level. What's happening for me, is that, only the DropMenu is getting displayed but none of the NavLinks that I'm defining under it in my bean. Any idea, what might be going wrong? – Tatha Jun 13 '19 at 22:10
  • I've added to my original post, the code that I'm trying out currently. – Tatha Jun 13 '19 at 22:23
  • @Tatha Thats odd - I have tested and now edited my answer. Hope that helps. – Selaron Jun 14 '19 at 08:12
  • Worked like a charm ...your tip to add that dummy `dropMenu`. Thank you very much :) – Tatha Jun 16 '19 at 11:54
  • @Tatha You are welcome. If this is the answer to you question, please consider accepting it as such. – Selaron Jun 17 '19 at 13:05