0

I am reading, and trying, and searching, and I have no idea what I am doing wrong. I used to program with JSP and Seam in pre-JSF era, and as much as I am used to the technology itself (well, kinda), I am completely new to JSF 2.x and I still need to grasp some concepts. My biggest problem right now is sending parameters from one view to another to perform actual business logic on passed values. My use case:

  1. Home page initiateQuery.xhtml with a multi-seletion search widget (currently PrimeFaces AutoComplete with multiple="true", as it gives the best UX in my use case, I believe)

  2. User can enter/select some values and hit a button that initiates the business process. Entered values should be passed to another view, refineQuery.xhtml where they should be displayed, and business query can be refined with other parameters

I have no idea how to store values selected in initiateQuery, pass them to refineQuery and retrieve them there for actual business logic together with a few additional fields. I have to admit that I am a bit lost with all possibilities of GETting, POSTing, Post-Redirect-Get flow, all stuff going on implicitly, viewParams, FacesContext's members, bean scopes, buttons vs commandButtons, and how to connect all these pieces. I tried many things, but without luck, and go from one error to another, from nothing being passed to "bean scope is larger than parameter's scope" to "cannot set value [] with null converter".

I believe that my backing beans must be view scoped, as session scope is not applicable (user may be not logged in when business query is initiated), and request scope is too small - there are some components on both views (namely p:gmaps) that modify backing bean properties with ajax. I think I can handle other aspects of application, like POJOs in autocomplete list, persistence, L10n, but passing form values to another view is too much for me to grasp ;) As much as some errors I got on the way might be caused by AutoComplete control or some PrimeFaces related stuff, it's more general problem for me, as I was not even able to pass simple string entered into p:inputText :(

You can find a reduced sample of what's not working for me and what I would like to achieve below. Please keep in mind that I am new to JSF and code evolved from one attempt to another and I do not really understand some aspects, so some things might be missing or superfluous.

initQuery.xhtml:

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

      lang="#{localeBean.language}"
      >

<f:view locale="#{localeBean.language}">

    <h:head>
    <title>#{msg.homeTitle}</title>
    </h:head>

    <f:metadata>
        <f:viewParam name="search" value="#{initQueryView.searchedServices}"/>
    </f:metadata>
    <body>

        <h:form>
            <p:autoComplete id="search"
                            multiple="true"
                            required="true"
                            value="#{initQueryView.searchedServices}"
                            completeMethod="#{initQueryView.completeService}"
                            var="serviceKey"
                            itemLabel="#{serviceKey}"
                            itemValue="#{serviceKey}"
                            forceSelection="true"
                            minQueryLength="1"
                            >
                <p:ajax event="itemSelect" listener="#{initQueryView.onItemSelect}" />
                <p:ajax event="itemUnselect" listener="#{initQueryView.onItemUnselect}" />
                <p:column>
                    <h:outputText value="#{serviceKey}" />
                </p:column>
            </p:autoComplete>

            <p:commandButton ajax="false" title="#{msg.search}" value="#{msg.search}" action="refineQuery?faces-redirect=true&amp;includeViewParams=true" >
                <f:param name="search" value="#{initQueryView.searchedServices}"/>
            </p:commandButton>
        </h:form>

    </body>
</f:view>
</html>

initQuery backing bean:

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;

import org.primefaces.event.SelectEvent;
import org.primefaces.event.UnselectEvent;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

@ManagedBean
@ViewScoped
public class InitQueryView {

    private List<String> searchedServices = new ArrayList<>();
    private Set<String> availableServices = new HashSet<>();

    @PostConstruct
    public void init() {

        availableServices = Sets.newHashSet("ServiceA", "ServiceB", "ServiceC");
        searchedServices = new ArrayList<>();
    }

    public Set<String> getAvailableServices() {
        return availableServices;
    }

    public List<String> getSearchedServices() {
        return searchedServices;
    }

    public void setSearchedServices(List<String> searchedServices) {
        this.searchedServices = searchedServices;
    }

    public String searchSubmit() {
        return "refineQuery?faces-redirect=true&includeViewParams=true";
    }

    public List<String> completeService(String query) {

        query = query.toLowerCase();
        List<String> filteredServices = new ArrayList<String>();


        for(String candidateService : availableServices) {
            if(candidateService.toLowerCase().startsWith(query)) {
                filteredServices.add(candidateService);
            }
        }

        return Lists.newArrayList(filteredServices);
    }

    public void onItemSelect(SelectEvent event) {
        String selected = (String)event.getObject();
        availableServices.remove(selected);
    }

    public void onItemUnselect(UnselectEvent event) {
        String selected = (String)event.getObject();
        availableServices.add(selected);
    }
}

refineQuery.xhtml:

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

<f:view locale="#{localeBean.language}">

    <h:head>
        <title>#{msg.homeTitle}</title>
    </h:head>

    <f:metadata>
        <f:viewParam name="search" value="#{searchResultView.searched}"/>
    </f:metadata>

    <h:body>
        Services to search for: #{refineQueryView.searched}<br/>
    </h:body>
</f:view>
</html>

refineQuery backing bean:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;

@ManagedBean
@ViewScoped
public class RefineQueryView {

    private List<String> searched = new ArrayList<>();


    @PostConstruct
    public void init() {
        searched = Arrays.asList("No idea how to retrieve searched services from previous view...");
    }

    public List<String> getSearched() {
        return searched;
    }

    public void setSearched(List<String> searched) {
        this.searched = searched;
    }
}
Maciek
  • 1,533
  • 11
  • 16
  • You would do this the same way as you'd pass a plain simple non-primefaces jsf input text value to another page, or any other 'bean value'. That is narrowing down your problem and you'll be able find answer to that question on stackoverflow – Kukeltje Dec 21 '17 at 07:15
  • @Kukeltje that last link is great, thank you! It seems that `FlashContext` is exactly what I need, I just was not aware of how it works since I have never used it, and I focused too much on viewParams. Now I think I get that flash context is THE way to connect view scoped components, and is designed particularly for this use case. Having said that, I still think that passing parameters by `viewParam`, `?icludeViewParams=true` and `` is a bit confusing for me, but I will get to it. Thanks! – Maciek Dec 21 '17 at 09:46

0 Answers0