Problem Description

In this article I will show you how to elegantly link documents in a Notes database with the use of event handlers. At first glance linking documents together seems to be a very trivial task. It is something we all implemented hundreds of times already, either using built-in Notes response hierarchies or through building document trees of our own design. Doing that programmatically while using backend classes is as trivial as it sounds, but letting the user do it interactively is something else entirely. Because then there are certain additional requirements that should be implemented to guarantee a consistent and hassle-free user experience.

So let us take a look at those requirements for the two standard use cases:

1. The user links an already existing document to the currently open document (through a dialog field or a view selection). The title of the linked document should be displayed in a field and the document can be opened through a hotspot.

Requirements

  • If the user doesn’t save the currently open document the link should be voided i.e. the field should be empty and the hotspot action should not execute.
  • The operation should not access backend classes to avoid replication and safe conflicts.

2. The user creates a new document from an already open document. The new document should be linked to the already opened one and its title should be displayed in a field (and can, after the initial save, opened through a hotspot).

Requirements

  • If the user doesn’t safe the new document AND the current document the link should be voided i.e. the field should be empty and the hotspot action should not execute, even if the new document has been saved.
  • The operation should not access backend classes to avoid replication and safe conflicts.
  • Every change the user performs on the new document should immediately be reflected in the old document.

 

Implementation

During the following demonstration I will call the document that provides the link the parent and the link target the child. I also will not make use of the Parent-Response-Hierarchy but will implement my own hierarchy using document IDs I generate myself programmatically. I am sure you know why that is ;-). Of course you can use a Parent-Response Hierarchy instead and, if you are feeling lucky, you can even use Universal IDs to define the link.

 

Case 1: Linking to an existing document

The first case is more or less trivial. The child already exists and will not be opened during the linking operation hence we actually can (have to actually) access it using backend classes, specifically an instance of NotesDocument or built-in functions e.g. a dialog list field.
We create a dialog list field on the parent form and populate the choices with a column of a lookup view. The column formula should be something like the following:

doc_title + “|” + doc_id

Now make sure “Allow keyword synonyms” is checked in the field properties and you are done.

If you have to actually use backend classes, for example in conjunction with the NotesUIWorkspace Functions PicklistCollelction or PicklistStrings you can use the NotesUIDocument Function FieldSetText to set the value. And yes you can set computed and even hidden fields that way, too. I normally use a dialog list field even if the field is computed and set with FieldSetText. That way I can set the doc_id and after a NotesUIDocument.Refresh the displayed value is automatically translated to the title. For this to work you of course have to check the option “Refresh choices on document refresh” in the field properties.

The hotspot action performs a lookup in the same view (the first sorted column has obviously to be doc_id) and opens the document.

 

Case 2: Linking a new document

This case requires a little more work. So let us split the requirements into two steps to make it clearer.

For the purpose of code clarity and management we create persistent backend classes which serve as a wrapper for NotesUIDocument instances. We accomplish that through creating an instance of our wrapper in the PostOpen event and saving the reference in a variable declared in the Globals section of the form. For more details read my previous articles on the subject:

Event Handling mit LotusScript Teil I - Persistente Event-Handler

 

Step 1: Creating the child

In the backend class for the parent we first declare a private member variable:

Private childUIDoc As NotesUIDocument

 

And the following sub:

Public Sub actionCreateChild()

   Dim ws As New NotesUIWorkspace()
   Dim db As NotesDatabase
   Dim childDoc As NotesDocument

   Set db = ws.CurrentDatabase.Database
   Set childDoc = db.CreateDocument()

   Call childDoc.ReplaceItemValue( "Form", "childDoc" )
   Call childDoc.ComputeWithForm( False, False )

   Set me.childUIDoc = ws.EditDocument( True, childDoc )


End Sub

 

That sub will be called by the action which we use to create the child document. In effect our persistent instance of the parent wrapper class now has a handle on the NotesUIDocument instance of the just created child.
 

Step 2: Handling Child Events in the Parent-Wrapper

Next we want to notify the parent if and when the child gets saved or closed. For that purpose we bind the corresponding events, which are fired by the child NotesUIDocument instance, to event handlers we define in our parent wrapper.

Public Sub actionCreateChild()
    
    Dim ws As New NotesUIWorkspace()
    Dim db As NotesDatabase
    Dim childDoc As NotesDocument
    
    Set db = ws.CurrentDatabase.Database
    Set childDoc = db.CreateDocument()
    
    Call childDoc.ReplaceItemValue( "Form", "childDoc" )
    Call childDoc.ComputeWithForm( False, False )
    
    Set me.childUIDoc = ws.EditDocument( True, childDoc )

    On Event PostSave From me.childUIDoc Call OnPostSave_Child
    On Event QueryClose From me.childUIDoc Call OnQueryClose_Child

End Sub

 

All the PostSave event handler has to do is to set the doc_id and refresh the NotesUIDocument instance of the parent document. This way the current title of the document is always displayed.

Public Sub OnPostSave_Child( Source As NotesUIDocument )
    
    Call me.mUIDoc.FieldSetText( "child_id", _
                                  Source.FieldGetText( "doc_id" ) )
    Call me.mUIDoc.Refresh()
    
End Sub

 

In the QueryClose event handler we first check if the child has been saved. Since we created the document using backend classes we need to check the property IsNewNote on the backend document rather than the property IsNewDoc which is provided by the NotesUIDocument instance (and hence will always be false). If the child has been saved we do nothing, everything already has been taken care of by the PostSave event handler. If it has not been saved we reset the link field to the empty string, thus removing the link.

Public Sub OnQueryClose_Child( Source As NotesUIDocument, _
                               Continue As Variant )
    
    If childUIDoc.Document.IsNewNote Then
          Call me.mUIDoc.FieldSetText( "child_id", "" )
          Call me.mUIDoc.Refresh()
    End If
    
End Sub

 

And that is it. If the child doesn’t get saved the link will be empty. If the parent doesn’t get saved the link won’t change at all. If it was empty before the child was created it will obviously stay that way regardless whether or not the child has been saved.

 

Step 3: Opening the link

To keep the behavior consistent we also have to obtain the child NotesUIDocument instance when the user follows the link. For that we create a new sub for the sole purpose of opening the document.

Public Sub actionOpenChild()

    Dim ws As New NotesUIWorkspace()
    Dim db As NotesDatabase
    Dim view As NotesView
    Dim childDoc As NotesDocument
    Dim childID As String
    
    childID = me.mUIDoc.Document.GetItemValue( "child_id" )(0)
    
    If Not childID = ""  Then
          Set db = ws.CurrentDatabase.Database()
          Set    view = db.GetView( "lkpChildDoc" )
          Set childDoc = view.GetDocumentByKey( childID, True )
        
          If Not childDoc Is Nothing Then
                Set me.childUIDoc = ws.EditDocument( True, childDoc )
          End If
    End If
    
End Sub

 

So in theory we are done but what if the child gets opened end edited outside of the parent context? Well if we use the dialog list approach we are still done because when the child gets edited the view entry will change and the link title will be updated the next time you open the parent (into edit mode). But what if you want the parent to reflect changes in the child immediately?
 
There probably is a reason why you made one document the child and the other the parent and not the other way around. You built your hierarchy to reflect some piece of business logic or maybe even real logic ;-).

In consequence it is highly likely that the user is already editing the parent when he opens the child. Hence updating the parent programmatically through the child without having the parent context invites all kinds of trouble. So don’t! Rather write an agent that runs once a night and performs a ComputeWithForm on all parent documents.


 

 

Neues vom ITWU-Blog

Unsere ITWU Simple Integration Engine – An einer Stelle konfigurieren und mit dem gängigen Standard per REST-Aufruf ganz „ISIE“ überall nutzen! - Weiterlesen
ITWU-Projektvorstellung: Die kluge Scan-Lösung - Automatisiert Lieferscheine scannen und prozessintegriert verarbeiten mit dem Canon Scan-Import - Weiterlesen
Das neue HCL Download-Portal My HCLSoftware (MHS), EOS Domino V11 und Sametime V11.5 - Weiterlesen
HCL Lizenz Update: Umstellung auf Complete Collaboration Lizenzen (CCB), EOM und EOS für alte Lizenztypen und Änderungen der jährlichen Preissteigerung - Weiterlesen
Update-Info: HCL Notes Domino 14 Fix Pack 1 - Weiterlesen
Mit ITWU Simple Signature automatisch generierte HTML-Mailsignaturen für HCL Verse und Notes erstellen - Weiterlesen
ITWU-Projektvorstellung: Digitale Werkszeugnisse automatisch erzeugen, standardisiert und transparent - so geht smarte Qualitätssicherung! - Weiterlesen
 zum Archiv