I guess to start my spring break blogging, I decided to do a proof of concept regarding the the #69 post in CodeCorner which strikingly is useful especially when your datasource does not rely on a direct database connection.
This blog consists of:
- WebService design based on BC4J
- UI ClientProxy consumer that converts the WS objects to map objects for generic usage.
- TaskFlow LOV Design
- Consuming page that will use this custom LOV
In my example, I will be utilizing a WS that is driven by BC4J and was designed from WSDL (TOP-DOWN). Using adf wizard to build my webservice classes, I am utilizing a stateless BC4J to service my DepartmentLovService.
For now, this service composes of two operations:
- FetchDepartments - Allows filter by Location, MaxFetchCount, and WildCard
- ValidateDepartment - Validates a Department input which can be the name or the id and only checks for a single returned instance
Our query in our ReadOnlyViewObject looks like this.
Pretty much the XSD request are the bindVariables and the response is a DepartmentType with an Id and a Name. That pretty much sets our WebService and using JDeveloper Wizard to create a WebService from WSDL, you should be all set by now.
Now for the Client side, we will still use JDeveloper to create a ClientProxy based on the same WSDL or better yet, the deployed WSDL. By now, on the client side i have 3 static methods.
- Retrieve a Map of Departments - public static List<Map> retrieveDepartments(Integer fetchCount, String wildCard, BigInteger locationId, BigInteger includedDepartmentId)
- Check if a departmentId is valid - public static Boolean isDepartmentValid(Integer departmentId)
- Retrieve the Department information based on the department Id. - public static Map retrieveValidDepartment(Integer departmentId)
All these methods we will use later in our TaskFlow LOV.
We have two taskFlows, one is where our main form is which uses the LOV and one for the LOV taskflow itself. Our main taskFlow is pretty straight forward.
Take note how the button for my LOV is set to immediate. This way, when we consider the input as invalid, we can skip the validation phase so that we can refresh it correctly.
Now for the LOV taskFlow.
We use a pageFlowScope Bean to give us the list of Map which populates the table, and this will also hold all the necessary operations we'll need.
This method's constructor tries to inspect if a departmentId was submitted. This submitted departmentId will be considered as already selected thus we'd like to give the user the feeling that his old selection is still active. Besides that, we also want to filter the fetch count of our WebService. In a real environment, our LOV might contain give or take 300 options, but this will be such an expensive WS call, so right now we're limiting it by saying "
Fetch the first 10 records by default, but also include the record which I've already selected."
Our return selection is triggered when we hit the Ok button in our LOV confirming our new choice. All it does is retrieve the selectedIndex from the table and retrieve this same index from our map as well as set it as our returnParameter (Defined in the taskFlow structure).
Let's go back now to our main calling page and check the returnListener implementation as well as the validation.
This viewBean allows me direct access to my components and input, but even when using the pageDef as binding attributes, you should be able to achieve the same thing.
Return Listener
Let me start explaining the ReturnListener. This return listener tries to inspect the returnParmeters (Map) for return values matching the static key Strings I defined in our LovPageFlowBean. If a value exist, meaning an "OK" was called from the popup instead of a "CANCEL", we are going to first reset the value in the inputComponent using
resetValue() this will guarantee that we tell ADF that if the component is invalid, reset it as we are going to set the correct values. After that a partial refresh is called.
Validator
At my first attempt, i was trying to mix the validator and the valueChange as I want to set the dependent field for the departmentName to be updated if the ID being validated is indeed valid. But for a much cleaner separation of the validation and the fetch for additional input, I decided to separate it. All the validator does is call the validate webservice operation, which is a really quick operation checking for atleast a row matching in the WS Side (See query). So all we're doing here is send the ID through the WebService, and websevice responds with a Nay or Yay hehehe.
ValueChangeEvent
So why bother with changeEvent? Because i want to set the outputLabel to show the selected Department when a department is selected from the LoV as well as when I enter a valid department Id.
Notes:
Why didn't I just use one WebService (Fetch) to handle everything?
- I use the fetch operation as my initial entry point of populating my table as well as when the user does a search (wildcard) in the LoV, I would do another re-fetch. Reusing the same WS for the validator might be expensive as this WebService functionality is deliver information while the validator should really be fast and light since it will constantly be called by the lifecycle to ensure all the data i'm sending to the server is valid for processing.
So here's the app demo. Enjoy.
|
Filter and select value |
|
Select another value but when the initial search is done, always include what's already selected. |
|
There is no department registered in the HR schema with ID set to 5 |
|
Set a valid value, and valueChangeListener sets the DepartmentName for you. |