Posts Tagged With: enableModifiedFlag

Dirty pages and keeping users on them in #XPages

As I’ve mentioned before, sometimes XPages does not react the way your Notes users would expect it to react. In particular, I’ve had issues with ensuring that XPages warns the user when they try navigating away from a page that they’ve edited without first saving it. Such pages are “dirty”, since something has changed on them. Back in November we examined this in my post on Modified flags in XPages.

Unfortunately, sometimes I want to inform the user that they’re leaving a dirty page and other times I don’t. Also, sometimes, I seem to be able to add things that don’t warn the user that they’re leaving a dirty page. I’m sure there is consistency, but I haven’t figured out the rules, so I have added some code that handles these situations.

Simple warning of a dirty page

This was described in that prior post. Set the enableModifiedFlag to true and provide a couple of properties to handle it. This should fire whenever you close the XPage or navigate away via a standard link.

<xp:view xmlns:xp="http://www.ibm.com/xsp/core" enableModifiedFlag="true">
	<xp:this.modifiedControl>
		<![CDATA[#{javascript:if ( true ) {return "saveButton1"}}]]>
	</xp:this.modifiedControl>
	<xp:this.modifiedMessage>
		<![CDATA[#{javascript:"Purchase order modified. Click OK to save, cancel to continue without saving"}]]>
	</xp:this.modifiedMessage>
...
</xp:view>

Unfortunately, I’ve found that sometimes, it doesn’t warn me.

Warning when clicking links

I’ve got some navigation on the left side of my XPages, set up in a lovely dojo accordian with links to different parts of the application. However, not every link is set up in the same way. Some are simple and use the modifiedControl to warn users, like this one:

<xp:link text="Attachments" escape="true" id="link3" value="/att_attachmentManagement_view.xsp">
</xp:link>

Some, however, run some scripts to remove some sessionscope variables when switching pages and I was having challenges getting the server-side Javascript to execute. So, I found a way to do both:

<xp:link text="Payment requests" escape="true" id="paymentLink" target="_self" value="#">
	<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
		<xp:this.action><![CDATA[#{javascript:clearDynamicViewSettings();
context.redirectToPage("/pro_paymentRequest_view.xsp");}]]></xp:this.action>
	</xp:eventHandler>
</xp:link>

The unfortunate thing is that when I’m using the redirect, it ignores the dirtiness of the page. Fortunately, Per Henrik Lausten found a way to deal with it on StackOverflow (thanks to PSolano), and posted it as an XSnippet on OpenNTF.

if (XSP._isDirty()){
  if (confirm ("Are you sure you want to navigate away from this page?" + "\n" + "\n" +
    "This document may contain unsaved changes." + "\n" + "\n" +
    "Press OK to continue, or Cancel to stay on the current page.")) {
    return true;
  } else {
    return false;
  }
} else {
  return true;
}

So, when I added that explicitly as client-side Javascript to each eventhandler, it worked beautifully. Now, being a fan of reusable code, I wanted to put it in a CSJS script library and invoke the function all over the place. My function was as shown above, with a little wrapper:

function isClean() {
	if (XSP._isDirty()){
	    ....
	}
}

Then, I just needed to invoke it. My first attempt looked great to me. Call isClean() and the client side would warn the user, then stop them as appropriate.

<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
	<xp:this.action><![CDATA[#{javascript:clearDynamicViewSettings();
context.redirectToPage("/pro_paymentRequest_view.xsp");}]]></xp:this.action>
	<xp:this.script><![CDATA[isClean();]]></xp:this.script>
</xp:eventHandler>

Ummmm, but that didn’t work. In order to prevent the server-side Javascript from executing, the client-side Javascript needs to RETURN false, not just compute it.

<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
 <xp:this.action><![CDATA[#{javascript:clearDynamicViewSettings();
 context.redirectToPage("/pro_paymentRequest_view.xsp");}]]></xp:this.action>
 <xp:this.script><![CDATA[return isClean();]]></xp:this.script>
</xp:eventHandler>

That’s added bonus knowledge – not only are we learning how to stick to dirty pages, we’ve also learned that if you have the CSJS return a value of false, it will not execute the SSJS. I’m sure you can grasp the opportunities for validation provided there.

What about when you don’t want to warn them?

In our application, the user can select the action to perform from a combobox and then click a button to execute it. (Thanks to Scott Good, Henry Newberry and the folks at Teamwork Solutions!) The challenge here is that when the user changes the value in the combobox, the page is dirty! I mean, no data may have changed other than that combobox, but the XPage still feels dirty. Since some of those actions will result in saving the document and all of them result in navigation away from this page in an expected and managed way, we don’t want it to feel dirty. To dodge the problem, I just add a little CSJS to convince that page that it’s not really dirty.

<xp:this.script><![CDATA[XSP._setDirty(false,"");]]></xp:this.script>

So, then the modifiedControl is not tipped off and we execute the intended commands. Now, I’m pretty sure we don’t run into this problem with standard save buttons, but we’re in an unusual case of taking two steps to execute one action.

Note

YMMV. These private calls to XSP methods may go away and using them requires care to ensure you’ve got the right parameters, or so the XPages Portable Command Guide advises us. Of course, everything we code may call functions that will go away and we always need to get the parameters right. So, I’m gonna use them, though sparingly.

Advertisement
Categories: Client-Side Javascript, Server-Side Javascript, Xpages | Tags: , , , , , , , , , , , , | Leave a comment

Modified flags in #XPages

One of the initially most frustrating things about XPages was that, unlike in standard Notes, if the user closed a window without saving, it never warned them. If you expect to be warned and never are, you’re bound to lose some data and be frustrated.

Fortunately, you can design your application to pay attention to whether the user has “dirtied” the page by editing it, even identifying down to specific input elements of which you care or don’t care about the “dirtyness”. I can’t explain why they wouldn’t have made it the default behavior, since it is the default behavior in Notes, but they did.

In the original edition of Mastering XPages, I got out to around page 300 before specific work inhibited me from further page-by-page progress, so I never got to read Chapter 13, on XPages in the Notes Client (XPiNC). I’m very surprised, since most of my work now needs to run XPiNC.

So, the key to making your custom controls and XPages being “cleanliness-aware” is to set the enableModifiedFlag = “true“. This allows the system to keep track of whether something within that element has been dirtied by the user and, if so, complain when the user tries to navigate away. You can even create a custom message for the user and specify a control to execute if the user clicks OK.

<xp:view xmlns:xp="http://www.ibm.com/xsp/core" enableModifiedFlag="true">
	<xp:this.modifiedControl>
		<![CDATA[#{javascript:if ( true ) {return "saveButton1"}}]]>
	</xp:this.modifiedControl>
	<xp:this.modifiedMessage>
		<![CDATA[#{javascript:"Purchase order modified. Click OK to save, cancel to continue without saving"}]]>
	</xp:this.modifiedMessage>
...
</xp:view>

Now, the unfortunate thing is that the only containers that this flag works on are the big ones – the XPage or the custom control. I’d really like to be able to use it on a panel or table or some smaller element, so I can package my concern for ‘cleanliness’ in slightly more granular ways.

Fortunately, they also allow you to use the disableModifiedFlag on specific core controls. It is really important to note that the enableModifiedFlag takes precedence, so you cannot use disableModifiedFlag=”false” to get your notice if you didn’t enable it on the custom control or XPage.

The following Core Controls support this feature:

  • Edit Box
  • Multiline Edit Box
  • List Box
  • Combo Box
  • Check Box
  • Radio Button
  • File Upload
  • Date Time Picker
  • Rich Text Editor

Again, this is a property I’d like to see on other containers so that I can use fewer properties to granularize my cleanliness monitoring (and maybe save minor bits of memory and bandwidth?).

Caveat

Not everything that you would consider “navigating away from the page” gets treated by XPages. I’m not sure of everything that it ignores, but I do know that when we had navigation using the simple action openPage, the system never warned me that I was navigating away from the page – browser or XPiNC. Fortunately, I was able to discard the simple actions that were running in that link and just have it use the URL for the XPage I wanted to navigate to, allowing notification of dirtyness,

This method did not work:

<xp:link text="All inventory" escape="true" id="link30">
	<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
		<xp:this.action>
			<xp:actionGroup>
				<xp:executeScript script="#{javascript:clearDynamicViewSettings()}">
				</xp:executeScript>
				<xp:openPage target="openDocument" name="/inv_allInventory_view.xsp">
				</xp:openPage>
			</xp:actionGroup>
		</xp:this.action>
	</xp:eventHandler>
</xp:link>

After I figured out I didn’t actually need to run that bit of javascript, it made it easy to switch to this link, which did prompt when the page was dirty:

<xp:link text="All inventory" escape="true" id="inventoryLink" target="_self"
value="/inv_allInventory_view.xsp">
</xp:link>

While I’m sure there are ways around the openPage failure and some way of doing all via javascript, I’m happy with this first step to making XPiNC react the way my Notes users expect it to react.

Categories: Xpages | Tags: , , | 1 Comment

Blog at WordPress.com.

%d bloggers like this: