Your Development & Design Resource

IBM XPages + Google Maps Mashup (or Surfacing traditional Views as NotesViewEntryCollections with a viewPanel Control)

An XPage viewPanel is an amazingly powerful control... that is often used solely as originally intended. Of course the viewPanel can be used to surface NotesData in a multi-document grid which is designed to emulate the traditional IBM Lotus Notes Domino View Design Element... but it is capable of much, much more.

Typically, an XPages developer will see a need for a view within their application, and they'll do the following:

  1. Create (or update as needed) a View, let's say "vw_documents".
  2. Add a viewPanel to their XPage (which might even be named "vw_documents.xsp", and set the data source for said viewPanel to the vw_documents View.
  3. Go 1:1 for viewColumns to View Columns, showing the content of the vw_documents View.

And it's doing this 1:1 display of content that already exists in a traditional medium that causes most seasoned Domino Web Application developers to pause and wonder why the hell they should even consider undertaking the task of learning XPages.

I get it -- trust me, I do -- but consider this:

XPages allows you to rather easily do things that were previously either not even considered possible within the IBM Lotus Domino platform or things that could be done with considerable effort, hacks, and undoing all of the RAD that made the IBM Lotus Notes Domino platform the better choice in the first place!

The other day I showed you how you could put a single category embedded view (if we're talking the traditional design equivalent) inside of a View column with my Nested XPage viewPanels/Xzibit Meme Demo. I wanted to take that base concept (using a viewPanel Control for something other than the simply 1:1 representation of a traditional View Design Element) and run with it.

At the core of this next idea was to use the a View as nothing more really than a NotesDocumentCollection. And if I could use a viewPanel to surface a NotesDocumentCollection (or rather a NotesViewEntryCollection I guess...), then I would have the added bonus of having a built-in Pager Control that would allow me to easily navigate the returned NotesData.

Wanting to wow people, as well as give you all something that you might actually be able to use in your applications... I thought I'd do something both fun and functional.

I created another rather simple demo: an XPage + Google Maps mashup.

First, a NotesDatabase architecture overview:

IBM XPages + Google Maps Mashup NotesDatabase Architecture IBM XPages + Google Maps Mashup NotesDatabase Architecture

In the above simple diagram, we see that the XPage Google Maps (xmaps.nsf) NotesDatabase uses a remote Data Source which points to a Personal Address Book (pnab.nsf) that will contain contact information. The Home.xsp XPage not only surfaces NotesData from the pnab.nsf, but it also employs CSJS (Client Side JavaScript) to interact with the Google Maps API.

Now that we have at least a basic idea of where the major moving parts within this solution are going to go... let's get into the meat of the idea that kicked this whole thing off.

In the Home.xsp, I will create a very basic viewPanel Control that points to a remote Data Source (using the above example architecture, the pnab.nsf):

  1. <xp:viewPanel rows="30" id="viewPanel1" var="thisentry" showColumnHeader="false" indexVar="thisentry_n">
  2.     <xp:this.facets>
  3.         <xp:pager partialRefresh="true" layout="Previous Group Next" xp:key="headerPager" id="pager1" />
  4.     </xp:this.facets>
  5.     <>
  6.         <xp:dominoView var="vw_People" databaseName="pnab.nsf" viewName="People" />
  7.     </>
  8.     <xp:viewColumn id="viewColumn5">
  9.         <xp:this.value><![CDATA[#{javascript:return ""}]]></xp:this.value>
  11.         <xp:viewColumnHeader value="" />
  12.     </xp:viewColumn>
  13. </xp:viewPanel>

Now, a few things to point out here:

Line 9 shows the extremely useful technique I've mentioned in the past to allow you to add additional controls and passthru markup in an unbound viewColumn (that is, a viewColumn that is not bound directly to any of the target View Design Element columns).

Line 10 is intentionally left blank... It'll make sense shortly.

And quite possibly most important: a viewPanel Control does not care about its contents, but will rather simply display what you tell it to display!

What I mean by this statement is simply this: if you were to use the above viewPanel as-is, it would create a grid showing you absolutely nothing... but the viewPanel would rip through the 30 (or less if there are not 30 documents in the remote View) entries and display, well, nothing. Even more interesting, the Pager Controls know how many records there are in the remote View, so you could literally page through nothing until you hit the end of the NotesViewEntryCollection.

... but what if you put something in the viewColumn? What if, not caring about apple pie or all that's holy, you wanted to throw a bunch of controls in there that would do some really funky stuff?!

Take, for example, the following updated viewPanel Control markup:

  1. <xp:viewPanel rows="#{javascript:sessionScope.get('count') }"
  2. id="viewPanel1" var="thisentry" viewStyleClass="xspDataTableViewPanel NotesView google_static"
  3. showColumnHeader="false" indexVar="thisentry_n">
  4.  <xp:this.facets>
  5.   <xp:pager partialRefresh="true" layout="Previous Group Next"
  6.   xp:key="headerPager" id="pager1" />
  7.  </xp:this.facets>
  8.  <>
  9.   <xp:dominoView var="vw_People" databaseName="pnab.nsf"
  10.   viewName="People">
  11.   </xp:dominoView>
  12.  </>
  13.  <xp:viewColumn id="viewColumn5">
  14.   <xp:this.value><![CDATA[#{javascript:return ""}]]></xp:this.value>
  15.   <xp:panel>
  16.    <>
  17.     <xp:dominoDocument var="thisdoc" action="openDocument"
  18.     documentId="#{javascript:thisentry.getDocument().getUniversalID() ;}"
  19.     databaseName="mab.nsf">
  20.     </xp:dominoDocument>
  21.    </>
  22.    <xp:table styleClass="card">
  23.     <xp:tr>
  24.      <xp:td styleClass="col0">
  25.       <xp:table styleClass="info">
  26.        <xp:tr>
  27.         <xp:td styleClass="col0">
  28.          <xp:label value="Name:" />
  29.         </xp:td>
  30.         <xp:td styleClass="col1">
  31.          <xp:text value="#{thisdoc.fullname}" />
  32.         </xp:td>
  33.        </xp:tr>
  34.        <xp:tr>
  35.         <xp:td styleClass="col0">
  36.          <xp:label value="Company:" />
  37.         </xp:td>
  38.         <xp:td styleClass="col1">
  39.          <xp:text value="#{thisdoc.companyname}" />
  40.         </xp:td>
  41.        </xp:tr>
  42.        <xp:tr>
  43.         <xp:td styleClass="col0">
  44.          <xp:label value="Email:" />
  45.         </xp:td>
  46.         <xp:td styleClass="col1">
  47.          <xp:text value="#{thisdoc.mailaddress}" />
  48.         </xp:td>
  49.        </xp:tr>
  50.        <xp:tr>
  51.         <xp:td styleClass="col0">
  52.          <xp:label value="Phone:" />
  53.         </xp:td>
  54.         <xp:td styleClass="col1">
  55.          <xp:text value="#{thisdoc.officephonenumber}" />
  56.         </xp:td>
  57.        </xp:tr>
  58.        <xp:tr>
  59.         <xp:td styleClass="col0">
  60.          <xp:label value="Address:" />
  61.         </xp:td>
  62.         <xp:td styleClass="col1">
  63.          <xp:text value="#{thisdoc.officestreetaddress}" />
  64.          <xp:br />
  65.          <xp:text value="#{thisdoc.officezip}" />
  66.          <xp:text value="#{thisdoc.officestate}" />
  67.          <xp:br />
  68.          <xp:text value="#{thisdoc.officecountry}" />
  69.         </xp:td>
  70.        </xp:tr>
  71.       </xp:table>
  72.      </xp:td>
  73.      <xp:td styleClass="col1">
  74.       <xp:div styleClass="container_map">
  75.        <xp:div id="map_canvas">
  76.         <xc:map_static mapsize="#{javascript:sessionScope.mapsize }"
  77.         zoom="#{javascript:sessionScope.zoom_static}" maptype="#{javascript:sessionScope.maptype }"
  78.         address="#{javascript:thisdoc.getItemValueString('officestreetaddress') + '%20' + thisdoc.getItemValueString('officezip') + '%20' + thisdoc.getItemValueString('officestate') + '%20' + thisdoc.getItemValueString('officecountry')}" />
  79.        </xp:div>
  80.       </xp:div>
  81.      </xp:td>
  82.     </xp:tr>
  83.    </xp:table>
  84.   </xp:panel>
  85.   <xp:viewColumnHeader value="" id="viewColumnHeader5">
  86.   </xp:viewColumnHeader>
  87.  </xp:viewColumn>
  88. </xp:viewPanel>

There is a lot going on in this markup... but the thing that should jump out at you at first is from Line 15. I'm simply creating a Panel Control inside of the viewColumn, and defining a Data Source for the Panel Control which returns (in this case) the entire NotesDocument from the NotesViewEntry.

Once I have it inside it's own Panel Control, I can do whatever I want with that NotesDocument without doing constant (and costly) lookups to the NotesDocument...

So what did I do? I threw in some bells and whistles inside the Panels, including some basic NotesDocument data surfaced in a traditional way (label + field value), Output Script Controls that push the address information to an array that's used to create both Static Maps and Interactive/Dynamic Maps via the Google Maps API, and even a Custom Control for those Google Maps.

XPages + Google Maps Mashup Panel Control 'Card' IBM XPages + Google Maps Mashup Panel Control 'Card'

So while I could have stopped at a card, I decided to take it a few steps further.

Now, the rest of the demo is just playing around with the Google Maps API and (quite frankly) just having some fun with it.

For example, not only can you control the default zoom options for the static and interactive maps via the Options Section, but you can also control the number of entries returned to the user. All of these options set sessionScope variables, which will allow you to give your users a persistent experience across the application... at least until they restart their sessions.


There's some pretty cool stuff in the demo application, but I think the idea that started this article (and the demo application) is the biggest take-away from this article:

Don't be restricted by the pre-conceived limitations of any XPage Control, nor restricted by their "intended" usage. The viewPanel Control is amazingly functional and can give you an opportunity to provide your application users with features that quite frankly just weren't previously possible with this platform.

Ultimately it is understanding that you have full control over the end-result output from the Domino server (finally!) without hacks and undoing all of the RAD that makes IBM Lotus Domino such an amazing platform.


You can download the demo application here:

IBM XPages + Google Maps Mashup Application Download

About the author: Chris Toohey

Thought Leadership, Web & Mobile Application Development, Solutions Integration, Technical Writing & Mentoring

A published developer and webmaster of, Chris Toohey specializes in platform application development, solutions integration, and evangelism of platform capabilities and best practices.

More from is powered by IBM Notes Domino XPages & hosted by Prominic.NET

Contact Us

Use our Contact / Feedback form or one of these email addresses:

Creative Commons License

Except where otherwise noted, by Chris Toohey is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.