Dynamische Feld-Bindings

Hallo,

ich habe hier ein Problem, dass ich bisher mit normalen Bordmitteln von Primefaces 4 nicht habe lösen können. Ich möchte kurz die Situation erklären und dann zu dem eigentlchen Problem kommen.

Zuersteinmal habe ich eine Klasse Kunde und eine Klasse Vertrag. Ein Kunde kann mehrere Verträge besitzen (1:n Beziehung). Um einen Kunden neu anzulegen habe ich mich für den Wizard von Primefaces entschieden. Neben den Vetragsdaten gibts noch weitere Attribute, welche hier aber nicht weiter betrachtet werden müssen. Initial bekommt jedes Kunde-Objekt ein Vertragsobjekt in seine Liste von Verträgen zugewiesen. Diese haben ja bekanntlich noch keine ID (hier ist die DB-ID und nicht die Objekt-ID gemeint).

Im Tab des Wizards bezüglich der Vertragsdaten sind nun alle Felder des Formularteils an das Vertragsobjeckt 1 der Vertragsliste des Kundenobjekts gebunden. Falls man weitere Verträge dem Kunden hinzufügen möchte (ganz wichtig schon bei der Erstellung des Kunden) habe ich mir überlegt hierfür ein Drop-Down Menu (SelectOneMenu) nebst einem NEU-Button anzubieten. In dem Drop-Down Menu werden die bereits vorhanden Veträge mit ihrer Vertragsnummer angezeigt (Zu Begin ist hier nur ein Eintrag enthalten) Über den Button soll dann serverseitig ein neues Vertragsobjekt erzeugt und der Liste beim Kundeobjekt hinzugefügt werden und in der Managed-Bean die Variable currentCustomerAgreement mit dem neuen Vetragsobjekt belegt werden. Danach wird das Gridpanel für die Vetragsdaten neu geladen (update). Soweit funktiniert das auch und das Drop-Down-Menü zeigt dann auch auf die neue Vertragsnummer. Per onChangeEvent (<p:ajax event=“change”) will ich es ermöhlichen zwischen den Veträgen hin und her zu springen um nochmals Änderungen durchzuführen. Dafür müsen dann die Felder des Formulars jedes mal an das neue Vetragsobjekt gebunden werden. Das onChangeEvent feuert nur wenn auch eine tatsächliche Änderung der Drop-Down Liste vorliegt. Das wird dummerweise anhand der DB-ID des Objekt evaluiert. Deshalb habe ich initial eine Random ID erzeugt.

Doch überschreibt mir JSF bei der Serverseitigen Eventverarbeitung Das erste Vertragsobjekt mit den Daten des neuen oder umgekehrt. Je nachdem von welchem Eintrag des Drop-Down-Menus ich ausgehe. Das ist natürlich doof, da ich so niemals die Daten von zwei oder mehr Verträgen gleichzeitig pflegen kann.

Die Rheinfolge ist also:

  1. Felder füllen (Vertrag 1)
  2. Neuen Vertrag hinzufügen
    2.1) onChangeNewCustomerAgreement() (Warum auch immer, aber nun gut)
    2.2) setField von Vertrag 1
    2.3) addNewCustomerAgreement()
  3. Felder füllen von Vertrag 2
  4. Select Vertrag 1
    4.1) onChangeNewCustomerAgreement()
    4.2) setField von Vertrag 1 (Hier liegt das Problem, das Object sollte eigentlich Vertrag 2 sein)

Ich habe auch schon mit dem Parameter immediate=“true” herumgespielt. Das hat aber auch nicht den gewünschten Effekt erziehlt. Ich bin jetzt etwas ratlos.

Hier noch ein bisschen Code:

    public void addNewCustomerAgreement(){
            CustomerAgreement customerAgreement = new CustomerAgreement("V00001-14-02");
            this.currentCustomer.getCustomerAgreements().add(customerAgreement);
            this.currentCustomerAgreement=customerAgreement;
        }
       
        public void onChangeNewCustomerAgreement(ValueChangeEvent event){
            logger.debug("+ CustomerPM.onChangeNewCustomerAgreement()");
            this.currentCustomerAgreement=(CustomerAgreement)event.getNewValue();
            logger.debug("- CustomerPM.onChangeNewCustomerAgreement()");
        }

[XML]
<p:column>
<h:outputText value=“Vertrag: " />
</p:column>
<p:column colspan=“4”>
<p:selectOneMenu id=“newCustomerAgreement” tabindex=“2”
style=“width: 190px;”
widgetVar=“newCustomerAgreementWidget”
valueChangeListener=”#{customerPM.onChangeNewCustomerAgreement}"
converter=“ObjectConverter”
value="#{customerPM.currentCustomerAgreement}">
<f:selectItems
value="#{customerPM.currentCustomer.customerAgreements}"
var=“customerAgreement”
itemLabel="#{customerAgreement.agreementNr}"
itemValue="#{customerAgreement}" />
<p:ajax event=“change” process=":newCustomerWizardForm:newCustomerAgreementDetails" update=":newCustomerWizardForm:newCustomerAgreementDetails"/>
</p:selectOneMenu>

                                    <p:commandButton
                                        style="position:relative; left: 5px; bottom: 9px;"
                                        value="Neu" icon="ui-icon-plus"
                                        actionListener="#{customerPM.addNewCustomerAgreement}"
                                        update="newCustomerAgreementDetails"
                                        oncomplete="newCustomerAgreementWidget.selectValue('#{customerPM.currentCustomerAgreement}');"/>
                                </p:column>

[/XML]

    @FacesConverter("ObjectConverter")
    public class ObjectConverter implements Converter {
     
        private static HashMap<String, Object> map = new HashMap<String, Object>();
        @Inject
        private transient Logger logger;
       
        @Override
        public Object getAsObject(FacesContext context, UIComponent component, String value) {
            if (value == null || value.isEmpty()) {
                return null;
            }
            Object object=null;
            try{
                object = map.get(value);
            }catch(Exception e){
                String error=e.getMessage();
            }
            logger.debug(object.getClass().toString() );
            return object;
        }
       
       
        @Override
        public String getAsString(FacesContext context, UIComponent component, Object value) {
            if (value == null || value.toString().isEmpty()) {
                return null;
            }
            Object object=null;
            try{
            object = value;
            map.put(object.toString(), object);
            }catch(Exception e){
                String error=e.getMessage();
            }
            return object.toString();
        }
       
    }
    @Named("customerPM")
    @ViewScoped
    public class CustomerPM implements Serializable {
       
        private static final long serialVersionUID = 1L;
     
        @Inject
        private transient Logger logger;
       
        @Inject
        private transient CustomerService customerService;
        @Inject
        private transient BankService bankService;
       
        private LazyCustomerDataModel lazyModel;
       
        private Customer emptyCustomer; // Um neue Mitarbeiter anzulegen wird selectedCustomer durch emptyCustomer überschrieben
        private Customer currentCustomer; // Der aktuell betrachtete oder neu anzulegenden Mitarbeiter
        private Customer selectedCustomer;
        private List<Customer> customerList;
        private List<Bank> bankList;   
       
        private CustomerAgreement currentCustomerAgreement;
     
        @PostConstruct
        private void init() {
            logger.debug("+ CustomerPM.init()");
            try{
                customerList = customerService.findWithNamedQuery(Customer.FIND_ALL);
                lazyModel = new LazyCustomerDataModel(customerService);
                bankList = bankService.findWithNamedQuery(Bank.FIND_ALL);
            } catch(Exception e){
                logger.error(e.getMessage());
            }
                   
            emptyCustomer = new Customer("K00001-14","V00001-14-01");
            emptyCustomer.getAddress().setCountry("Deutschland");
            currentCustomer = new Customer("K00001-14","V00001-14-01");
            currentCustomer.getAddress().setCountry("Deutschland");
           
            this.currentCustomerAgreement=this.currentCustomer.getCustomerAgreements().get(0);
           
            logger.debug("- CustomerPM.init()");
        }