fatdevelops
fatdevelops

Reputation: 435

PrimeFaces SelectOneMenu with icons (custom f:selectItems)

I am trying to display all available FontAwesome icons in a p:selectOneMenu component.

The renderer is supposed to render the icon first, then display the icon's name. A similar example can be found here but is implemented especially for BootStrap: https://mjolnic.com/fontawesome-iconpicker/

 ----------------------------------
|- ICON - | Icon name              |
 ----------------------------------

Unfortunately, the custom columns just do not get rendered. The p:selectOneMenuappears but is rendered as by default.

My picker xhtml page looks as follows:

<!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://java.sun.com/jsf/facelets"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:p="http://primefaces.org/ui"> 

<h:head></h:head> 
<body>
    <p:panel>
        <h:form id="iconPicker">
            <p:selectOneMenu id="iconSelectOneMenu"
            value="#{iconController.availableIconStrings}">
                <f:selectItem itemLabel="Choose icon"
                noSelectionOption="true" />
                <f:selectItems
                value="#{categoryController.availableIconStrings}"
                var="_icon" itemValue="#{_icon}"
                itemLabel="#{_icon}" />
                <p:column>
                    <p:button id="iconButton" icon="#{_icon}" />
                </p:column>
                <p:column>
                    <h:outputText value="#{_icon}" />
                </p:column>
            </p:selectOneMenu>
        </h:form>
    </p:panel>
</body> 
</html>

The IconController class:

public class IconController implements Serializable {

    private List<String> availableIconStrings;

    public IconController() { }

    @PostConstruct
    public void init() {
        availableIconStrings = new ArrayList<>();
        availableIconStrings.addAll(Arrays
                .asList("fa-adjust,fa-adn,fa-align-center,fa-align-justify,fa-align-left,"
                        + "fa-align-right,fa-ambulance,fa-anchor,fa-android,fa-angellist,"
                        + "fa-angle-double-down,fa-angle-double-left,fa-angle-double-right,"
                        + "fa-angle-double-up,fa-angle-down,fa-angle-left,fa-angle-right"
                        // Shortened list
                        .split(",")));
    }

    public List<String> getAvailableIconStrings() {
        return availableIconStrings;
    }

    public void setAvailableIconStrings(List<String> availableIconStrings) {
        this.availableIconStrings = availableIconStrings;
    }
}

As you can see, I am trying to render the icon using a p:button. Do I understand it correctly that I need a PrimeFaces component that is able to display icons (e.g. p:button, p:commandButton, p:commandLink) to achieve my goal?

Any help is appreciated. Thanks a lot.

P.S.: Using PrimeFaces 5.3, JSF 2.2 and Mojarra on WildFly 9.0.2

Upvotes: 3

Views: 7585

Answers (4)

Jasper de Vries
Jasper de Vries

Reputation: 20168

See https://www.primefaces.org/showcase/ui/input/oneMenu.xhtml for an example:

<p:outputLabel value="Icons" />
<h:panelGroup styleClass="ui-inputgroup">
    <h:panelGroup id="icon" styleClass="ui-inputgroup-addon">
        <i class="pi pi-#{selectOneMenuView.icon}"/>
    </h:panelGroup>
    <p:selectOneMenu value="#{selectOneMenuView.icon}" var="item">
        <f:selectItems value="#{['flag','wallet','map','link']}" var="item" itemLabel="#{item}"
            itemValue="#{item}" />
        <p:column><i class="pi pi-#{item}" /> #{item} </p:column>

        <p:ajax update="@parent:@parent:icon" />
    </p:selectOneMenu>
</h:panelGroup>
Screenshot

It is a workaround, yes, but it's the best option currently.

See also:

Upvotes: 0

utrucceh
utrucceh

Reputation: 1116

Use inputGroup

   <div class="ui-inputgroup">
       <div class="ui-inputgroup-addon"><i class="pi pi-user"></i></div>
       <p:inputText placeholder="Username"/>
   </div>

https://www.primefaces.org/showcase/ui/input/inputGroup.xhtml

Upvotes: 0

fatdevelops
fatdevelops

Reputation: 435

For simplicity and re-usability, I decided to wrap the Strings in an Icon class and not to follow implement a different Renderer class as suggested by BalusC in the workaround (https://stackoverflow.com/a/32740430/2118909).

    public class Icon {

    private String fontIconName;

    public Icon(String fontIconName) {
        this.fontIconName = fontIconName;
    }

    public String getFontIconName() {
        return fontIconName;
    }

    public void setFontIconName(String fontIconName) {
        this.fontIconName = fontIconName;
    }
}

The JSF page looks as follows:

<p:selectOneMenu id="iconSelectOneMenu"
    value="#{iconController.selectedIcon}" var="_icon" converter="#{iconConverter}">
        <f:selectItem itemLabel="Choose icon"
        noSelectionOption="true" />
        <f:selectItems
        value="#{categoryController.availableIcons}" itemValue="#{_icon}"
        itemLabel="#{_icon.fontIconName}" />

        <p:column>
            <i class="fa fa-fw #{_icon.fontIconName}"/>
        </p:column>
        <p:column>
            <h:outputText value="#{_icon.fontIconName}" />
        </p:column>
    </p:selectOneMenu>

Please also note the following:

  • #{iconController.selectedIcon} now refers to an instance of the Icon class
  • #{iconController.availableIcons} now refers to a List<Icon> instance
  • I have added a Converter for the Icon class.
  • The icon is now rendered in an <i/> element.

Please also note the importance and role of the customContent variable in the SelectOneMenuRenderer.encodePanel(...) method which indicates that you need a var value on the p:selectOneMenu directly (not in the <f:selectItems/> element!) or the renderer will not recognize that there is custom content as it does not evaluate the <f:selectItems/> var attribute.

Upvotes: 2

Mathieu Castets
Mathieu Castets

Reputation: 6040

Two things:

In order to use p:column in combination with p:selectOneMenu you need to declare the var attribute on the p:selectOneMenu and reference it within your p:column. An example can be found on the PrimeFaces showcase.

So your code should be:

<p:selectOneMenu id="iconSelectOneMenu"
        value="#{iconController.availableIconStrings}" var="ic">
            <f:selectItem itemLabel="Choose icon"
            noSelectionOption="true" />
            <f:selectItems
            value="#{categoryController.availableIconStrings}"
            var="_icon" itemValue="#{_icon}"
            itemLabel="#{_icon}" />

            <p:column>
                <p:button id="iconButton" icon="#{ic}" />
            </p:column>
            <p:column>
                <h:outputText value="#{ic}" />
            </p:column>
        </p:selectOneMenu>

Now, You are facing a bug because you are using a List<String> as explained on this SO question:

So, if the item value is an instance of String, custom content via p:column is totally ignored.

You can find a workaround in the same post: https://stackoverflow.com/a/32740430/2118909

Upvotes: 1

Related Questions