Jun 4, 2009

Μια πρόταση υλοποίησης σχετιζόμενων lovs σε οθόνες αναζήτησης (search cascade lovs) του JHeadstart

Μια απαίτηση που προέκυψε σε ένα έργο με JHeadstart 10g, είναι να υπάρχει συσχέτιση μεταξύ δυο list of values (lovs) πεδίων στις οθόνες της προχωρημένης αναζήτησης (advanced search) και άρα η επιλεγόμενη τιμή του πρώτου, να κατευθύνει τον περιορισμό των τιμών του δεύτερου. Αν και μια τέτοια συμπεριφορά είναι σχετικά εύκολο να επιτευχθεί για οθόνες καταχώρησης, όπου τα bindings μας απευθύνοντας ευθέως σε ένα ADF view object, οπότε αλλάζοντας την τιμή ενός πεδίου, μπορούμε να επηρεάσουμε τον κώδικα του setter του ώστε να θέτει μια bind variable σε ένα άλλο view object, κάτι τέτοιο δεν συμβαίνει με τις οθόνες αναζήτησης: ο λόγος είναι πως ο JHeadstart δημιουργεί ένα Java bean για τα πεδία της αναζήτησης και άρα δεν επικοινωνούμε απευθείας με το view object μας. Επομένως, αν ανατρέξουμε στον κώδικα μιας JSP σελίδας θα δούμε κάτι ως το ακόλουθο για το value binding:

value="#{searchDeptAndEmployees.criteria.DeptAndEmployeesLocationId}"

όπου το πρώτο τονισμένο (bold) μέρος είναι το όνομα του JHS search bean, ενώ το δεύτερο (criteria) είναι το όνομα της δομής που κρατά σε μορφή java.util.Map τα κριτήρια μας.

To παράδειγμα μας στηρίζεται στο σχήμα HR, όπου έχουμε φτιάξει ένα συνδυαστικό view object (DeptAndEmployees) από τα entities Departments και Employees, στο οποίο γίνεται η αναζήτηση. Επιπλέον υπάρχουν δυο view objects που υλοποιούν τα list of values μας. To πρώτο είναι το DepartmentsLookup και το δεύτερο το EmployeesByDeptLOV που αναμένει την παράμετρο του department id για να εκτελεστεί. Έτσι έχουμε σχεδιάσει δυο εξαρτώμενα view objects.

Αφού το ξεκαθαρίσαμε αυτό, παρατηρώντας πιο κοντά ένα list of value σε επίπεδο JSP σελίδας, βλέπουμε πως γίνεται η επιστροφή της τιμής που επιλέξαμε από το JSF:

<af:selectInputText id="SearchDeptAndEmployeesEmployeeId" value="#{searchDeptAndEmployees.criteria.DeptAndEmployeesEmployeeId}"
action="dialog:EmployeesByDeptLOV" windowHeight="200" windowWidth="600" ...
returnListener="#{DeptAndEmployeesEmployeeIdLovItemInAdvancedSearch.returnedFromLov}">
</af:selectInputText>

Η πρόκληση που έχουμε να αντιμετωπίσουμε είναι να μεταβάλλουμε τον τρόπο λειτουργίας του returnListener, ώστε ταυτόχρονα με την ενημέρωση της τιμής στο parent window, να προκαλεί και την εκτέλεση άλλων view objects που αναμένουν την ενημερωμένη τιμή ως bind variable. Όλα αυτά τα στοιχεία θα πρέπει να είναι παραμετρικά. Γι' αυτό το λόγο, θα κάνουμε χρήση ενός γνωρίσματος του JHeadstart που μας επιτρέπει να ορίζουμε metadata ανά πεδίο, όπως φαίνεται στην επόμενη εικόνα. Συνεπώς ορίζουμε μια λίστα τιμών που περιλαμβάνει το όνομα του data control (δηλαδή του application module), του view object που θα ενημερωθεί και την bind variable που απαιτείται.

Θα χρειαστεί τώρα να φτιάξουμε ένα δικό μας lovitembean που θα λαμβάνει υπόψη του αυτές τις παραμέτρους. Άρα, ορίζουμε μια νέα κλάση που κληρονομεί από τη oracle.jheadstart.controller.jsf.bean.LovItemBean και βασικά επανά-ορίζει τον κώδικα της μεθόδου returnedFromLov:

public void returnedFromLov(ReturnEvent returnEvent) {
// Execute parent event.
super.returnedFromLov(returnEvent);
// Retrieve returned value.
String returnedValue =
((ValueHolder)returnEvent.getComponent()).getValue().toString();

// Iterate over custom properties of the field.
Set> entrySet = customProperties.entrySet();
for (Map.Entry entry: entrySet) {
// Custom property found.
if (entry.getKey() != null && entry.getValue() != null) {
// param1: Datacontrol name.
// param2: view object name.
// param3: bind variable name.
String[] params = entry.getValue().split(",");
assert params.length == 3;
// Apply bind variable to dependent view object.
ApplicationModule module =
(ApplicationModule)JsfUtils.getExpressionValue("#{data." +
params[0] +
".dataProvider}");
ViewObject vo = module.findViewObject(params[1]);
vo.setNamedWhereClauseParam(params[2], returnedValue);
vo.executeQuery();
}
}
}


Η δήλωση του δικού μας Java bean πρέπει να επηρεάσει την διαδικασία του generation, άρα ορίζουμε το δικό μας lovItemInAdvancedSearchBean.vm στο επίπεδο του master lov, όπου γίνεται το πέρασμα των παραμέτρων των metadata, δημιουργώντας ένα νέο managed property.


Κάνουμε generate και επιλέγουμε μια τιμή από το αρχικό (parent) lov, έστω του IT Department.

Κάνουμε click τώρα στο δεύτερο lov, το οποίο έχει ενημερωθεί και εμφανίζει τα φιλτραρισμένα αποτελέσματα.

No comments: