0

I'm trying to develop a reusable data picker. It will be used to lookup to related data and set in managed bean property.

What I'm trying to archieve is something like this:

1) A composite with an input text and lookup button:

enter image description here

2) When user clicks on search button, it opens a new page.

3) This page allows to search for a specific record

4) Once it is found, user clicks on a button and chooses it

enter image description here

5) When user clicks on check button (last column in the table above), it sets value in field bound to input (#1).

The search is always done in the same entity because it is a data picker for such entity but the destination is not known because it is a reusable component.

How do I make connection between search results, what user chooses and destination object (which is a managed bean's property)?

I have no idea on how to proceed.

Thanks in advance,

EDIT

MORE INFO: After some research I found a way to pass managed bean and property/actionListener. I'll explain what I did and later describe what is the problem now.

1) Created a sample page (sample1.xhtml). It is a composition/fragment:

    <?xml version='1.0' encoding='UTF-8' ?>
    <!DOCTYPE html>
    <ui:fragment xmlns="http://www.w3.org/1999/xhtml"
        xmlns:h="http://java.sun.com/jsf/html"
        xmlns:c="http://java.sun.com/jsp/jstl/core"
        xmlns:b="http://bootsfaces.net/ui"
        xmlns:ui="http://java.sun.com/jsf/facelets"
        xmlns:f="http://java.sun.com/jsf/core"
        xmlns:p="http://primefaces.org/ui"
        xmlns:bt="http://xmlns.jcp.org/jsf/composite/tags/bt"
        xmlns:pt="http://xmlns.jcp.org/jsf/passthrough">

        <ui:composition template="/templates/pages/form.xhtml">
            <ui:param name="title" value="Manage Value Object Category" />
            <ui:define name="toolbar">
            </ui:define>
            <ui:define name="form-fields">
                <c:set var="variable" value="#{valueObjectBean}" scope="request" />
                <p:commandButton actionListener="#{valueObjectBean.doTest()}"
                    value="test" update="@form" />
            </ui:define>
            <ui:define name="bar-cmd">
            </ui:define>
        </ui:composition>

    </ui:fragment>

Please note:

a) c:set sets to a request scoped variable the managed bean to call.

b) valueObjectBean.doTest() just replaces the composition to be shown.

2) This is destination page:

            <?xml version='1.0' encoding='UTF-8' ?>
    <!DOCTYPE html>
    <ui:fragment xmlns="http://www.w3.org/1999/xhtml"
        xmlns:h="http://java.sun.com/jsf/html"
        xmlns:c="http://java.sun.com/jsp/jstl/core"
        xmlns:b="http://bootsfaces.net/ui"
        xmlns:ui="http://java.sun.com/jsf/facelets"
        xmlns:f="http://java.sun.com/jsf/core"
        xmlns:p="http://primefaces.org/ui"
        xmlns:bt="http://xmlns.jcp.org/jsf/composite/tags/bt"
        xmlns:pt="http://xmlns.jcp.org/jsf/passthrough">

        <ui:composition template="/templates/pages/form.xhtml">
            <ui:param name="title" value="Manage Value Object Category" />
            <ui:define name="toolbar">
            </ui:define>
            <ui:define name="form-fields">
                <h:outputText value="Hello: #{variable}" />
                <p:commandButton id="call" actionListener="#{variable['doTest'](null)}"
                    value="Execute" />

                <p:commandButton id="set" value="Set">
                    <f:setPropertyActionListener value="true"
                        target="#{variable['actAddCatItem']}"></f:setPropertyActionListener>
                </p:commandButton>
            </ui:define>
            <ui:define name="bar-cmd">
            </ui:define>
        </ui:composition>

    </ui:fragment>

Please note:

a) commandButton with id "call" calls an action listener from managed bean set in #1a: {variable'doTest'}. Call method 'doTest' from managedBean passed as variable to 'variable'.

b) Just to mention but it's not important now. Another commandButton with id "set" just sets a property to managed bean set in #1a. I won't explore this option yet.

Now, what is happening:

1) When # c:set is request scoped (scope="request"), second page "sees" the managed bean passed as parameter:

enter image description here

a) As you can see, it outputs toString of managed bean but when I click on command button with id "call", I get this error:

16:59:24,901 WARNING [javax.enterprise.resource.webcontainer.jsf.lifecycle] (default task-39) /WEB-INF/lib/infra-wm-1.0.jar/META-INF/resources/infra/valueobject/sample2.xhtml @24,44 target="#{variable['actAddCatItem']}": Target Unreachable, identifier 'variable' resolved to null: javax.el.PropertyNotFoundException: /WEB-INF/lib/infra-wm-1.0.jar/META-INF/resources/infra/valueobject/sample2.xhtml @24,44 target="#{variable['actAddCatItem']}": Target Unreachable, identifier 'variable' resolved to null

If I change c:set scope to 'view' this approach works well. But I can't use this scope because there might be more than one composite using same approach in the page (in the end it is a composite component).

The question now is: Why sample 2 recognise 'variable' when it is view scope and doesn't when it's request scoped despite value is passed correctly and it is not NULL?

Thanks,

Emilio Numazaki
  • 836
  • 1
  • 5
  • 25
  • Try without a composite first... – Kukeltje Oct 03 '17 at 06:24
  • Hello again @Kukeltje! Thank you for your help! Actually, without a composite it is straightforward because I can set back choosen value to caller page. What I'm trying to do is a kind of "data picker" where I add this component to a page and it will add search and pick feature. I mean, once user selects, it sets selected value from search results to "value" attribute as you can see in #1. – Emilio Numazaki Oct 03 '17 at 13:40
  • But look, I'd like to create a generic picker and it doesn't know where to set back selected value, until developer sets it in value attribute of composite. I knnow there is no way to pass attributes by reference because of nature of managed beans so, what is the next try I should do? – Emilio Numazaki Oct 03 '17 at 13:42

1 Answers1

0

After some research, I gave up from this approach and did what I was trying to accomplish through Primeface's modal Dialog but I wasn't happy with it because modal dialogs does not works well on mobile devices, specially when you need to open a dialog over dialog. But the problem were solved, at least for version one.

Now I returned to challenge and did another approach that, finally, worked quite well.

1) Find a way to pass and bind managed bean from one composite to another

It failed because actionListener binding from commandButton is evaluated during render time and always got NULL. I asked and developed final thoughts here: Question

Solution:

Reading some articles, questions, I got final answer to all my questions: Use commandButton binding to programmatically instantiate component, set actionListener and set selected value to entity that initially did lookup. Description is based on my first post:

1) The edit with lookup commandLink is a simple composite (#1)

1.1) Composite interface has all attributes necessary to its setup, most important are:

  • The value attribute where selected object will be set

  • Managed bean + actionListener to call back (or set back) selected object from search results

1.2) Set some component variables to avoid exposing unnecessary attributes to end user (developer)

  • Page that performs search based on criterias (it might be a managedBean + actionLister that redirects to search page

  • Expression representing the row in result dataTable (var attribute name) to define actionListener's callback method expression

2) At search page (each entity type must have it's own) the commandButton (#4) to select row is created programmatically based on component's interface and variables. The declaration is just <p:commandButton binding=#{dataLookupBean.bind} />.

Finally, the code that allows return to previous page setting selected object:

public CommandButton getBind() {
    try {
        cb = new CommandButton();

        StringBuffer fullCommand = new StringBuffer("#{").append(bean).append(".").append(action).append("(")
                .append(paramEl).append(")").append("}");

        Class<?>[] aParamTypeClazz;

        aParamTypeClazz = new Class<?>[] { Class.forName(paramType) };
        cb.addActionListener(new MethodExpressionActionListener(
                createMethodExpression(fullCommand.toString(), aParamTypeClazz)));

        if (!BtUtil.isNullOrEmpty(buttonIcon))
            cb.setIcon(buttonIcon);

        if (!BtUtil.isNullOrEmpty(buttonValue))
            cb.setValue(buttonValue);

        return cb;
    } catch (ClassNotFoundException e) {
        log.error(e.getMessage(), e);
        throw new InfraException(e.getMessage());
    }
}

commandButton of column "Actions" (#4) are bound to code above.

Now it is very easy to add a input with lookup in any page, just declare something like:

<bt:pickerCompetency toolbarMode="true"
                            onSelectBean="reviewFormBean"
                            onSelectAction="doPickIndividualCompetency" />

All the logic to open a search page, search and select is done by underlying composite code.

Off course there are much more code behind it but it is all business specifics. The roadmap is as I described above. Feel free to ask something that might not be clear.

Emilio Numazaki
  • 836
  • 1
  • 5
  • 25