First of all, Happy Yellow Day all you yellow-peeps ... now on with the show...
Yesterday I began a deep dive into creating a managed bean that would create a cached master detail record set. We created two Java classes, one for the master record, Hello World, and one for the detail record, HelloCountry, set up HelloWorld as a managed bean, and wrote an XPage to work with Record set to display the detail and allow the user to interact with that detail.
Today, we're going to work more with those same classes to add more functionality to the page.
First, lets work on on added a button to remove a line from the detail. I'll add an xp:link with an icon to the beginning of the line thats a red/white minus button to act as the delete button. I'll also style the link to have some right margin to seperate the image from the detail data to help prevent accidental clicking.
Now to remove the line, we are going to need to know which item in the list the user clicked on, so go back to the xp:repeat tag, and set the Index name to countryIndex, like this:

To create the action that will delete the selected HelloCountry instance, we are going to use SSJS. We can directly reference our Java Managed Bean objects as JavaScript objects too! To perform the action, we are going to add code in the link's onclick event handler. So select the delete image link, open the Events view, select onclick, change the action from Simple Action to Script Editor, and enter the following code:
HelloWorld.getCountries().remove(countryIndex);
What we have done is called the getCountries() method directly, which returns our Vector of countries, and we've told the Vector to remove the item whose index matches the current iteration of our repeat, eliminating the item that the user has selected!
Now, let's work on getting the data commited to the database. We can do this by writing a function in our managed bean that will perform all the heavy lifting. So let's open the HelloWorld class again, and add a new method block called commitToDb().
We'll also need to add some other import statements so we can work directly with Notes objects, iterate through our vector, etc. So, add the following import statements at the beginning of your HelloWorld class if they are not already there:
import java.util.Iterator;
import lotus.domino.Database;
import lotus.domino.Document;
import lotus.domino.NotesException;
import javax.faces.context.FacesContext;
Now, let's get down to the nitty gritty of commiting the records to the database. The first thing we want to do is get some of the global objects used in our session from the XPages runtime, most notably, the current database. So let's go ahead and do that. There is a call you can make that will return any global object available, and the call is made like this:
FacesContext.getCurrentInstance().getApplication()
.getVariableResolver().resolveVariable(
FacesContext.getCurrentInstance(), variableName);
You replace variableName with the name of the object you want returned, like "database" or "sessionScope" or "viewScope" or any of the other declared objects you have available to you at runtime.
A "best practice" would be to create a single class, named something like JSFUtils and create a set of common functions to perform routinely used functions like this. For example, you could create a static function called getObject that recieved a String variable name to retrieve that made the call above to retrieve it. You could then also create helper functions like getSession, getDatabase, getSessionScope that would return type-cast objects of the type expected.
So let's start off creating our method block, and get the current database. We're going to enclose the entire a try..catch block so we can send any errors we see to the domino console for debugging. Here we go:
public void commitToDb() {
try {
Database database = (Database) FacesContext.getCurrentInstance()
.getApplication().getVariableResolver()
.resolveVariable(FacesContext.getCurrentInstance(), "database");
Now, let's go ahead and create the parent document (or the master record) in the database. We don't have any real content for it in our application at this point, so we'll just save an empty document that the detail records can be responses to.
Document masterRecord = database.createDocument();
masterRecord.appendItemValue("Form","masterRecord");
masterRecord.save();
Now we are ready to commit the detail records to the database as well. To do that we need to iterate through our Vector, and save each document in turn. We use the java.util.Iterator to perform the iteration. Let's create the iterator, and start the iteration process:
Iterator itr = myCountries.iterator();
Document newCountry;
while (itr.hasNext()) {
Now we're ready to commit the detail records, the code is fairly self-explanatory if you are familiar with LotusScript or SSJS already:
HelloCountry currentCountry = (HelloCountry) itr.next();
newCountry = database.createDocument();
newCountry.appendItemValue("Form","detailRecord");
newCountry.appendItemValue("CountryName", currentCountry.getCountryName());
newCountry.appendItemValue("CapitalCity", currentCountry.getCapitalCity());
newCountry.appendItemValue("GoodFood", currentCountry.getGoodFood());
newCountry.makeResponse(masterRecord);
newCountry.save();
newCountry.recycle();
}
masterRecord.recycle();
The one line you may notice that is different with Java than with LotusScript or SSJS is the call to the recycle() method. You have to call this method on every Notes/Domino object you create to make sure that memory gets released and cleaned up appropriately in the C API side of the call. Make it a habit.
Now we've gotten through the meat of the save, we have some clean up to do, we need to our error catching so we can take care of any needed debugging.
} catch (NotesException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
The multiple catch blocks above are really just for show, but it illustrates how to capture different types of errors and handle them separately.
Ok, the last bit we need to take care of is to add a button to our XPage that will call the commitToDb() method. So let's add that button to our HelloWorldDetail.xsp XPage after the add Country button's. To call the save, you can add either an SSJS or Custom action, calling HelloWorld.commitToDb() ... like this:
And that's all she wrote folks! The next installment will look at loading detail records into our cached object from the database, detecting if the records have already been committed, and saving changes instead of creating new documents on save, and maybe more!
And as before, the demo at http://www.xpagecontrols.com/xpagesblog.nsf/HelloWorldDetail.xsp has been updated with this new functionality.
Until then, happy yellow-Coding!