PermaLinkModal XPages SSJS dialogs the easy way08:55:42 AM
Written By : Scott Good

XPages brings a lot of really interesting technology and capabilities to the table for web (and for that matter, Notes) developers. In the middle of all that goodness, however, are some really basic things that are shockingly hard to do. For instance, modal dialogs from Server-Side JavaScript (SSJS).

From the research I've An XPages dialog done the easy way. done, there is probably, possibly, pretty sure, MAYBE a way to do modal dialogs with Dojo. Well, sure, of course there's a way to do modal dialogs with Dojo. They're really easy...unless you actually want to do something like, for instance, gather any user input. As soon as you need to collect the first data point (other than, I suppose, the click of an OK button), things get harder.

Hard enough that I gave up on them right after I discovered detailed instructions which included having to load extra code onto the server. Maybe there's an easier way, but any idea that begins with requiring a customer to talk an admin into loading new code on their precious Domino servers is pretty much a non-starter around here..

Now, in fairness, I'm not a Dojo expert, so maybe there's a way to do it without server add-ins. If so, I couldn't find it. So, I rolled my own.

I don't know about you but the applications we build are CONSTANTLY needing dialog boxes users can fill out to update information with. REAL dialogs, with fields and options and choices, not just text-only dialogs for annoying our users. And, with XPages, we rarely write any client-side JavaScript anymore so I needed a solution that would work in Server-Side code.

So, I says to myself, "Self?," I says, "There has to be an easy way to do this with CSS." And, joy of joys, there is.

To have a modern-looking modal dialog, fashion (and, for that matter, function) pretty much says you need an overall transparent background on top of which the dialog appears. Also, that you can't click on or update things beneath the background until the dialog has been dismissed. You can see a finished example in the image above.

Technically speaking what's needed to make this work is shockingly little. You need a <div> of the right size with a dark background and another <div> which contains whatever you want inside the dialog which has been positioned appropriately. For all intents and purposes, that's it.

To do this I defined two classes with easy to remember (and understand) names: dialogForeground and dialogBackground. You can probably guess which goes around what. Here are the CSS specs for both these divs:

div.dialogForeground {
  position: absolute;
  top: 70px;
  left: 50%;
  margin-left: -400px;
  z-index: 1000;
  background: white;
  padding: 15px;
  border: 4pt solid #ddd;
}
div.dialogBackground {
  position: absolute;
  top: 0;
  left: 0;
  width: 110%;
  height: 200%;
  background: black;
  opacity:0.4;
  filter:alpha(opacity=40);
  z-index: 999;
}

On your XPage, in addition to including the Style Sheet with the above code as a Resource, you'll need to create two divs:

<div class='dialogBackground'></div>
<div class='dialogForeground'>
  <!-- the stuff inside your dialog goes here -->
</div>

It's important that the background <div> is physically before the foreground <div> in the flow of your page. Other than that, it's easy to get right. What you end up with is elegantly simple, relatively attractive, XPages modal dialogs can be as sophisticated as you need them to be. and easy to use. And, like the example here, can be more or less anything.

The trick with all this is to get the dialog to appear and disappear when you need it to. For that, use partial page refresh. Both of the above divs should be enclosed in a panel or other container you can name and choose for a partial page refresh.

Inside that outer panel, add a second panel which has a "Visible" formula which makes the divs show up when you need them. That decision might be determined from a scope variable, from the edit mode of the document, or from whatever makes sense in your application. Remember, though, that this is all server-side JavaScript so plan accordingly.

Is it perfect? No, not yet. I could really use some client-side code to sense the size of the window and re-size the background appropriately. Also, it would be nice to position the dialog in the upper center of the current window position. It doesn't do that (yet), but easily could. Still, it's way better than nothing.

Now, on to harder things like a NAB picker...

Comments :v

1. Jeremy Hodge08/12/2010 11:05:27 AM
Homepage: http://www.hodgebloge.com


I assume you are referring to the dijit dialog solution I posted to the XPagesBlog ...

There is no requirement to load anything on your server file system ... you can just as easily add the com.ZetaOne.widget.Dialog.js file as a client side javascript library and include it on your XPage as a resource.

Then, all you need to do is set the dojoType, and have your button call the show() method ... Dijit.Dialog is modal by default btw....

Its actually much easier, and you can set the size of the dialog using the width/height style of the xp:div/panel ... and no partial refresh is needed, and you won't run into issues with values getting not saved/not validated if the contents of the dialog are part of the underlying form, but aren't rendered when the save occurs.

Jeremy




2. Scott Good08/12/2010 11:15:52 AM
Homepage: http://www.teamworksolutions.com


Hi Jeremy,

It may very well have been your article I read. It's been quite a while now, so I'm not sure. I'll have to try this again the way you've described. Somehow I never really managed to get it to work before.

Thanks for the tip!

Scott




3. Tim Tripcony08/12/2010 12:02:32 PM
Homepage: http://www.timtripcony.com


Scott, the primary problem with the standard dijit.Dialog implementation is that on load, Dojo moves the div surrounding your dialog contents outside of the form element... hence, all events are broken because fields are no longer posted back to the server.

However, this is surprisingly easy to resolve: add a scriptBlock tag with the following value:

dojo.addOnLoad(function(){
dojo.query(".dijitDialog").forEach(function(eachDialog){
document.forms[0].appendChild(eachDialog);
});
});

This simply moves any dialogs back inside the form so that their contents will be included in any post events.

Jeremy's approach is an elegant way to address other issues with dialogs, but simply by adding the above code, at least you can just use the standard dijit implementation without it breaking server-side events.




4. Scott Good08/12/2010 01:30:53 PM
Homepage: http://www.teamworksolutions.com


Thanks Tim, I'll give it a try.

Scott




5. Paul Calhoun08/12/2010 02:33:56 PM
Homepage: http://www.nnsu.com


Dojo can be implemented programmatically or declaratively. If the panel you add to the XPage's dojo type is set to dijit.Dialog, then put the fields inside the panel then they are automatically part of the form and you don't have to fuss with any additional code.

So the panel control source looks like this
<xp:panel styleClass="DemoLeft" dojoType="dijit.Dialog"
id="exampleDialog">
<xp:this.dojoAttributes>
<xp:dojoAttribute name="title" value="Example Dialog"></xp:dojoAttribute>
<xp:dojoAttribute name="style" value="width:400px">
</xp:dojoAttribute>
<xp:dojoAttribute name="execute"
value="updateCName();">

</xp:dojoAttribute>
</xp:this.dojoAttributes>

<xp:table>
<xp:tr>
<xp:td>
<xp:label value="First Name:" id="label1"></xp:label>
</xp:td>
<xp:td>
<xp:inputText id="fName"></xp:inputText>
</xp:td>
</xp:tr>
<xp:tr>
<xp:td>
<xp:label value="Middle Initial:" id="label2"></xp:label>
</xp:td>
<xp:td>
<xp:inputText id="mInitial"></xp:inputText>
</xp:td>
</xp:tr>
<xp:tr>
<xp:td>
<xp:label value="Last Name:" id="label3"></xp:label>
</xp:td>
<xp:td>
<xp:inputText id="lName"></xp:inputText>
</xp:td>
</xp:tr>
</xp:table>
<button dojoType="dijit.form.Button" type="submit">
OK
</button>
<button dojoType="dijit.form.Button" type="button"
onClick="dijit.byId('#{id:exampleDialog}').hide();">
Cancel
</button>
</xp:panel>

and the updateCName client side script looks like this.

<xp:this.value><![CDATA
function updateCName(){
//The first three lines read the Edit Box controls that were added to the Panel control
//that becomes the Dialog Widget and concatenates them into a string.
var cName = XSP.getElementById("#{id:fName}").value;
cName += " " +XSP.getElementById("#{id:mInitial}").value;
cName += " " +XSP.getElementById("#{id:lName}").value;
//This code writes the value of the string variable to the Edit Box control that is bound to the data source.
XSP.getElementById("#{id:contactName1}").value = cName;
}]]></xp:this.value>
</xp:scriptBlock>

Then just have a button or some other event that opens the dialog.
<xp:button value="Set Name" id="button2" styleClass="DemoLeft">
<xp:eventHandler event="onclick" submit="false">
<xp:this.script><![CDATA[//Get an instance to the Panel control by it's ID name
exDlg = dijit.byId("#{id:exampleDialog}");
//Display the Dialog
exDlg.show();
]></xp:this.script>
</xp:eventHandler>
</xp:button>

My approach anyway.





6. Scott Good08/12/2010 03:58:40 PM
Homepage: http://www.teamsol.com


Thanks Paul (and others) for your different cuts on this...but here's the question. Well, OK, the NEXT question:

I can get this to work just fine with new documents. For instance, I used Tim's method around a table with a set of fields connected to a particular form and I can pop it up as a dialog, enter some values, submit it, and everything works great.

But (there's always a "but"), we're trying to do things that are more complicated than that. For instance, I have a repeat object which contains many of these addresses. I want to be able to put any one of those addresses into Edit mode and update information in it using the dialog.

I suppose I could work up a way to click on an "edit me" link of some kind and have that link then divert to an entirely new XPage to do the editing in, and then return back to the XPage I was originally on but that seems like a hack. I have huge resistance to the idea of having to load yet another page for every little thing that has to happen in the UI. What I want to do is just click on the link and have the individual address pop up in a dialog like it did when I created it originally.

The way we're doing this with my CSS-based dialog is pretty easy. We use a computed field to show the display version of the data and below it, but still inside the repeat object, are the actual fields which are inside the dialogForeground area and hidden. When I want to edit I change the edit mode of the document in the repeat and unhide the dialog area. When the dialog closes, I also refresh the repeat object so it picks up any changes. Works well and it's pretty fast.

The way I'm trying to do this with Dojo does not seem to work because, I think, I'm calling the dialog with the Client-side script before calling the Mode Change in server side. The dialog appears for a moment (not in edit mode), then disappears.

Am I just missing something obvious here or is this not possible for some reason?

Thanks very much for your help,

Scott




7. Tim Tripcony08/12/2010 04:17:24 PM
Homepage: http://www.timtripcony.com


@6 - Scott, we've done what you're describing fairly often. It's a bit more involved a process than just slapping a table into a div, but here's the gist of it:

1. Put a panel inside the dialog box
2. Cut a hole in the... oops, wrong list.
3. Define a data source that is specific to that panel and set its ignoreRequestParams to true (NOTE: making the data source panel-specific is optional, but highly recommended)
4. Calculate the documentId of the panel-specific data source dynamically, pointing it to a viewScope variable (i.e. "selectedDialogDocId")
5. Set the action of the "Edit Me" link/button for each member of the repeat control to set that viewScope variable's value to be the NoteID/UNID of the corresponding document
6. Set the refresh Id of that event to target the panel inside the dialog
7. Set the onComplete of the event to call .show() for the dialog
8. Set the refresh Id of the OK/Save button to target the repeat control
9. Set the onComplete of that event to call .hide() for the dialog

So... again, a bit more involved, but here's what happens:
- When the user clicks to edit any member of the repeat, it actually changes the data source to be bound to that specific document
- Because you're delaying the .show() of the dialog until the onComplete of an event that's targeting the dialog contents, by the time the dialog displays, all the controls inside it are now bound to fields on that document and will show the correct existing values
- Since the button that writes back the data is targeting the repeat, and you're delaying the hide() until the onComplete, by the time the dialog closes, the repeat will already be reflecting any changes that were made (including not displaying the associated document if any of the changes caused it to no longer qualify for the repeat collection). Another handy side-effect of this is that you can even include server-side validations on the dialog fields, because if any fail, the onComplete never fires, so the dialog will still be visible but will now show the appropriate validation messages to the user.

Hope that helps...




8. Scott Good08/16/2010 10:51:11 AM
Homepage: http://www.teamsol.com


@7 Tim...that is excellent. Thanks for the detailed instructions. Not only are they easy to follow, they sound like they'll actually work, too! I'll give this a try this afternoon. MUCH appreciated. I owe you a beer or two.




9. Henry Newberry08/19/2010 05:29:18 PM
Homepage: http://www.henrynewberry.com


All right now, using the suggestions of all of you (@Jeremy, @Paul, and @Tim), I got it to work for new and old documents.

I built the dialog panel as part of (inside) the repeat control which uses an array of objects stored in the viewScope which includes the UNID of each of the documents being shown. This panel is setup with dojoType="com.ZetaOne.widget.Dialog" per @Jeremy suggestion. I also built the CSJS file as suggeted which performs the move back into the form element flawlessly!

<xp:panel styleClass="dialogForeground" id="EditDialogPanel" dojoType="com.ZetaOne.widget.Dialog">
<xp:this.dojoAttributes>
<xp:dojoAttribute name="title" value="Editing Dialog"></xp:dojoAttribute>
<xp:dojoAttribute name="style" value="width:400px"></xp:dojoAttribute>
</xp:this.dojoAttributes>

I then used @Tim's suggestion of embedding another panel inside the dialog and defining a data source specific to that panel based on the UNID element of the Repeat control's object.

<xp:this.data>
<xp:dominoDocument var="dlgDoc" formName="subDoc" ignoreRequestParams="true" action="editDocument">
<xp:this.documentId><![CDATA[#{javascript:(rptobject.docUNID == 'x') ? "" : rptobject.docUNID}]]></xp:this.documentId>
</xp:dominoDocument>
</xp:this.data>

Then I added a standard table of fields as @Paul suggested but instead of leaving them unbound, I bound each to the dlgDoc data source.

Finally I added XP Buttons to do the save and cancel. The save button does SSJS to dlgDoc.save(); and remove the top object used by the repeat control from the viewScope. It then does a full page refresh which recomputes the repeat control and reloads it from the newly created or saved documents. The cancel button does the dijit.byId('#{id:editDialogPanel}').hide(); in CSJS with no page refresh.

Only issue left (yeah right) is that the dialog is positioning with its middle point left to right on the left margin of the window. But that's a UI issue that I can get Scott to figure out when he gets back next week.

Since I got it working in one place, time now to make it work in all 10-15 places we are doing similar things... Thanks for all of your help...

Newbs




10. Daniel Friedrich09/01/2010 11:02:09 AM


Hello Henry,

I wanted to do something similar with my repeat controls, but for some reason, I can't get it to work properly. When I add a new document to the list of documents of the repeat control and save it, the fields are empty.

You don't have, by any chance a sample database were it works that you could send me?

It would be highly appreciated.

Thank you

Daniel




11. Henry Newberry09/02/2010 01:37:23 PM
Homepage: http://www.henrynewberry.com


@10 Daniel - We do not hvae a canned demo database of these techniques. Heck we are only now developing them the first time. And the number of mega sized speed bumps have been incredible.

But, send me an email (newbs@teamsol.com) and I may be able to throw one together as I need a sample to prove a bug I have identified.

Newbs




Enter Comments^


Email addresses provided are not made available on this site.





You can use UUB Code in your posts.

[b]bold[/b]  [i]italic[/i]  [u]underline[/u]  [s]strikethrough[/s]

URL's will be automatically converted to Links


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





Remember me    

Disclaimer & Copyright
Monthly Archive
Contact me...
Racing sponsors and such...

Thank you sponsors!

GABlogLogo.jpg

GOODAero

GOODAero specializes in building aerodynamic products for racing cars. Our first product, the GOODAero Raptor wing is available at a surprisingly reasonable price (under a grand) for a full carbon, full-sized, racing wing. Check it out.


Infinite Fiberworks Co, a great source for high-quality Porsche fiberglass parts

Infinite Fiberworks Co.

If you are looking for fiberglass for Porsches, IFC is the place to go. I have used parts from most of the major suppliers and IFC's are easily the highest quality and the most reasonably-priced. Contact Mike at Infinite Fiberworks for more information.

Located in Racine, OH (so far Southeast they're almost in West Virginia), IFC's goal is to be The Best. Give 'em a try.


Bent or ugly wheels? Call Wheel Medic!

Wheel Medic & The Round House

Whether you need to repair, repaint, refinish or just replace your wheels, the guys at Wheel Medic/Round House can get you back on the road in no time!

Wheel Medic, Inc is a family-owned company which specializes in the repair and restoration of aluminum wheels.

The Round House was founded to service Wheel Medic's clients looking for more than just repair work...from custom wheel colors to high-end wheel applications and body kits, the Round House is there to serve the discriminating automotive enthusiast.


Used Porsche parts, great prices!

A Part Above

Looking for used parts for 944s, 924s, 968s or other late-model water-cooled Porsches? Contact John at A Part Above.

Located in Strongsville, OH (20 miles south of Cleveland) their goal is to provide top quality parts and services. I can tell you, John is great to work with and the prices? Very hard to beat.


SMRT Motorsports wants you!

SMRT (that's short for Skid Mark Racing Team), a very-

loosely organized band of fun-loving friends who enjoy auto racing (heck, cars in general), and the occasional adult beverage, wants you to be a part of our team.

Go here to find cool T-shirts, sweatshirts, caps and mugs with the SMRT team logo.

The BlogRoll
Lotus Domino ND6 RSS News Feed RSS Comments Feed Geo URL RSS Validator Blog Admin Lotus Geek Open Notes Picture Database OpenNTF BlogSphere
Calendar
February 2012
Su
Mo
Tu
We
Th
Fr
Sa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
By Category
What I do for a living


I am the President of Teamwork Solutions a long-time Lotus, now IBM, Premier Partner.

With offices in Columbus and Cincinnati, Ohio, we specialize in custom application development for IBM Lotus Notes, Domino, and related technologies. Our software product, ProcessIt! (see below), is quite possibly the world's best, most powerful and easiest-to-use workflow tool for Notes and the web.

Our clients are some of the world's largest corporations along with others that aren't so big.

We do excellent work, quickly, and often on a fixed-fee basis. We'd love to talk to you about your next project.




I am a Contributing Author to Lotus Advisor Magazine, with more than 40 articles under my belt.

I've written how-to series (serieses?) on LotusScript, JavaScript, Cascading Style Sheets (CSS), and now, AJAX (Asynchronous JavaScript and XML), as well as a bit on miscellaneous web development topics.


TheView.jpg

I also write for The View as of the July/August issue where I showed how to take an ugly Notes applications and make it beautiful with just a few minutes' (careful) work.



I am the chief architect and one of two primary developers for what many consider the best all-around workflow tool for Notes/Domino, anywhere, regardless of price.

It's called ProcessIt!, and you can read all about it at www.notesworkflow.com but the bottom line is this: ProcessIt! is fast and easy to learn, extremely powerful, and can be used by mortals. Even--dare I say it?--common users.

You can spend a lot more on a workflow tool but you won't be able to do a lot more for all the extra money.

Don't believe me? Download and try it for free for 60 days.



GTSLogoSm.gif
Copyright Porsche and NASA...not me!

I race a Porsche 944 S2 in National Auto Sport Assocation events and am the 2008 National Champion in NASA's GTS2 class.

Blame this event, a few years ago, for starting that particular money drain all over again.

In support of my habit, I am the NASA Great Lakes Region's GTS (German Touring Series) Director.

I'm also a Nationally-Certified Instructor for the Porsche Club of America and am in charge of classroom sessions for the Mid-Ohio region when we are doing high performance driving events.

In a prior racing life, I was the Midwestern Regional Formula Atlantic Champion and, in 1991, the Ohio Vally Region of SCCA's Regional Driver of the Year (but that, alas, went away when my credit cards let go of the rope!).




I'm writing a book...or at least trying to.

It's murder mystery in which, not too surprisingly, the main character runs a small software company and races cars for fun. Oh yeah, and lives near where I do.

Just where do they come up with these crazy ideas?

Facebook