The purpose of the Embedded Editor is to allow you to include multiple forms in a single container form or to allow you to edit documents in an embedded view without having to open those documents directly. Sounds pretty cool and usually works well, but there are several places where the implementation is just not quite right. For one thing, creating a new document using a combination of an embedded editor and an embedded view doesn't work (correctly) out of the box. Also, the context in which certain events or properties of the various elements are known or unknown can be limiting. You can find several examples on LDD of people trying to transfer data between the main form and the embedded form, most with limited success. It seems that for vanilla functionality these embedded elements work fine, but try to get that fancy chocolate and you start to make a mess.
The situation I recently found myself in was creating a database to store forecasting information for machines that need to be built. Each machine is defined in a Project document and one aspect of the Project document is the Bill of Materials (BOM). The BOM is made up of Notes documents, each representing a particular part of the machine. There are only a few fields on the Part document and these documents are directly related to the Project. From a usability perspective, allowing the user to add or change the content of the BOM directly from the Project form would be best, so this was an ideal place for the embedded editor. Through trial and error (and multiple crashes of the Notes client), I came up with a method that allows users to do this in a fairly elegant way. Let's explore it...
I created a sample database that you can download if you want to play along with the home game. (Disclaimer: No...my interfaces don't normally look like this. I just wanted to have a little fun with it). [Link has been moved from box.net due to problems...new link should work fine]
Talking about manufacturing stuff is pretty boring, so for purposes of this example, let pretend we have a database that allows users to order Squishees from Apu's Kwik-E-Mart. The main Order document includes the "metadata" we need for purposes of views and such, like the customer's name, address, etc. Related to this will be the 'OrderList' document (our embedded editor form), containing the detail of the Squishees the user wants to order (think shopping cart). Here's what something like that looks like when implemented:
Here we see that there is an embedded editor along with an embedded view. The embedded view is showing the 'OrderList' documents. When one of these documents is selected in the embedded view, the document is available for editing via the embedded editor. This is actually a pretty slick UI technique, since the user is not interrupted by another document opening up just to edit a few fields. Notice on the right that there are links where a user can update the information on the document or delete an item from the order entirely.
Creating a new document in an embedded editor
One of the things that I know some people have found tricky is creating new documents in the embedded editor and having them appear right away in the embedded view. This should be easy, right? Well...5 yard line again. It's a bit tricky, so let's examine that part first. For purposes of this demo, I've included the code to create a new 'OrderList' doc in two places, one in a link on the main form and one in an action button in the embedded view. Choose whichever will work best for your situation and UI. The code is similar for both, and you can take a look at this in the sample database. I'll just walk you through the logic here.
Aside: I stripped out as much unnecessary code as I could in this sample database. Of course, you'll want to add error checking, logging, etc. as needed for your application. I did try to add comments to help make certain parts more clear, but feel free to e-mail me if you have any questions.
I choose not to have the embedded editor available if there were no 'OrderList' documents created yet. The behavior in this situation is less than desirable. You can see this for yourself by creating 'Generic Embedded Editor Example' document in the sample database. Since I am relating the OrderList documents to the main Order document, I created a key value that links these together. For this reason, I make sure the main Order document is saved before Squishees can be added. Once you fill out the Customer Information tab, you can save the document and then you will see on the Shopping Cart tab that you have the embedded view but no embedded editor available. I implemented this by using a computed subform. There is a field called "txt_OrderAvailable" on the main form which does a lookup to the embedded editor view. If it finds any docs, this flag is set to "1", otherwise it's "0". The computed subform then uses this value to determine which subform to show.
When you click the link to 'Add A Squishee', the code first checks to see if there are any OrderList documents for this Order. If none are found, we know that we are going to have to reopen the main Order document so that we get the correct subform to display. Before doing that, we create the new OrderList document in the back end, set some of the important fields (such as the key value) and then save it. At this point, we determine what to do with the ui document in order to see the newly created Squishee in the list. If this is the first OrderList document, we get the main Order document in the back end and save it so that the "txt_OrderAvailable" flag gets updated. We then close the ui document (which is the front end representation of the Order document) and re-open it, which will force the computed subform formula to be re-evaluated and show the subform that contains the embedded editor, along with our new Squishee item. If we already have items in the list, then we need to just reload the main document to see the update.
If you compare the code for the 'Add A Squishee' action button in the embedded view to that in the action hotspot on the main form, you'll find there are some slight differences but the main ideas are the same.
Deleting documents from an embedded editor
Another area people often have problems with when using an embedded editor is deleting documents. Depending on the way a doc is deleted when you have an embedded view/embedded editor combo, some wierd things (i.e. not good) can happen. To make this work seamlessly for the users, I created an action hotspot on the OrderList (embedded editor) form. The first thing that happens when the user clicks the link is that the embedded form is put into read mode (setting SaveOptions to "0" so the user isn't prompted to save) and then an agent is called which deletes the document. The embedded view is then refreshed and the main Order document is closed. If this was the last OrderList document for this order, we get the Order document in the back end and update the "txt_OrderAvailable" flag (setting this to "0"). Finally, the Order document is re-opened in the UI. This process of opening and closing the main document occurs so quickly that it is imperceptible to the users.
Additional ideas to add
In the production database where I implemented this technique, there are several other bits of functionality that I used that you could consider adding as well. These include:
- Allowing the main document to be deleted only from an action on that form. This will make sure you are not left with "orphaned" documents when you get rid of a main document.
- Preventing a user from opening the documents based on the embedded editor form. We show these documents in views throughout the database but we did not want a user to open these directly. Code in the QueryOpenDocument event in each view prevents this from happening. Taking it one step further, the actual "parent" document is opened instead, navigating the user directly to the tab which shows the embedded view/embedded editor. Nice, eh? You can see an example of this if you open the 'Other Stuff" view in the sample database.
Aside: Another trick I found many developers don't know is to navigate to a particular part of your document when you open that document via script. There's a handy trick to be found here. When you call the EditDocument method of NotesUIWorkspace, there is a parameter (documentAnchor$) that allows you to open the document to an anchor link. But wait...how can you use this in a form? Therein lies the trick. Go to any old document and create a new anchor link, giving it whatever name you want to use. Now select and copy this anchor link. You can then paste it wherever you'd like on your form and it will then be available to use programmatically in any document that uses that form. You can see this in the sample database by looking at the Shopping Cart tab on the tabbed table in the main form. Pretty sweet...
While it seems there are a lot of odd ball hacks happening here, the bottom line is that in usability testing, our users found the functionality exhibited here to be very easy and relatively obvious. We actually observed users that were completely new to the process and new to Notes who had no difficulty figuring out how to add, update and delete documents using the embedded editor. While some might frown on this code for not being "pure" or "elegant", the fact remains that it has had a positive impact on this business process, which, in the end, is what really counts.
[Update: People were having problems with getting the sample database from box.net, so I moved it and made it a direct download. You can get it here. Cheers...-cmb