1

Using JSF, I'm trying to make a kind of complex situation (Class names has been refactored, and example is minimalist) :

  • DB relation : a guy has a car, a car has a type, a type has cars

The selectOneMenu for CarType need to operate 2 roles :

  • show the type of the car at loading
  • change the cars propositions when it changes

When I load my dataTable, for each row, I want to show the value of the car if the guy has one (null is a valid choice), and in the selectOneMenu for CarType if want to show the type of the car of the guy if the guy has one, but I can't use guy.car.carType because I don't want to write over when changes its value, this menu needs also to serve as a research menu : I choose a Type, I can see its cars

I can't figure out which binding to use to set its value at beginning


1. Classes

class Guy{
    Car car;
}
class Car{
    CarType type;
}
class CarType{
    String type;
}

2. View

<p:dataTable value="#{Bean.guys}"  var="guy">
    <p:column>
        <p:selectOneMenu value="#{Bean.selectedType}>
            <f:selectItem value="#{null}" />
            <f:selectItems value="#{Beans.types}">
            <p:ajax event="change" listener="#{bean.changeType}" />
        </p:selectOneMenu>
    </p:column>

    <p:column>
        <p:selectOneMenu value="#{Bean.selectedCar}>
            <f:selectItem value="#{null}" />
            <f:selectItems value="#{Beans.cars(guy)}" />
            <p:ajax event="change" listener="#{bean.changeCar(guy)}" />
        </p:selectOneMenu>
    </p:column>

</p:dataTable>

3. Bean

class Bean{
    public List<Guy> guys;        // filled from DB
    public List<Car> cars;        // filled from DB
    public List<CarType> types;   // filled from DB
    private Car selectedCar;      // getter setter ok
    private CarType selectedType; // getter setter ok

    public List<Car> cars(Guy g){
        selectedCar = g.getCar();
        cars = selectedCar.getCarType().getCars(); // all cars of this type
    }

    public void changeType(){
        cars = selectedType.getCars();
    }

    public void changeCar(Guy g){
        g.setCar(selectedCar);
    }
}
Kukeltje
  • 12,223
  • 4
  • 24
  • 47
azro
  • 53,056
  • 7
  • 34
  • 70
  • Hello. Could you clarify a bit what you meant with that type of selectone? From what I can understand from your question what you basically want is to be able to pick one car for each guy? If you want to pick the type first you could use two dependent selections. – Aritz Jun 12 '18 at 13:52
  • @XtremeBiker I want to be able to : pick a type in the first Select, and it displays the available cars (of the selected type) so I can select a Car for the guy << easy to do, BUT to inform the user when ge opens the page, both Select have to be filled with the data of DB (if already has value) – azro Jun 12 '18 at 13:55
  • I'd better load everything I need before the view renders. Use the info from this post: https://stackoverflow.com/questions/2451154/invoke-jsf-managed-bean-action-on-page-load Apart from that why do you bind `p:selectOneMenu` selections to a single value in the bean? You're iterating over guys. You need to have a value per guy. – Aritz Jun 12 '18 at 14:07

1 Answers1

2

I'd rather:

Load everything you need before view gets rendered:

<f:metadata>
    <f:viewAction action="#{bean.onload}" />
</f:metadata>
public void onload(){
  //Here load all the guys and all the cars and make the associations for the cars per guy. 
  //Use the java structures you consider, do not limit to what you have in your model
}

Change your datamodel. As you're selecting cars per guy and also keeping selected types per guy, you would need two maps: Map<Guy, Type> type and Map<Guy, Car> cars. See the link at the end for more info.

The table should look like this:

<p:dataTable value="#{bean.guys}" var="guy">
    <p:column>
        <p:selectOneMenu value="#{bean.type[guy]}">
            <f:selectItem value="#{null}" noSelectionOption="true" />
            <f:selectItems value="#{bean.allTypes}">
            <!-- This reloads only the cars menu for this guy -->
            <p:ajax event="change" listener="#{bean.typeChanged(guy)}" />
        </p:selectOneMenu>
    </p:column>

    <p:column>
        <!-- Use a map here, see https://stackoverflow.com/a/49653561/1199132 -->
        <p:selectOneMenu value="#{bean.car[guy]}>
            <f:selectItem value="#{null}" noSelectionOption="true" />
            <!-- Loads the car selection for the type selected for the guy -->
            <f:selectItems value="#{bean.availableCars(bean.type[guy])}" />
        </p:selectOneMenu>
    </p:column>

</p:dataTable>

You don't need a listener in the car selections, unless you want to immediately persist that value in DB or refresh any other pieces in the page based on this. Just implement a save button and make its logic save all the Map<Guy, Car> values when being hit.

You would need also proper Type and Car converters.

See also:

Aritz
  • 30,971
  • 16
  • 136
  • 217
  • 1
    Perfect, thanks ;) I simply put the onLoad stuff on the `@PostConstruct`. Also thank for the 2 links but I already know and implemented that, I did not write them here for clarity only – azro Jun 13 '18 at 07:46