Archive | June 2014

Providing URL Parameters for Multiple Document Data Sources

You’re no doubt used to seeing URLs to open a document that look like this: http://www.server.com/db.nsf/myPage.xsp?action=openDocument&documentId=0123456789ABCDEF0123456789ABCDEF in XPages. The documentId and action URL parameters are used by each document data source by default. In this post, I’ll show how you can use the requestParamPrefix property to define a separate set of URL parameters for an additional data source.

By default, a document data source will use the documentId and action parameters to determine the document to open and the edit mode. However, if you have more than one document data source on a page, this may not be the behavior that you want.

For example, I have a database with a simple XPage that has two document data sources for the Person form.

<xp:dominoDocument var="document1" formName="Person"
  action="openDocument">
</xp:dominoDocument>
		
<xp:dominoDocument var="document2" formName="Person"
  action="openDocument">
</xp:dominoDocument>

If I open the page with a standard URL, both display the same document.

http://www.server.com/db.nsf/TwoDataSources.xsp?documentId=9E9324F079A7F31885257D06007ADF9C&action=openDocument

requestParamPrefix_1_SameDocument

If I change the action parameter to editDocument, then both change to edit mode. Sounds like a great way to generate a save conflict!

ignoreRequestParams

Of course, this isn’t really what I want. One option to work around this is to set the ignoreRequestParams property of the document data source to true and compute the documentId property (and, optionally, the action property).

<xp:dominoDocument var="document2" formName="Person"
  action="openDocument"
  documentId="#{javascript:return '0123456789ABCDEF0123456789ABCDEF';}"
  ignoreRequestParams="true">
</xp:dominoDocument>

This is the method that I am in the habit of using and it works well when putting the ID in a scope variable and using that for the computed property value.

The ignoreReuqestParams property tells the data source to ignore the documentId and action properties in the URL and leaves it up to you to manage them.

(Note: Even if you compute the documentId, it will not matter if you don’t set ignoreRequestParams to true; the URL parameters will still override the property value.)

requestParamPrefix

However, there is another way to handle this situation without computing the data source properties — the requestParamPrefix property.

You can set this on a data source and it will look for a separate copy of the documentId and action parameters and use them for that specific data source.

For example, if I set the requestParamPrefix to doc2_, then it will look for URL parameters named doc2_documentId and doc2_action and use those for this data source.

You can add the parameter directly in the page source or find it in the properties panel under All Properties > data > data > [data source]. (This assumes the data source is at the page level. If it’s on a panel, then that would be your starting point to find the data source properties.)

requestParamPrefix_2_requestParamPrefix

Now, I can open two different documents on the same page with a URL like this:

http://127.0.0.2/BlogTesting.nsf/TwoDataSources.xsp?documentId=9E9324F079A7F31885257D06007ADF9C&action=openDocument&doc2_documentId=01F564DFFEE4307A85257D06007AED1D&doc2_action=editDocument

requestParamPrefix_3_SeparateDocuments

With this approach, you do not need to set ignoreRequestParams to true. In fact, if you do, then the URL parameters will be ignored — even if they contain the specified prefix.

Advertisements

Order of execution for client-side JavaScript event handlers and callbacks

There are a number of different places from which you can execute client-side JavaScript upon clicking an xp:button. In this post, I’ll document both the order of execution and which ones execute for each refreshMode option.

Client-Side Javascript Events and Callbacks

There are 5 ways that I can see to execute client-side JavaScript when a button is clicked.

The easiest way is the “Client” event tab. Use the script editor to add code.

There are also 3 callbacks on the event handler (onStart, onComplete, onError) where you can add client-side JavaScript. These are a little harder to get to — you have to switch to the Source view, click on the xp:eventHandler tag, and then find them in All Properties under the events category.

EventCallbacks

You can also run client-side JavaScript from an SSJS code using the view.postScript() method. (Here’s an example snippet from Russ Maher) This is a bit more complex because you need to get code into a string (watch the quotes closely!), but it works well.

Here’s an example:

view.postScript('alert("server event - view.postScript");');

An Example

Here’s an example of a button that makes use of all 5 options. xp:this.script is the client-side event. xp:this.action is the SSJS event code with the view.postScript call. onComplete, onError, and onStart are part of the xp:eventHandler itself.

<xp:button value="Refresh Fields 2-4" id="button2">
  <xp:eventHandler event="onclick" submit="true"
    onComplete="alert('server event callback - oncomplete');"
    onError="alert('server event callback - onerror');"
    onStart="alert('server event callback - onstart');"
    refreshMode="partial" refreshId="field1">

    <xp:this.script>
      <![CDATA[alert('client event');]]>
    </xp:this.script>

    <xp:this.action>
      <![CDATA[#{javascript:view.postScript('alert("server event - view.postScript");');}]]>
    </xp:this.action>
  </xp:eventHandler>
</xp:button>

Order of Execution

The callbacks that run very based on the refreshMode property of the event.

If the page is fully refreshed, then none of the event handler callbacks are triggered. If the event is set to No Update or No Submission, then view.postScript will not run.

Here is the order of execution for each event and callback (with the exception of onError, because it would break the flow) that runs, based on the refreshMode:

  • Full Refresh — client event, view.postScript
  • Partial — client event, onstart, view.postscript, oncomplete
  • No Update — client event, onstart, oncomplete
  • No Submission — client event

The only way they all run is with a partial refresh.

Generated Code in the Browser

Here’s what gets generated and passed to the browser for our sample button (when set to partial refresh). The client-event code is defined in its own function. In the XSP.attachPartial() call, the client side event handler function and the callback functions are all passed (along with the button ID, refresh target ID, etc)

<script type="text/javascript">

function view__id1__id13_clientSide_onclick(thisEvent) {
alert('client event');
}

XSP.addOnLoad(function() {
XSP.attachPartial("view:_id1:_id13", "view:_id1:button2", null, "onclick", view__id1__id13_clientSide_onclick, 2, "view:_id1:field11", "alert(\'server event callback - 

onstart\');", "alert(\'server event callback - oncomplete\')", "alert(\'server event callback - onerror\')");
});

</script>

The Client-Side Javascript Event vs the onStart Callback

Of particular interest to me is the distinction between the onStart callback of a server side event and the client-side JavaScript event.

I like the concept of the onStart callback, because your code can be consistent if you’re also running code onComplete, however, the client-side JS event is easier to get to and it also has the ability to return false and cancel execution of the server-side event. (The onStart callback throws an error if you try to return false, because it’s not a function.)

If you have any other insight into the differences or a benefit to using the onStart callback of the event handler, I’d love to hear it.

XPages Tip: Accessing a custom control’s custom property from outside of the custom control

If you’ve used custom properties, then you’re aware that the compositeData object provides access to those properties within the custom control. However, you can also access those properties from outside of the custom control. In this post, I’ll show how.

Accessing a Custom Property from Within a Custom Control

Let’s start with a custom control named myCC that has a custom property named myProperty. Here’s what it looks like to include it on an XPage and pass in a value of myValue:

<xc:myCC myProperty="myValue"></xc:myCC>

On the custom control, you can refer to that value as compositeData.myProperty. Here’s an example of a computed field that displays the property value.

<xp:text escape="true" id="computedField1" 
  value="#{javascript:return compositeData.myProperty;}">
</xp:text>

Accessing a Custom Property from Outside of a Custom Control

If you have a need to access the a value passed to a custom property of a custom control from outside if the custom control, there are two steps to take:

  1. Add an id to the custom control instance on the page (so you can refer to it)
  2. Get the custom control component, retrieve a map of its property values, and then access the property that you need

Here’s an updated example of the custom control implementation on the XPage, with an id attribute added.

<xc:myCC myProperty="myValue" id="ccID"></xc:myCC>

Here’s how to refer to the custom property from the XPage:

getComponent('ccID').getPropertyMap().getProperty('myProperty');

This concept will work fine with multiple instances of the same custom control within a page because each instance would be required to have its own unique ID.

If you want to make the property available in client-side Javascript, you can use the "#{javascript: }" syntax:

var propertyValue = "#{javascript:getComponent('ccID').getPropertyMap().getProperty('myProperty')";

A Use Case

Here’s an example where I’ve recently found this useful. I have an application where I have a flexible grid custom control that takes a parameter with the URL for a REST service to supply the data. When I execute a search, I don’t want to reload the entire page — I just want to call the REST service to get the updated results and apply them to the grid.

But the search field exists as part of the application layout, so it’s outside of the grid custom control. Since the grid control is used for several types of grids, the REST service will vary, so I need to read a custom property of the custom control from another place on the page. This allows the Search feature to be reusable like the custom control.

Article Published in The VIEW: Extending Localization in XPages

The second part of a 2-part series on localization that Kathy Brown and I co-wrote has been published in The VIEW journal (subscription required). Part 1 showed how to get started with XPages Localization and part 2 shows how to extend XPages localization to handle things that the built-in localization functionality does not handle.

Abstract

The enterprise is global; business doesn’t only happen in your native language. It is not practical to expect everyone to speak the same language to use your applications.

The first article in this series showed how to enable localization and use the built-in features of XPages to set up translations for all hard-coded values. Part 1 demonstrated a few features that browsers handle for you automatically and explained how to enable and use the built-in features of XPages.

When you started developing in XPages, you likely saw the blue diamond next to virtually every property to compute it. Unfortunately, built-in localization features do not handle those computed values. This article shows how to enhance localization and provides additional tips and tricks for localization support.

Dojo in XPages – Demo Site

Due to David Leedy’s generosity, I now have a simple demo application set up at demo.xcellerant.net. Currently, there are samples of several Dojo widgets about which I’ve recently blogged and I will add to it over time.

Each page has a sample that corresponds with one blog post, along with a button to link to the blog post in order to learn more about it and get the code.

DemoSite

Dojo in XPages Series

Dojo in XPages — All Blog Posts

Book Review – Mastering XPages, Second Edition

I recently completed reading Mastering XPages, Second Edition. I read the previous edition cover to cover and did the same for this edition — it reinforced my understanding of a many topics, corrected my understanding of a few topics and opened my eyes to some features I had not yet tried. In short, this extremely thorough reference covers everything from <a> links to z-index; it is a worthy successor to the original book and a valuable addition to any Notes developer’s library.

I love giant tech books, so this 1000+ page tome makes me hearken back to the days of plowing through several large volumes (in addition to the yellow books!) to learn everything I could about earlier versions of Notes and Domino. In recent years, lots of great information has been disseminated in smaller chunks via blogs and articles, but XPages as a platform is so broad that there is absolutely a place for such an in-depth authoritative reference.

Mastering XPages starts at the beginning, dipping its toes in the water with the history and foundation of XPages. It progressively builds from explaining the basics the Notes schema and JSF through to simple actions and programming, then wading into deeper waters of partial refreshes and mixing client- and server-side code, before diving into the deep end of advance performance tuning.

Even though XPages has only been around for 5 years, it was interesting to review the history and gain a renewed sense of appreciation of how far the platform has come in just a few years. (Remember the days of creating, saving, then cleaning up temporary documents just to exchange information with LotusScript agents?)

Why Read the Second Edition if You’ve already Read the First Edition?

Even if you’ve read the first edition, it is worthwhile to read the second edition, for several reasons:

  1. It is updated for Notes 8.5.3 and 9.x, with numerous tips and descriptions of new and updated properties and features, including the Extension Library that is now built into core Notes. (Examples include Data Views and mobile development design patterns and enhancements, including the new DeviceBean and touch-based events.)
  2. It contains several new chapters (and additional information in existing chapters), including mobile controls and tons of great information about performance and scalability.
  3. If you’ve been working with XPages for awhile since you read the first edition, you’ll find that your additional experience provides a stronger foundation to get more out of the book. Several years after reading the first book, I can much more easily grasp the more complicated topics.

Writing Style

Even though it’s a massive book, the writing style is fairly colloquial, making it easier to read than you might expect.

There’s a reminder not to execute admin commands on a production server ‘willy nilly’ and a demo of customizing a pager to spoof the Goooogle results pager with “Loooootus”. Bonus points for referring to the use of $$Return fields as a “black magic” practice!

It definitely does not read like a stuffy manual; the authors clearly explain topics from the most basic (view panels) to the inkhorn (advanced performance analysis and tuning).

GoFaster=1

Okay — it’s not quite that simple, but the new chapters on performance tuning are well worth the price of the book alone. Two new chapters dive deep into the bowels of the JSF lifecycle and heap dumps to provide critical information about application performance and scalability. Many other tips throughout the book also show how to build performant applications by efficient use of partial refreshes and partial execution, as well as XPiNC enhancements to optimize initial load time and the use of applications with a shared design.

Things I Learned

Even after working with XPages heavily for the past 4-5 years, there are a myriad of features that I either learned about or gained a better understanding of as a result of reading this book.

It was a refresher on features that I was aware of but rarely use:

  • navigation rules
  • the ability to assign access control to a panel
  • the In Place Form control
  • mask converters
  • the createForm property of an XPage
  • control declaration snippets
  • the requestParamPrefix property of a document data source
  • the generic viewEntry object that you can use to get the current view entry (based on the context) just like currentDocument gets the closest document data source
  • the ability to programmatically get/set the custom properties of a different custom control from outside of that custom control
  • the ability (since 8.5.3) to use sortColumn and search properties of a view data source together (and the fact that column sorting icons are no longer disabled post-8.5.2 after a search was executed because now search and sort can work together)

I learned about some features that I wasn’t aware of:

  • making Dynamic Content facets available to web crawlers
  • the skipContainers property of a radio button
  • the fact that IE11 does not work with the version of CKEditor in 9.0.1

I also learned more about a new feature that I’m well aware of but haven’t tested even though I complained for a long time that it was desperately needed:

  • *cough* the SSJS debugger *cough*

There are many great debugging tips for XPiNC, SSJS, Java, Client-JavaScript, as well as information on how to debug when you find the compressed and aggregated libraries hard to troubleshoot.

And there’s plenty more where that came from. If you don’t know all of this already, it will be well worth your time to read this book. I still refer to the original Mastering XPages book from time to time and I’m sure I will refer to this edition numerous times in the future.