For those of you who attended my @Formulas meet Ajax session at Lotusphere this year, you will recall me covering how to create JavaScript classes for Domino objects with just @formulas. I gave examples of how to create a NotesSession and a NotesDocument class for JavaScript. Jake has done a similar thing with the NotesSession class. For today, I want to cover how to create a JavaScript NotesDocument class. All of the code/forms/folders for this is already included with DWT so go over and download the latest version if you haven't already done so.
Getting Started
To create an instance of the JavaScript NotesDocument, you simply do this:
var doc = new DWT.domino.NotesDocument({"unid" : unid});
Behind the scenes, an XMLHttpRequest is made to the following URL:
/db/($dwt.SwitchForm)/unid?OpenDocument&form=dwt.NotesDocument.json
where db = the database you are currently in and unid = the DocumentUniqueID. ($dwt.SwitchForm) is a folder that you copy from the DWT database into your database and it's sole purpose is to execute a form formula in order to display the contents of the document referenced by the unid in the form we reference with the "form" parameter.
The Folder
I chose to use a folder instead of a view since there is no extra overhead for a folder whereas the indexer on the server has to keep a view indexed. The reason this works is because Domino automatically can detect if the string that appears after /db/view/ is a UNID or not. It then retrieves the document regardless if it's in the actual view or folder. It's kind of like how the /db/0/unid?OpenDocument URLs that a lot of developers use works. Once Domino recognizes a unid, it just simply finds and opens the document. The added bonus is that it will still execute the Form Formula in the view/folder specified in the URL.
The Form Formula
The Form Formula for the ($dwt.SwitchForm) folder is simply this:
@UrlQueryString("form");
Pretty simple, isn't it? My first solution was to come up with a separate folder with it's own Form Formula for each form I needed to switch to. However, after I started accumulating more and more folders with their own custom Form Formula, I sought out a better way to do this and this is what I came up with. Now of course, there might be a better way to do this and some of you I'm sure will point this out. Or will bring up that this could open a whole can of worms with users who discover this URL and start playing with the form= parameter to see the same document open in different ways. I'm with you on that and I'm open to suggestions. :)
The Form
The form that makes all of this work is called dwt.NotesDocument.json and needs to be copied from the DWT database to your own database. It contains the code that produces JSON to send back to the browser that, in turn, is evaluated and stored as an object (in this case it will represent a NotesDocument object).
The contents of the form looks like this:
{
"Authors": ["<Computed Value>>"],
"ColumnValues":[],
"Created": "<Computed Value>>",
"CanEdit": <Computed Value>>,
"HttpURL": "",
"IsNewNote": "<Computed Value>>",
"IsResponse": "<Computed Value>>",
"IsUIDocOpen": "",
"Items": "",
"LastAccessed": "<Computed Value>>",
"LastModified": "<Computed Value>>",
"NoteID": "<Computed Value>>",
"ParentDatabase": "",
"ParentDocumentUNID": "<Computed Value>>",
"ParentView": "",
"UniversalID": "<Computed Value>>"
}
And once a document is opened in this form, the results look like this:
{
"Authors": ["Jack Ratcliff/DWT"],
"ColumnValues":[],
"Created": "09/19/2002 02:28:08 PM",
"CanEdit": false,
"HttpURL": "",
"IsNewNote": "0",
"IsResponse": "0",
"IsUIDocOpen": "",
"Items": "",
"LastAccessed": "10/21/2004 12:46:32 PM",
"LastModified": "09/19/2002 02:28:08 PM",
"NoteID": "NT0000097A",
"ParentDatabase": "",
"ParentDocumentUNID": "9336C7B92E2D982C85256C390075EF02",
"ParentView": "",
"UniversalID": "9336C7B92E2D982C85256C390075EF02"
}
Working with the object
So, now that you have an instance of the NotesDocument class in your own object variable, how can you work with it? Well, it's just about the same as the LotusScript equivalent. So, far instance if you wanted to know the created date of the document you would do, doc.Created. If you wanted to know if the document was a response document, you would do, doc.IsResponse, and so on.
What about Items?
Well... it's coming. You can see what the JSON will look like by copying the dwt.NotesDocumentItems.json form to your database and open a document using this url.
/db/($dwt.SwitchForm)/unid?OpenDocument&form=dwt.NotesDocumentItems.json
What I haven't decided, however, is if the Items property should be populated when a NotesDocument class is instantiated or should the items only be fetched via a method, such as getItems(). Your opinions are welcomed.
Hopefully this excites you as much as it does me. I think that having a JavaScript class library for the Domino objects will greatly help with advanced web-based applications. What do you think? Would you use something like this if it were available?
Nice stuff.
What about methods though? Will you be adding .open(), .edit() and .delete() to the document class?
Well, I'm thinking of having the JavaScript Classes for Domino map to the LotusScript/Java Classes that already exist for Domino. There isn't an 'edit', or 'open' method to the NotesDocument class. There is, however, a 'remove' that does a delete. To open/edit a document, I'm thinking of having a NotesUIWorkspace (or maybe call it DominoUIWorkspace) JavaScript Class for that. What do you think?
Hadn't thought about it from that angle. Makes a lot of sense. The DominoUIWorkspace sounds better than NotesUIWorkspace as, well, it's not Notes ;o)
This is very nice. However, I don't think exposing all items to JavaScript is a good idea. As Stan Rogers pointed out in a discussion on LDD forums { Link } , "hide-whens and form formulae have so far been legitimate security in web applications". At least this functionality should be optional.
Jack, Jake et al,
Great to see the start of Domino/Ext integration and thanks for sharing your efforts!
It seems like there is an attempt to mirror LS/Java classes in JS, however there's a fundamental difference: in LS the calls are synchronous (i.e. your code continues, once the call is completed). In JS these function call require a browser/server roundtrip and therefore will/should be asynchronous (otherwise they will possibly lock up your browser).
Take for example an action button which:
- search for Notes documents matching a keyword, get a collection back
- loop through collect and change status of doc and save doc.
Any ideas how to approach this?
Hi Luc,
You bring up a good point so I'll blog about it in more detail in a few days. With Ajax calls, you *could* make your calls synchronous if you wanted to. And in some cases that may be ok. My plan is to give the developer the option to decide. If you decide to make the calls asynchronously, then you would pass in a callback function to handle the request.
Jack
Hi Jack,
Looking forward to your blog post!
Making synchronous Ajax call is by many considered as a bad practice because it may (irreversibly) lock-up your browser.
On the other hand, when you try to program some non-trivial logic (such as looping over documents and modifying them) using callbacks (chains), this will quickly become a mess. Some have suggested an approach called Continuations.
I was Just wondering what your thoughts are on this.