Saturday, April 01, 2006

Embedded Editors That Work

In talking about certain aspects of Notes development, my friends and I often use a football analogy to describe functionality which is almost great, but not quite. Many times, Lotus drives it to the five yard line, in some cases making spectacular moves along the way, only to fumble the ball and lose the game. One such example is the Embedded Editor. I decided to do a write up on this, as I was surprised how many of my colleagues did not know about this functionality. When tweaked, it can be a powerful UI construct.

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:

kwik-e-main


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

18 comments:

Anonymous said...

I created a box.net account but wasn't able to download your sample database.

I was wondering if you could email it as the ideas seem pretty slick. The email address is steven.cannon@checmail.com

Thanks in advance.

Steve

Wayne Sobers said...

Thanks for this sample.
I have done something similar using an action bar on an embedded view. The result works but feels and operates like a kludge.
I agree with the comment on how far the client interface takes you. We are on the 6.5 client for the time being, so there are some annoying design issues. The main one being that some progromatic changes require the database to be closed and opened in order to see the changes. Frames are another area where the notes client could really shine.
Anyway your solution is very helpful, thanks again.
Wayne

Nicolas Boureau said...

Before reading your article I make somthing similar with dialog boxes .. but embedded editor are just awesome for my kind of application. thanks a lot to share it !!!

Ram said...

Hi,
Its nice article. I am trying to use the same technic but i am facing some issues in this approach. Lets say if i enter some info in Customer info tab and then if create new Squishee and then if I go back to the Customer info tab there I am loosing the data which i have entered. Any solutions for this.
Thanks & regards

Keil said...

Chris, one thing worth mentioning is that in order for the embedded view to target the editor properly, the embedded view must be inserted below the embedded editor. I had the view inserted above the editor on the parent (container) form, and when I refreshed the parent form manually(F9), the embedded editor fields would go blank. I also couldn't get the field values in the editor to update programatically at all. I copied/pasted the embedded view below the embedded editor and viola, it works like magic.

Chris Blatnick said...

@Keil...perhaps this is a version issue, as in 6.5 I have successfully had the embedded editor both above and below the view. What version were you trying this in? Do you have any different result if you copy the embedded items to a new test form?

Sasa said...

Thank you for this sample!
I found your site by looking some info on embedded views at developerWorks forums - I like what you are doing and it really makes Notes applications much more appealing and user-friendly.

A question about this sample: is there any (dis)advantage to using response documents to create order list? Wouldn't that make connecting main document (order) and order list automatic and thus make some actions (update, delete) easier?

Regards,

Sasa

Chris Blatnick said...

@Sasa...that's a great question. Ideally, response documents probably would make a better choice, since as you point out, routine maintenance and actions could be made easier. The only reason I did not use repsonse docs here is that the requirements of the real application that I took this from dictated that I not use response documents (due to the way documents were linked to other applications in the system). That said, I haven't used this method with response documents and I don't see any immediate problems that could arise, but I would make sure to test and be sure it all works correctly before you go down that path. Cheers!

Paul G said...

Hi Chris, cheers for your site; awesome stuff, as is your sample db here.

I have a similar need for this a in manufacturing app with a BOM, but I have a need I am researching.

Do you think the displayed embedded view could lookup another database for documents programmatically? I want to store lots of documents in separate DBs (for indexing purposes) and want to have the embedded view formula compute to the same view name in another database.

Are you aware that this can be done? Trawling ldd, openNTF and other locations has turned up blanks.

I have a hunch that I can't set the embedded view formula to "Notes://serverName/DBRepID/ViewUNID".

Any pointers for me?

Cheers, Paul

Chris Blatnick said...

@Paul...the way I have handled this in the past (which I admit might not be the safest method), is to create the view I want in the other database, embedded it onto a temporary form then copy and paste this embedded element into the target database. Once it is there, you can manipulate it as you normally would, including using 'show single category', etc.
So far, I haven't really had any problems with this (knock on wood!).

Allan said...

Chris, this is a GREAT sample and gave me just the piece of info I was missing to complete my own application using embedded views as a picklist. Just prior to finding your page here I came across someone else asking the same question I had on LDD so I replied this morning pointing them here as well.

You are doing great things and certainly helping others in the process!! :)

Thank you for this sample!

Oh yes, I love the Simpsons so this was quite fun for me as I worked my way through your DB.

Hoatzin said...

Chris,
when I use an embedded editor in combination with an embedded view in a form on a DialogBox, the embedded editor opens in edit mode and no document is shown.
Is this a known problem to you, and do you have an answer?
Thanks in advance.

Aidan said...

Chris,

Firstly, great example and I've now managed to incorporate this into a new project I'm on.

Have you experienced any problems with Notes R8? My implementation always seems to open up the embedded editor in a new window (despite working fine in previous versions). I loaded up your Kwik-e-mart example and it has problems updating newly added items.

I don't expect you to solve my problem, just thought you might want to update your example.

Mark said...

Hi there...great site. Only issue I find with the embedded editors is the annoying "Current Document Is Being Edited".

Does anyone know of a way to allow users to transverse to the next document and ABANDON changes? Seem like at worst Notes should ask if you want to Save Yes/No but it doesn't!

Mark

Chris Blatnick said...

@Hoatzin...Sorry, I've never tried an implementation like that in a dialog box. If I get a chance, I'll try and report back here.

@Aiden...same answer. I haven't had the opportunity to try these in Notes 8, but I've heard there might be some issues. I am going to get to the bottom of this and let you guys know what I find.

Chris Blatnick said...

@Mark...Yes, the way I get around this issue is as follows. (I've used this in a couple production apps now and it works like a charm):

Create some computed for display fields on the embedded editor form to hold the initial values of the fields you are allowing the user to edit. Set the value of the fields to be something like:

@If(@IsDocBeingLoaded;
field_1;
@ThisValue)

Thus, when the doc is opened in the embedded editor, these fields will all get the value of the corresponding fields. If you want to "Cancel this action", just make sure to set the fields back to their initial values, then save the doc. Something like this:

@SetField("field_1"; field_1_OnOpen);
@SetField("field_2"; field_2_OnOpen);
@SetField("field_3"; field_3_OnOpen);
@Command([ViewRefreshFields]);
@PostedCommand([FileSave])

That should do it!

Anonymous said...

Hi Chris,

Thanks for sharing the design. But, I wonder is it possible to just embed the editor without embedding the view? E.g. the system will default to open a document for editing in the embedded editor.

thanks,
yc

Chris Blatnick said...

@YC...The embedded editor element is designed to be wired directly to the embedded view. Without it, you basically just have a standard form within a form and while this works, it's not really practical in any way.