Notes Thread Navigation With DHTML
As Domino developers we are all used to the idea of response documents arranged in a threaded hierarchy. We see them all the time in Notes views. They're fairly easy to create. All you need is a view, containing parent and child documents, that has a response-only column. You get a simple but effective way of navigating the resulting document structure.
What's never been easy is duplicating this functionality on the web. The de-facto solution was to use a custom view, a lot of fields and a dblookup. I talked about this approach in this article, some time ago. Since then we've seen the View Applet grow in popularity but we are still without a satisfactory way of mimicking the client's features. Hopefully, I can offer a nice alternative for you.
Let's take a simple example of a discussion thread. In the Notes client, here's what we would expect to see:
Looking at the same view in the browser, by default, we see this:
It's the Java View Applet and it does a fairly good job of replicating the functionality of the client. We don't like the applet though, do we? Oh no sir, we hate the applets!
Turning off the applet and using plain HTML we might see something like this:
Again, the functionality is there but it's ugly and requires a lot of server traffic. What we want is something light-weight and easy to use. That's what this article is about to describe.
JavaScript to the rescue:
Have you ever thought to yourself that you could create some really fancy views, all by yourself, using some DHTML? I know I have. The only thing that ever stopped me was knowing it would take a hell of a long time to do it. What would be better is if somebody had written it already, in a form that could easily be plugged in to Domino, and that they were giving away for free. Well, somebody has!
The code I am talking about is called dTree. It's a really nice script that works well across most browsers and is fairly easy to configure to meet your needs. Have a look at this example to see how you can alter the configuration in different ways.
What's particularly nice about dTree is the ease with which it can be used in Domino. That's what the rest of this article will be devoted to. First a simple example:
Just to show that it is configurable, here's the same tree but without the folders, icons or lines:
And, finally, a sample implemented in a Domino database. This is what we are working toward in this article and the template is available for download at the bottom of this article.
Out of interest, here's how the script is promoted on the author's site:
- Unlimited number of levels
- Can be used with or without frames
- Remembers the state of the tree between pages
- Possible to have as many trees as you like on a page
- All major browsers suported
- Internet Explorer 5+
- Netscape 6+
- Opera 7+
- Mozilla
- Generates XHTML 1.0 strict validated output
- Alternative images for each node
Using dTree:
Before we look at using it with Domino, let's see how it works on a normal page. Here's the source JavaScript requried to generate a simple tree:
d = new dTree("d");
d.add(0,-1,"Codestore Navigation");
d.add(1,0,"Related Articles");
d.add(2,1,"Article 1","unid/DOMM-4Y9KZH?OpenDocument");
d.add(3,1,"Article 2","unid/DOMT-5UBUVW?OpenDocument");
d.add(4,0,"About Codestore","about?readform");
document.write(d);
Simple isn't it!?
The method for adding a new node to the tree is called "add()" and the parameters are as follows:
Name | Type | Description |
---|---|---|
id | Number | Unique identity number. |
pid | Number | Number refering to the parent node. The value for the root node has to be -1. |
name | String | Text label for the node. |
url | String | Url for the node. |
title | String | Title for the node. |
target | String | Target for the node. |
icon | String | Image file to use as the icon. Uses default if not specified. |
iconOpen | String | Image file to use as the open icon. Uses default if not specified. |
open | Boolean | Is the node open. |
Example:
mytree.add(1, 0, 'My node', 'node.html', 'node title', 'mainframe', 'img/musicfolder.gif');
The fact that we can apply this so easily to Notes hinges on the first two parameters. The first is the unique ID of the node and the second is the unique ID of its parent. Think of this in Notes terms and we soon see that we can make both parameters the @DocumentUniqueID of the documents and its parent, respectively.
Note: Although the API of the script says that the first two parameters are Numbers, it's not strictly true. We can use the 32 character hex id of a Notes document!
By now, you should start seeing how this would take shape in a Notes view that shows response documents in a hierarchy. The two systems seem made for each other.
Using dTree in Domino:
Hopefully this won't turn in to a lesson in creating response views. We should all be comfortable with this and I'll try and skip the gory details. There's a sample database you can download at the end of the article, so anything you don't understand will be demonstrated in there.
Obviously we need a view. Before I create this I will assume a few things. Firstly, that we are using a simple response document hierarchy consisting of a standard form called "main" and a response-type form called "response". Secondly, that both of these forms contain a field called "MainID" that contains the document unid of the top-most document in the thread.
Create a view and call it "(Threads)". The selection criteria should be something like Select Form="main":"response". By default the view will have the property "Show response documents in a hierarchy" enabled. Leave this so. The only other property to change on the View is to make sure that "Treat view contents as HTML" is selected.
The view needs three columns. The first is categorised column and displays the MainID field I mentioned. Because it's a categorised view we can add it to a form and restrict it to only show documents in one category. In our case we restrict it to the thread we are interested in by using the MainID field of a document. This way we only ever see documents relating to the thread we are interested in.
The second and third columns show the response documents and main documents, respectively. The formula for both columns is very similar. The main difference is that the second column has the property "Show responses only" enabled. You don't have to have the columns in this order but it makes sense in Designer when you view the data, this way. First lets look at the third column's formula:
@If(form="main";
"mytree.add('"+@Text(@DocumentUniqueID)+"', -1, '"+Title+"', '0/"+@Text(@DocumentUniqueID)+"?OpenDocument')"
;"")+@NewLine
First we test to see if the form is the main document. If, and only if, it is we create the "root" node for the tree. This is special because it has the parent id of -1. All trees have to have a root node with this id! If it's not the main document we simply have an empty entry in the column. Note that this "root" node has the ID of the document id of the main document. By definition this is the value of MainID and the same in all documents in the thread.
The second column has the following formula:
"mytree.add('"+@Text(@DocumentUniqueID)+"', '" + @Text($REF) + "', \""+ Title + "\', '0/" + @Text(@DocumentUniqueID)+"?OpenDocument');"
The trick here is that we've used the reserved field called "$Ref" to access the ID of the document's parent. This way we can ensure that every node refers to its rightful parent.The resulting code from both columns would look something like:
mytree.add('246A0A59E91E277486256E1D00812489', -1, "The first main topic", 'all/246A0A59E91E277486256E1D00812489?OpenDocument')
mytree.add('409C234D9868F94586256E1D00814AB9', '246A0A59E91E277486256E1D00812489', "First response (Jake, 16/01/2004 11:32)", 'all/409C234D9868F94586256E1D00814AB9?OpenDocument');
Note how the first response we add has the parent id parameter which is the same as the root node, its parent. Also, that the second node has the parent ID that's the same as the main ID - because it's on the first level of responses.
Now we need to place this view on both main and response forms, so that, whenever you open a document in a discussion, you get to see the whole thread as a navigation tree. First thing to do is add the required JavaScript file for the dTree code along with the dTree CSS file. You can download all the files needed here.
Extract the zip contents to your hard-drive and get ready to add the ones we need to your database. I'm assuming we're all working with Domino 6, although this script will work with any version.
Import all the images from the "img" folder as Image Resource elements in Designer. Rename them if you like so that they are easily recognised:
Create a new JavaScript Library in Designer and paste in the text content of dtree.js. Save the library and call it "dtree.js". Import the dtree.css file in to the Style Sheets section of the database's shared resources. These are all the files you need in order to get started.
Now, in the HTML Head Content section of both forms, make sure the CSS and the JavaScript files are included. If you don't know how to do this, don't worry, the example database shows you how.
Note: Depending on where you placed all the images and how you named them you may need to edit the configuration from within the dtree.js library.
On both the forms we need to embed the (Threads) view. We do this inside <script> tags, like so:
The important step is to make sure that the view is restricted to "Show single category", using the simple formula - @Text(MainID).
Can you see how it all works now? Because the view is treated as HTML and it lives inside a <script> tag, it simply makes a series of calls to the tree's .add() method and build us our navigation. As simple as that. Here's the sample database. Download it and play around.
Taking it further:
Had any ideas about how you can use it yet? Maybe you just want to use it like I've demonstrated, for discussion threads. This isn't the place you can use it though. Obviously, it can be used for displaying documents in any kind of parent/child relationship. Also, it can be used for site navigation at a higher level. Even if you have to hardcode the IDs and links, it's versatile enough to be used all over a site.
If you look at the API documentation for dTree you should get some idea for further enhancements. Notice how you can specify the icon for each node on a document-by-document basis. You could use this to highlight documents or for whatever purpose you see fit.
Summary:
Whether or not you read this whole article you should all be able to get at least something from the content. I would imagine most of you will simply want to download the demo database and take it from there. It's would be too hard to cover how to create the whole thing from scratch. All this article is meant to do is accompany the download for those who might not be comfortable with working out how it all sits together inside an NSF.
Way cool & a warning
Thanks Jake! Just what I was looking for. dtree is exactly the kind of javascript API I like. It's simple, does only 1 thing very well, has an easy to understand API, very little config/setup and do on. Too many dhtml APIs are humungous and try to offer everything under the sun. The end result is often a confusin mess of an API and a huge config burden just to get started. Some more simple APIs can be found at http://youngpup.net .
Anyways, I noticed that the dtree code uses the ids to write the current tree state to a cookie (if enabled). Cookies do have a maximum length for cookie values, last time I checked it was 4k. With your example thread and all nodes open the cookie looks like this:
"409C234D9868F94586256E1D00814AB9.29E6FF4DA81977EE86256E1D00815498.9117CC01D83A7 17686256E3E003C0D08.E7829B0A4504AB6086256E3E00467693.74C22D0B707E662086256E3F002 DD052.E4DA017B1C3F684986256E3F00698333.AE7DF2A1AB4E95A086256E3F006B906B.3DEC0F9A F65152FA86256E3F006B98EA.7D143565619780FB86256E3E0052CD4B.5285FBB8F382329886256E 1D00817028.FF1C622AEFE6525186256E3E003B56D0.AE47DA647858A2FF86256E3E0053BDDC.944 208A3EB3CE8F286256E3F00540FE7.D95EC20C8C45C94680256E1E00572367.83D46D4AF7B40FFC8 6256E3E003AF24D.C4BBC3E4B803887886256E3E003B4B83.A15840E0423F970F86256E3E003B0E9 C.0EC57DB9241C718186256E3E0061C64D.628AEAFAECC9C39486256E3E0075DE5E"
Which is only 626 bytes long. My point is a huge view with lots of tree nodes open may blow the limit. Small chance I know, but something to keep in mind.
Anyway, off to implement! Thanks again.
Reply
Great article Jake!
Great article Jake - I'll be testing this out with a view to making navigation in a few of my webapps a tad more intuitive.
Y'know I could never spell Feburary either ;) Justin
Reply
Well, nice, but..
I made a similar javascript library, for identical purposes, few months ago (not as advanced though :). What i realized is that acting this way was not a good idea, because i was losing some views benefits ; especially the facts that you always get a small chunk of the results ; you collapse a thread, you expand another, etc. Thus, users were not downloading a huge html page, but lots of small html pages. When you have thousands of entries, i'm sure that's much more convenient (would your users accept to download ~x00Kb pages and wait for the tree to compute ?). And browsing on several pages does not help us in that issue.
Reply
Re: Well, nice, but..
Good point YoGi.
Something I forgot to talk about was view document counts. By default this is 30. So, if a discussion goes beyond this number of document, you won't see them!
There are ways round this. You can add count=-1 to the end of all the URLs or you can change the default count property of the embedded view to a higher number.
I know it means more HTML to download, but so do all discussion templates where you see the thread of every page. The Notes.net forums, for example.
Jake
Reply
Re: Well, nice, but..
So I am agree with YoGi. Same way I did before 4 years. I’ve created domino database based folders tree. It works fine in small databases and degrees download time in big databases. More documents in view - more time to download it. And in very big views tree generation take unacceptable time. The solution we found is to load only first level nodes while page downloading, and each node click downloads its sub-nodes. The advantage is it loads and renders very quick, - the disadvantage is it works only in MS Internet Explorer because of using Microsoft.XMLDOM ActiveX Control.
Fabian Nirman
SharePlace.info
Reply
Show the rest of this thread
A nice feature is the auto expand a defined node
A nice feature of the script is the ability to expand automatically a defined node.
Take for example a help application which uses the tree in the left side whilst displaying the help document in the right - you could link directly to the appropriate help document (from within another application) and once opened auto expand its place in the tree.
Works very well and gives context sensitive navigation. I guess this would work just as well for site navigation.
Reply
Slight Problem With Your Code
Jake,
I have experienced a minor issue with your code in your article.. I had to modify the "(threads)" view, column 2 formula to the following:
"mytree.add('"+@Text(@DocumentUniqueID)+"','"+@Text($REF)+"','"+Title+"','0/"+@T ext(@DocumentUniqueID)+"?OpenDocument');"
otherwise I received an error if there were any responses.
Just thought I would share this incase anybody else experiences the same problem.
Other than that, its a great article.
Cheers Adam
Reply
Re: Slight Problem With Your Code
Forgot to say, the bit I changed is just before the +Title bit... you had: \"" (2 double quotes)
I had to change it to: '" (single quote double quote)
Ta
Reply
We sussed this about 18 monts ago
Reply
More scalable with XML
Making an XML-based view and using something like the XLoadTree script ( http://webfx.eae.net/dhtml/xloadtree/xloadtree.html ) let's you dynamically load just the part of the tree that the user is viewing/acting on. This is especially helpful in larger views -- plus the XML-tagged views can be used for various purposes with XSLT, rather than the very specific purpose of this JS implementation.
Reply
NICE! Re: More scalable with XML
I can see another article on its way. Thanks Tamer!
Reply
XloadTree view
Hi
I actually made some webviews based on XloadTree a couple of years ago. It is pretty cool but it is only useful with small amounts of documents in each category. Otherwise it is VERY slow. And IE goes bananas with RAM..it eats away ...plus the old cache bug in IE means it loads the graphics for every document from the server and not from the cache.
regards Jesper Kiær http://www.jezzper.dk
Reply
Holy Grail: the only way to go is...
Typical really. You come up with a neat piece of code and some smart arse pokes faults in it:
- Its slow to load - It won't cope with large amounts of data
The environment we've chosen to work within is always prone to these problems. Indeed anything Domino view based is always going to perform 'badly'.
I downloaded dTree and ruthlessly pulled it apart to serve my own meek needs, added xml functionality along the lines of xTree and came across all the same problems. But for me it didn't matter. My goal was to build a non-scalable tree of a known size but not a known content.
In order to acheive the holy grail though what we need is a set of basic building blocks:
- Virtually scrolling interface so that top level we can fetch data on demand and only download enough to fill the visible region. - Fetch data on demand using xml to populate all levels of the tree/view - Build all icons and images using best practice methods to avoid caching
Of course doing all of the above cross browser, modular and configurable will indeed be challenging and perhaps costly.
Reply
Re: Holy Grail: the only way to go is...
If you have a code ex. with Fetch data on demand, will you then send it to me ?
Please....
Reply
Great Stuff Jake
Just wonder, how do you implement the openAll() and closeAll() method on Domino. I havetried but failed.
Regards,
Reply
Re: Great Stuff Jake
This article is exactly what I have been looking for. However, some of my views are receiving a "Stack Overflow" or "Out of stack space" error. Any ideas why this is happening?
Reply
Limitation of the number of nodes in the tree
Hi, I'm using the dtree. and if the number of nodes goes beyond 1000, my internet explorer 6.0 shows an error saying that "the script might cause the browser to run slow".. any workarounds or solutions will be helpful..
regards, Gaurav
Reply
Re: Limitation of the number of nodes in the tree
It is posible to use @DocNumber and @DocParentNumber instead of the 32 character long DocumentUniqueID. This will probably give you some more documents in the view before you start to get the Error msg. And it will be a lot less characters to load into the browser.
Then the code would lokk something like this:
ID := "'" + @DocNumber + "'"; PID := "'" + @DocParentNumber + "'";
"myMenu.add(" + ID + "," + PID + ",'" + Name + "','" + link + "');"
Reply
Long line wrapping?
Great article. I only have one question. Is there a way to make text wrapping look good - not cut at the right side ('white-space: nowrap;' in the dtree.css file) and indented at the left side? Something like this site http://javascript.cooldev.com/scripts/cooltree/demos/wrapping/ This site's example only works in IE but I would really like to see the Gecko solution. Maybe I should ask Geir Landrö, the creator of dTree.
Reply
Limitation of dtree
Hi, I am facing a problem while using dtree.js when the size of the data is increased it is unable to load all the data. So, i would like to know if there is any limitation in the dtree.js.
Reply