« Programming your own mind | Main| You know you've been coding too long when... »

What if they had a Revolution...


...and nobody came?

Did Tim and I leapfrog our audience with the latest spoon-bending efforts?  My emails go unanswered, the one pointer to this stuff come from my colleague (who made a significant advance with this technique yesterday) and people look at the demo database and say "I don't get it."

Folks, this is sharing Lotuscript object references between tabs.  Shared objects between NSF, between frames, between embedded elements, and in the case of ActiveX controls, between applications!

And I can barely get a "I might find this interesting if you weren't so puffed up about it" in the comments.  Bah.

So, if you have a question on how this works, download my or Tim's example.  If you have an idea on where this technique could be used, post it in a reply here.  I'll start...

1) A global instantiation of audit trail/logging instances for an entire NSF context.  Imagine establishing an OpenLog session in the NotesUIDatabase PostOpen event, and keeping it until the application is closed.  How much faster would it be to write log documents?  How much more might you log?  Since you could actually keep the same DOCUMENT open the whole time, why not do the equivalent of a DEBUG_OUTFILE and record every move the user makes in your application?

2) Cross-frame COM objects.  Tim and I showed a technique for full-page web browser linkages in frames the other day.  But that creates an entirely new Browser object for each document selection.  With The Revolution, you could render a browser object in a frame and update it dynamically, say, based on document selection in a view in another frame.  Imagine having a list of Taco Bells in your view, and as you select each one, they're adding to a Google Map in the browser frame.  Sound impossible?  It's easy, if the view has the COM object in the other frame available, you're literally using browser.document.addAddress(notesUIView.documents.getItemValue("Address")(0))

3) Need to get the selected document collection from an embedded view?  We've shown how to do it before, but it was necessary to match the view in a hidden frame.  That has the downside of prohibiting the use of "switch to view" column headers.  Now you don't have to worry about it.  The new view can simply reach into the parent container, inject it's UI reference, and then your containing document just says myCache.contexts("embeddedViewName").refUIView.documents  That's all there is to it.

4) Want something simpler?  Okay, you can stop trying to force a view refresh from the wrong context.  Ever have a form where you write a PostSave that includes...

NotesUIWorkspace.currentView.refresh?

Yeah, you can stop that now.  When you create the document from the view, grab a handle and do "On Event PostSave from NewDoc..." and tell the blasted view to refresh itself.  Now you can put that code where it belongs.

Starting to get a feel for it yet?

I'll tell you what, if you still don't get it, don't worry about it.  Just ask in the comments for the most outrageous thing you can think of from the Notes client.  What's something you consider totally impossible?  Interactive dashboard-style graphs?  Mash-ups?  Name it.  Let's see if The Revolution changes as much as Tim and I think it does.

Comments

1 - OK, lets suggest something that a lot of the corporate devs might find usefull.

Using a single MQ Series, or LS:DO, session at view level, rather than an instantiation per Document Script.

Previously, if response times were a critical requirement (WAN?), then the solution would have required a bit of C to hold open the pipe. Usually this just forced a UI decision which was often not optimal if not bad.

2 - I get it, and it's amazing. But I can't use it because we aren't doing any Notes client development at work. At all. So bravo, kudos, etc, and have fun with the cool stuff you can do now. Us pure web shops are focused on different issues, though.

3 - I sorta get it after digging thru the download, and reading the article thrice, but its still all fuzzy. I can see that you can definitely establish context for this (breadcrumbs), have more elaborate log files, or force dialog-box values to 'get down' to the document even before the dialog has closed like we get on all those banking / travel sites. More app-based (as opposed to technical) scenarios would be a great help!

I also get the nagging feeling that this could be used in 'Interface Matters' Chris's embedded editor example, where he closes and opens the document when one of the embedded docs is deleted using the embedded editor. Also, I think we could use this to line up the New Doc, Update Doc, and Delete doc options, instead of having the New Doc in the view action, and the others inside the embedded editor...that was something a client asked the last time I implemented this, and I couldn't think of a way to do it then.

4 - Thanks for dumbing it down, now I can think of a few scenarios that this might be useful. In particular I'm working on synchronizing embedded views with minimal user interaction. I think this technique may help with that.

5 - very cool sir. we are getting more focused on web dev too lately, but i'm still doing some client stuff. i do plan to look at this soon. i'm just so freakin busy all the time!

this IS way cool though. congrats. your gigantic brains are all set to take over the known universe. Emoticon

6 - Nathan,
I haven't dug into the fine details of this, but I had a scenario present on a recent project where I could have used this functionality. I have an embedded view with an action to create a new document which passes values from the parent document into the new document. Problem is, if the user accidentally clicks the 'New' action and then closes the subsequent new document, they are presented with a Save New Document dialog. I thought this would be a bit confusing to the user if they hadn't done anything to the new document and simply wanted to close it. I looked into setting the IsNewDoc property to false, but couldn't get a handle to it. I'll be studying The Revolution in more detail to see if it can set my IsNewDoc to false. Thanks! Emoticon

7 - I'm very keen on using this technique to tighten up and enhance SuperNTF, paricularly in terms of user activity tracking. For example, if I could add the ability to track when a user opens a view WITHOUT putting any additional code in the view, that would be big.

My two-row action bar logic might benefit as well.

Hmmmm.....

Of course, We shouldn't forget about your other recent innovation relating to embedded framesets. All very kewl. Emoticon

8 - Just hold on to you pants, dude. Most of us haven't had the time to play around with this yet. We're busy trying to finish up the current project we're working (perpetually). When I get a little time to do some "technical prototyping" I'm definitely going to look into this though. Until then, I have to admit that I can't shake the feeling that this may be another situation where Notes gives us some functionality that's *almost* totally cool, but doesn't quite work they way you need it to.
BTW - Thanks for totally nuking the "get selected document from embedded view" solution I've writing up for posting on Mr. Blatnik's site Emoticon.

9 - Thanks for the feedback, guys. Sorry if I seemed a little needy here, I'd just only gotten feedback from a couple of people, and there's just so many good ideas that should be circulating from this stuff. I want to hear them! Emoticon

Keep 'em coming.

One thought I had -- could we drive view type-ahead in an embedded view from a form field? Say by establishing a NotesTimer in the view that monitors the field in the container UIDocument?

For that matter, do we even need remote binding for this? Maybe not... a timer is a timer, right? We can always get NotesUIWorkspace.currentDocument.fieldGetText so maybe there's nothing NEW available there.

But we COULD trigger some stuff in cross-frame contexts with, say, an onChange event in a field. That might open some new doors.

10 - Nathan - it is cool stuff .. but I my previous comment to you in IM is even more valid after the silence you got ... you need simple, real-world examples. I also think you are hitting into what i call 'summer time i dont care' attitude. Between vacations and workload and so much new coming from IBM, people are just not reading blogs or playing with tech as much as they do in the colder months. So, about those real-world examples - Create some basic use-case samples for download. Tim's sample is great, but its too high level for people just playing around. Take 3 samples that are simple and detail what your doing and make those available. Create movies on how they work. Strip away anything but what your doing (pigs in space is just so distracting!). But the best demo is to take one of the IBM composite app samples and reproduce it 100% ... without using the ca editor and wiring and eclispe components. That will get everyone talking, including IBM.

11 - I'm with John on this one. It's summer. Between holidays, regular work, and keeping my boys entertained, I don't have the time to look at something I actually might use everyday, let alone concepts that might have value that I can't readily see. It's just a matter of priorities. While this all sounds cool and I applaud your usual Herculean efforts, I personally don't have the time to make Dovid play with it to tell me how cool it really is... ;)

12 - Wow this is neat stuff! Now we can open a main document, then open a response document with a postsave that updates it's parent, with no chance of the user causing his own save conflicts.

Here's a simple example for you, Nathan:
A recursive budget database.. i.e. you start with an overall budget, say for "Smart Family Budget" with a total amount and an amount allocated/spent. Then have a response document under that for "John Smart's Budget", then have response document under that for, say, "John's Beer Budget".

All forms open directly to edit mode.

You should be able to open the Smart Family Budget, then open John's Budget either from the view or from an embedded view within Smart Family Budget, then the Beer document from the view.

With Tim's idea of using a cached list of variants, you could open all three documents, in any order, change the Beer budget and then see your change immediately reflected in the the TotalAllocated fields other two NotesUIDocuments. If they're open in the UI, they'll update through the UI instead of the back end.

We could even include the % allocated in the window title of each budget document so that when the Beer budget is changed, we can see the other two tab titles update immediately. If they're open in edit mode, call the appropriate NotesUIDocument.Save. If they're not open, they won't be in the cached list, so we can update it via the back end as we always have. If the parent docs are open in read mode... the best I can think of is to switch to edit mode, save, then switch back. (I first thought of updating and saving via the back end then calling...
set uidoc2 = ws.EditDocument(False)
call uidoc.Close
... but that would change the tab order.)

13 - This is probably not the response you want to hear. My first impression when I read through it was, "Wow! Finally a way to build some pretty sophisticated Notes client apps without having to resort to really ugly hacks...too bad those days are over."

From what I see in day to day land, hardly anybody's requesting or building sophisticated Notes client apps these days. Those apps have moved over to the web side now.

I see people being asked to maintain legacy monstrosities, or newby companies looking for quick doc library type things, and nobody requesting something truly sophisticated, unless it's on the web.

I hope I'm wrong. If I'm right, I hope it's a temporary blip that Notes 8 will alleviate. However, with Notes 8, maybe this technique is less relevant with multiple windows and property brokers, I dunno, haven't looked closely enough at it yet.

At any rate, I applaud the achievement and look forward to digging into it when opportunity allows. Many thanks!

14 - Ugh. I think Lance is right. I get to do new apps every once in a while, and hope/expect to see more of it in Notes8, but starting in August when Notes 8 comes out the "right" way of doing this will probably be the property borker.

... yes, "borker" was an honest typo, but it made me giggle so I left it in. Emoticon

... Gah! Now I got that Swedish Chef song in my head!

15 - I love the idea, really keen to put it into practical use. I'd love to see a few more examples of it being used so i can fully get my head around it.

Especially interested in seeing something like your 3rd point. I've had a play but cant seem to get the embedded view to work for me ... Emoticon

Excellent work though!

16 - Nathan, a very nice article. Now, since the object is instantiated at the view level and passed to the documents being composed, I believe the object will be destroyed if the view is closed. I was just wondering could we create the object at the post open event of the database and then pass it around? The object should then be available to all the objects the database contains. Or am I mistaken?

Although dynamic loading of the script library works great and allowed us to prevent circular references and also enabled on demand loading of the script libraries, we encountered weird type mismatch error at our end when we tried to pass the built-in constant 'NOTHING' as an argument to a function in a script library that is being loaded dynamically. I had posted this problem in the Notes forum, but could not get an answer to the problem. Additionally, we cannot use typed objects, but variants only. I was first introduced to dynamic loading of script libraries a few years ago while reading a red book. Had an entire chapter dedicated to the same. Although not exactly similar to your idea, at our end, I have used the concept extensively in a multilingual application that we use. Amongst other things, we have used the concept in an agent in the application suite that loads language specific script libraries while generating emails for the end users. The object reference variable of the script libraries are "cached" in a list and depending on the language being processed, the script library is either loaded from the list or dynamically instantiated. I look forward to more articles that further this revolution.

17 - Nathan
My first reaction on reading your post was - wow this could be **really** powerful, but I just don't have time to play with it right now. It's a concept that takes some time to sink in.

But the more I think about it, the more I think I should work it out before my next project starts. Just thinking about the ability to have to two documents open in the UI and be able to update either notesUIDocument object - this is truly revolutionary. For example: if you build your application correctly, I'm starting to think you could do things like
- get a list of all open documents. Think of what that could do for you.
- possibly bring a document which is open but does not have focus to the front? Not sure but this would be super.
- way better co-ordingation between embedded objects
and so on.

I think like any true revolution, this will build momentum and gather pace before taking over. Then we will wonder how we ever did things any other way. Yes - composite apps should improve things also, but many of my clients are still on 6.5 and 8 is years away.

Bring on the revolution !

18 - Yeah Nathan. The first thing i thought about that one was WOW. This might be a solution to the problem of Database wide Inline Variables filled once and then used again and again.

So i took your database and that one Tim did and tried to play around a little bit.
I build another view into Tims database and changed between those two views. Every time i changed the objects got lost. So i see a brilliant idea, but without practical impact. Perhaps i am missing something and it would work even when changing between views?

19 - First thought when I started reading was 'my god, we're playing Zork...' how do I get the dragon away from the ice wall and where is the darn cheat book? Yeah, leapfog the audience is right on target for my pea brain.

I have the db but I'm so buried on other stuff right now, it's gonna have to wait. Based on the first read and your 'needful' update, I think this could be one of those techniques that changes how I do what I do. Between you and Chris, I am in a constant state of 'wait, I could do it this way, or that way, or wait, another way....' My productivity has fallen through the floor!

Speaking generally, the problem I have is trying to really really wrap my head around how to use some of the new stuff. I've been stuck in 'form, doc, view' for soooooo long, it took a while to see the value of embedded views so when you toss more stuff on the pile it's gonna take a while for me to 'get it'.

And thanks! Awesome stuff.

20 - @19 - being stuck in Form, Doc, View is not a bad place to be. I would say about 60% of the Notes applications I've ever written were quick and dirties. It pays to just get them up, and forms and views are still your basic friends there. All this fancy stuff starts to help when the users insist on making the app nicer, or when the app gets conplex enough that the bailing wire starts to spring away from the chewing gum.

@11 - too late, I already played with it. Of course, I'd BEEN playing with it, but Nate pcreated a better gestalt than I had done. Anyway, until our teams stops its current bread and butter of migrating the planet to Exchange, who the heck cares about Programming Power Pops Proving Prowess? Give me a good 5-to-8 migration with some change control to make the old apps better on the new platform, then we can rock.

Nate, I don't think you'll be able to go global to the Database Script level. The scheme depends on being able to refernce a "real" variable from the $EXECUTE$ module (whose accidental persistence we're relying on). Code can only access variables outside its own module if the variable is a global, public variable. For example, in your contextView, you have a global variable myCache. Since you have Option Public turned on, this is a global public variable accessible to any modules co-loaded with the view's own module, including the $EXECUTE$ module in the viewQueryRecalc routine. The same holds true for the similar myCache variable in the form. When the form's event gets consumed both internally to the form and by the externally latched routine in the view, the $EXECUTE$ module loaded by both confuses LotusScript, and it incorrectly uses shares public globals between the two $EXECUTE$ modules. This is the key to the magic.

Now, move on over to NotesUiDatabase. Stick the myCache variable in the (Declarations). Everything's looking pretty much the same. Except it won't work. because there's no Option Public in NotesUiDatabase. You can't put on in there, and you can't even override the default by delclaring Public myCache directly... because for some reason, all code in the Database Script is ocnsidered procedure-level. Hmm, I wonder if you imported it all.... or if you Use'd a special module just for the database script. Might be a way around it. Let me know if that works for you, or if you need me to play with that. I've only tried this kind of stuff with things called from agents, or the occasional one-form-calling-another (I think that was where I managed to RBOD it).

21 - @20 - First off, describing the behavior is "incorrect" is making an unwarranted assumption. It's certain undocumented behavior. I'll leave it to IBM to define the behavior as incorrect.

And yes, I have it working from a NotesUIDatabase space.

22 - Hooking to NotesUIDatabase and getting it to push out the shared code worked for me, too.

Now, Nathan, how about a hat trick. We have had (are still living) two of your flashes of insight. How about one of these "unsolvable" problems:
1. showing in memory only (not saved) documents in a view and by extension same for notes document collections
2. dynamically changing a frameset to include additional frames.

Maybe there is more such challenges that we could list and attempt to solve.

23 - Seeing as you are looking for a challenge, let me ask this. I would love to be able to call a method in another database, does this code help me at all?

Basically, what I would like to do is define e.g. a register user function in the NAB - perhaps in the database script library - and be able to do something like this:
set session blah
set db = session.getDatabase (name of your nab file)
call db.registerUser(pass some sort of structure here containing the data)

Any thoughts, or am I stuck?


Post A Comment

:-D:-o:-p:-x:-(:-):-\:angry::cool::cry::emb::grin::huh::laugh::lips::rolleyes:;-)

11 Aug 

Hire Me 

Lotus-911-Logo.jpg

Search 

Disclaimer 

Welcome to Escape Velocity!

Opinions expressed here by Nathan T. Freeman are not necessarily those of his employer. However, there's a decent chance they are, so check with them if you really want to know.

But really... do you need that kind of validation? Are the opinions expressed here in doubt?

MiscLinks