Sunday, September 30, 2007

PHP-like Templating in Domino

Templating may sound confusing, since "we" have Design Templates, but I believe that is what Ruby On Rails/PHP-guys call this.

Vision:
Imagine you get a XML-spec your customer want data exported in. With my method, you may just be able to copy/paste the spec into a template-document. Add field references, and you are good to go.

The same with HTML-pages. Make a static mock-up in Aptana/DreamWeaver. Paste it into your template (maybe split it into modules), add field-references, and you have a nice-loking, valid (if that's what you're aiming for) dynamic (X)HTML-page.

To make/edit the documents, you can use the notes-generated form.

The demo
I've been wanting to do this for a while, but other experiments have overshadowed this. Thanks to this post by Michel Van Der Meiren, and a couple of beers this weekend, I finally got around to making a prototype.

Since I'm lazy, I don't take the time to make data. I use the FakeNames database, kindly provided by Jake.

I think making the templates took me about an hour, including finding out the names of the fields I was going to use. Rapid deployment..

Document in it's original form:




Simple table with header-module:


>> HTML Template source

XML:


>> XML Template source

Text:

>> Text Template source

Implementation:

Getting the correct content-type
To show the documents in a template, I have a view that opens the Person-documents in a form that has the correct content-type (Form Formula). In the demo, I look for Text or XML in the @UrlQueryString( "template" ). If XML, XML-form, text, Text form, else HTML-form. On WebQueryOpen, I run the agent that applies the template to the document. It prints its output to a NotesRichTextItem.

The template-query string decides which template to show the document in.

Processing the template
I first split UnformattedText of the body-field in the template on #%, which are used to process modules, if there are any. Example of module-reference, #%header#%.

I test each item in the resulting array, item Like "*[A-Z,a-z]". This simple testing-method should work for HTML/XML, because they containt a lot of has brackets, which are not Like A-Z. With text-templates, there may occur errors using this. For more complex content, you could make a LS2J-class that enables you to do Regular Expression tests of the content.

The reason I check for modules first, is that they can contain formula-snippets and field-references, which also should be processed.

In it's current state, it doesn't support modules within modules.

Then I look for field references, which are contained within #$, e.g. #$FirstName#$.

Finally, I look for formula-snippets, e.g. #@WebDbName#@, in the home-link of the simple table template, and pointing to a stylesheet in the application.

>> Demo application

If you're downloading the demoapp


All design-elements made for the demo have DontPanic as a comment. I cleaned out a lot of the original design-elements in the app, to make it easier to find what I made for the demo.

The agent that "renders" documents is called "RenderPage". It's not well commented, but I hope most of the code speaks for itself.

Open the db on the Web, click on one of the people to open the document in its original form.

At the top of the page, there is a button for each template I made for the demo.


All the templates are in the templates-view, modules (only one in the demo) in the modules-view.

Why should I care/You can already do this with a form+content-type HTML


If you add more advanced back-end processing, you could have inline LS/Function calls in the context of the document. e.g.
#LS
Use "MenuBuilder"
makeMenu( #@UserRoles#@ )
#LS
^^This using Execute.

I don't think you need to pass the document-object to the inline functions, since NotesSession.DocumentContext is a global object. From a little test, it seems like it's best to Update the RT-field before running each inline LS-modification to it.

Inline formula-example

#F
@For( i:=0 ; i<10 ; i := i++;
    num := @Text(i);
    html := html + "<tr><td>" + @GetField( "line" + num) + "</td></tr>"
)
"<table>" + html + "</table>"
#F

You can of course do this with a a regular form using a WQO agent + computed elements, but what I hope my proposal of "Notes Templating" can do, is to add a layer of abstraction, maybe to make it easier for notes newbies to make Domino Webapps.

I also think this way is more readable than a lot of computed text/Computed For Display fields. The downside is that it adds processing overhead.

Comments/suggestions are greatly appreciated!

Friday, September 28, 2007

Entry not found in index

If you're trying to open a document from a view, using a stored @Text(@DocumentUniqueID), that don't match the documents UNID, you may get "Entry not found in index" when trying to open the document from the web.

It seems the Domino Server "mistakes" a 32byte hexadecimal string to be the UNID of a document, and ignores the values in the first sorted column of the view.

The quick and dirty workaround is to add a character or two before the stored ID (e.g. "u_" + UNID), in the sorted column of the view, and in the links that point to documents in the view.

The best-practice solutions is for fix the application, and remove a stored UNID as a lookup-value. The simplest alternative is @Unique.

Tuesday, September 25, 2007

Formula on WebQueryOpen

I'm used to putting lookups inside fields, but it's also possible to have this kind of code directly inside WebQueryOpen or in formula-agents that are run on WQO.




Form on web:


You can also run ViewRefreshFields back-end, before the form is rendered.
You may want to do this if you have computed fields that get their values from fields that are set by code in WQO, or by a WQO-agent.



The subject2-field in the first image is computed with value:
@If( @Elements(subject) = 0 ;
    "" ;
    "First name in view: " +
    @Word( subject[1] ; "|" ; 1 ) + " " +
    @Word( subject[1] ; "|" ; 2 )

)



>> Simple Demo-application

Thursday, September 20, 2007

Comments inside formula loops

It isn't possible to put REMs inside @-loops, and therefore I haven't commented these before.

I just discovered that you can put strings that don't belong to a variable, anywhere within the loop.

I've now started commenting like this (ignore the bad example):

@For( i:= 1 ; i <= 5 ; i := i+1 ;
"## If counter is above one, add comma before next number ##";
str := str + @If( i > 1 ; ", " ; "" ) + @Text(i)
);
str
==
Result: 1, 2, 3, 4, 5

The ##'s to make it easier to distinguish comments from code.

This is a basic knowledge kind of tip, so I tag it with Tims proposed tag.

Tuesday, September 18, 2007

NotesFX 0.2 - Stable, but still not perfect

Since intensive string concatenation with JS crashes the JS-engine in Notes, I moved the concatenations to the formula-engine in Notes, which is more or less rock solid.






>> Download Demo Application

To do this, I use a "proxy" (between JS and Formula) field per effect, where I set x and y values with JavaScript. I then use document.forms.nameOfButton.click() to click a hidden formula-button which makes the string needed to inflate the layer/sets fieldvalue.

The problem now, is that the mouse pointer "blinks" (between hourglass and regular pointer) when the button(s) are clicked with JS. *d'oh!*

In the flash, you can't see the mouse pointer blinking as much as it does in the app.

I'll do my best to find a perfect solution, but I'm not sure the right idea will pop up. Suggestions are appreciated :)

If you're downloading/running the demoapp:
The new demo-form is called DemoAnimation_formula.

Saturday, September 15, 2007

NotesFX 0.11

If you're coming to this post directly, NotesFX 0.2, which is stable here.

Changes since 0.1 A:
One field per effect, less JS, speedier, possibility to set speed of animation.

Dropped the alpha (still in the DB-title). It's still nothing more than a proof of concept (mostly because of instability in the Notes Client).

Will still kill JS in Notes (unable to save JS-libraries/run JS in the client). Solution, KillNotes.exe.








>> Download NotesFX 0.11 Alpha

Thanks goes out to Nathan for input on the first version. I planned on having the weekend blog-free, but when people mail me about my experiments, I sometimes get a boost in motivation.

If I ever get this stable, I came up with a couple other possible uses than I have in my demo. Layer-based progress bar, horizontal and vertical bar charts (dynamic, based on field values and animated if you wanted).