Search code examples
javascriptjqueryjsfseam

JavaScript: List Box not being updated after pop-up look up


I currently have a listbox (part of a search criteria) that represents a list of strings that are retrieved one at a time via a pop-up window. However when the pop-up window closes the selected String is never added to the listbox. (I have confirmed that the hidden variable is updated and if you navigate away from the page after a search is performed and navigate back, the list box correctly shows Strings I had selected from the pop up previously). Any help would be greatly appreciated!

    <script type="text/javascript">
        function selectBook(bookId, extendedBookName) {
            var idInput = jQuery("#myForm\\:bookNames");
            if (idInput.val() == "") {
                idInput.val(extendedBookName);
            } else {
                idInput.val(idInput.val() + '@@@' + extendedBookName);
            }  
        }
    </script>
...
 <h:form id="myForm">
    ...
      <ui:define name="form-fields">
         <h:selectOneListbox id="booksListBox" size="3" style="width:470px">  
            <s:selectItems var="_var" value="#{bean.searchCriteria.bookNames}" label="#{_var}" noSelectionLabel="" />
         </h:selectOneListbox>
         <h:outputLink onclick="return openNewWindow('#{bean.booksLookupUrl}?#{bean.bookLookupParameters}', 'book');" 
                       target="_blank">
                     <h:inputHidden id="bookNames" value="#{bean.searchCriteria.bookNames}" converter="StringListConverter"/>
                     <h:outputText value="Add"/>
         </h:outputLink>

      </ui:define>
    ...
 </h:form>

This is javaScript that belongs to the lookup window. This calls the selectBook function

function selectBook(bookId, extendedBookName) {
        var extendedName = unescape(extendedBookName);
        window.opener.selectBook(bookId, extendedName);
        window.close();
    }

And for my Java code...

public class BookSearchCriteria implements Serializable {
...
private List<String> bookNames = new ArrayList<String>();

    public List<String> getBookNames() {
    return bookNames;
}
public void setBookNames(List<String> bookNames) {
    this.bookNames = bookNames;
}

The StringListConverter code...

@FacesConverter("myStringListConverter")
public class StringListConverter implements Converter {

    // this is used as a regex, so choose other separator carefully
    private static final String MY_SEPARATOR = "@@@";

    @Override
    public Object getAsObject(FacesContext context, UIComponent component,
            String value) {
        if (value == null) {
            return new ArrayList<String>();
        }
        return new ArrayList<String>(Arrays.asList(value.split(MY_SEPARATOR)));
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component,
            Object value) {
        if (value == null) {
            return "";
        }
        return join((List<String>) value, MY_SEPARATOR);
    }

    /**
     * Joins a String list, src: http://stackoverflow.com/q/1751844/149872
     * 
     * @param list
     * @param conjunction
     * @return
     */
    public static String join(List<String> list, String conjunction) {
        StringBuilder sb = new StringBuilder();
        boolean first = true;
        for (String item : list) {
            if (first) {
                first = false;
            } else {
                sb.append(conjunction);
            }
            sb.append(item);
        }
        return sb.toString();
    }

}

Solution

  • I don't really understand why you want to populate your listBox using a popup.

    But if you modify the underlying bean values, you need to rerender the <h:selectOneListbox/> to reflect these changes. If you are using (or can use) the richfaces framework it can be done quite easily through an ajax call.

    The ajax4jsf component called <a4j:jsFunction/> can do the job. For example in your parent page add :

    <a4j:jsFunction name="refreshListbox" reRender="booksListBox" limitToList="true"/>
    

    and in your popup just call this js function when a new book value is selected :

    <script type="text/javascript">
        window.opener.refreshListbox();
    </script>
    

    You could also use a <rich:modalPanel> instead of a popup to stay in the same page and interact more easily with your jsf components.