Xpages

An apparent theme: application agnosticism

As always, I’m enjoying learning new things and seeing old friends (and some new ones!) at MWLUG. There have been sessions that talked about accessing and displaying data from within Notes without using Notes or XPages and sessions about accessing non-Notes data using XPages. Much of the excitement is about having data exposed via a REST service and using a good front end tool to display it. So, in some senses, we are talking about not needing Notes at all.

Earlier this summer, our company’s Vice President of the Office of Information Management and Technology announced that we’re actually moving away from the Notes client for mail. While I knew that this was a possibility, I’d thought we were still just looking at allowing folks to choose other mail applications to access their mail. Of course, allowing multiple mail applications either tosses some of your users to the wolves of ‘no support’ or complicates things immeasurably for support. So, it does make sense and was not particularly unexpected. Nonetheless, it still surprised me.

The core of our Notes use has always applications anyway. Our business relies on a lot of people working disconnected and, as such, the Notes replication model has been key to our need for Notes. So, we’ll be keeping the Notes client on machines for all those people who work disconnected, but much of our access had already moved to the browser. So, in a sense the work I do was moving in this direction as well.

So, the agnosticism is where we’re all going, it seems. I guess the mantra remains — use the best tool for the job.

Categories: Conferences, General Interest, Xpages | Tags: | Leave a comment

Sessions for #MWLUG2016

Looking over the announced sessions for MWLUG 2016 down in Austin, I’m excited. There’s a good mix of things you can use today and road maps you can apply in the future. I’m a developer, so all the Development and Best Practices sessions look interesting. The tough part will be picking which ones to attend (and record!)

Five sessions that jumped off the page at me are, in no particular order:

Debugging Java In Your Domino Applications with Julian Robichaux — Java just kills me sometimes. Heck, all of XPages does, but learning more about how to debug and troubleshoot is always useful.

Extreme Development: Pair Programming with Devin Olson and Mike McGarel — Now that Elvis Lezcano is aboard at DAI, we might have a chance to do some of this. He’s the smartest developer I’ve worked with, which I why this is the third job we’ve had together.

Think Outside The Box with Karl-Henry Martinsson — We’ve just been breaking into using REST services with jQuery and Bootstrap to present data, so getting someone else’s take on it will expand my ability to combine data from multiple databases to dazzle our users. (see Kathy’s session on dashboards for ideas she’s using for us)

Getting Your Hands on Graphs with Nathan Freeman — I have loved all the conceptual sessions and want to learn more. If Nathan can get me to understand, there’s no telling how far we can go!

A Modernized Developer’s Workflow with Domino/XPages with Eric McCormick — Workflow has always been a great strength of Notes, so getting modernized by someone who’s not spouting theory, but displaying methods in practice is exciting.

Now, before anyone complains about me not mentioning their sessions…. I’d like to sit in on about… 27 sessions. Since Marky has made no progress on the time machine and IBM doesn’t have anyone working replication of people instead of just data and design, I think I only get about a dozen sessions.

Categories: Conferences, Java, Xpages | Tags: , , , , , , , , , , , , , , | Leave a comment

Orphan #Java class files in the Local folder in #XPages

Every once in a while, when I do a design refresh for one of my XPages databases, I notice it adding .class files for XPages that I’ve already deleted.

So, I do another clean & build on the template, but they’re still there. Annoyed, I console myself that the files are never referenced, so I don’t need to worry about it. Of course, I worry anyway.

Vestigal Java FilesSo, in my quest to figure out what was going on, I continued poking around. I checked in the Local folder while looking at the Navigator Eclipse view and saw that the .java files were still sitting there as orphans, with no XPage in the design any more. So, I did just a ‘Clean’ and it eliminated all the files except for those. According to “Mastering XPages”, this doesn’t happen. In warning you not to create Java files in the Local folder, it states that

the incremental builder in Domino Designer would then flush the contents of the Local folder before recompiling all the XPages again. Your custom Java source files would be permanently deleted in this case.

Yet, those files stubbornly remain. Fortunately, when I create a new copy of the template, none of those Local files copy into the new database. So, my habit of creating new files for new versions of the template has helped me by dodging this little, harmless bug.

 

Categories: Java, Xpages | Tags: , , | Leave a comment

Copying property definitions for custom controls in #XPages

PropertyDefinitionSometimes, when I’m working on my applications, I’ll decide that I want to copy a property definition from one custom control to another. When using the standard properties interface, it’s a lot of work. You have to put the right information into the right fields and switch between tabs. It’s a real bother.

Interestingly, it never occurred to me to look at the .xsp-config documents until we started using SourceTree for source control.* I was looking at some changes and suddenly noticed that … the property definitions are right there! So, instead of slogging through manual editing of the definitions in the UI in Designer, I could just go to either the Navigator or the Package Explorer Eclipse view to open and edit the properties as simple XML.

    <property>
      <property-name>deletionAllowed</property-name>
      <property-class>boolean</property-class>
      <display-name>Allow deletions of attachments</display-name>
      <property-extension>
        <designer-extension>
          <category>Control information</category>
          <editor>com.ibm.std.Boolean</editor>
          <default-value>false</default-value>
        </designer-extension>
        <required>false</required>
      </property-extension>
      <description>Determines if attachments may be deleted,
Deletions are soft and may be recovered within recovery period.
(default: false)</description>
    </property>

So, if I realized that I needed that property on a different custom control, or I wanted to create a second property on the same control with a different name, I could simply copy-paste the code and edit it as necessary.

There is one pretty cool PropertyCategoriespiece of the property definition that seems only to be available when you edit the code directly. That’s the category definition. If you provide a category definition as shown in my code snippet above, then when you’re setting the properties of the custom control that you’ve inserted, it displays categorized properties as part of that category in the UI for ‘All Properties’. I urge you to make sure that if you do fiddle with the properties on the back-end this way, that you first give yourself examples by doing most of it using the UI for adding properties. As with anything else I do in XPages, I find myself wanting to get right to the code, not to use the UI provided by Designer.

It’s interesting that it actually does give you an option that you don’t seem to be able to get otherwise.

* I want to thank PSC for getting us to use SourceTree and bitbucket (while Kathy and Brad do some work for us) and to Paul Withers for his excellent Notes in 9 video on how to do it (for our internal ones).

Categories: Xpages | Tags: , , , , | Leave a comment

A venture into DateTimes in #XPages

Recently, I was trying to change the display of some dates in our application. We’ve found that the simplest way for our international company to display dates is to stick to the dd-MMM-yyyy format since that seems the clearest. No one wonders whether 1/4/16 represent the 4th of January or the 1st of April, because they all see 04-Jan-2016.

This was simplest to fix for all the date fields that use simple inputText controls – just change the convertDateTime pattern.

						<xp:inputText value="#{modDoc.RevPerf1Date}" id="revisedPerformanceDateStart">
							<xp:dateTimeHelper></xp:dateTimeHelper>
							<xp:this.converter>
								<xp:convertDateTime pattern="dd-MMM-yyyy"></xp:convertDateTime>
							</xp:this.converter>
						</xp:inputText>

Then, I noticed that in place I wasn’t letting them edit the dates, it wasn’t using that format. We’re not displaying the inputText, but using a label computed from that control to determine what to display. This code may actually hurt your eyes, but it did convert the date into a US-format date, like 01/04/2016.

<xp:label id="label14" style="color:black;">
	<xp:this.value><![CDATA[#{javascript:if(modDoc.isNewNote()){ 
		if(modDoc.getItemValueDate("PerfDate1") != null) {
			var termBegin = @Text(modDoc.getItemValue("PerfDate1")); 
			if(termBegin != ""){ 
				var dt2:NotesDateTime = session.createDateTime(termBegin); 
				var d = new Date(dt2.toJavaDate()); 
				var mon = ("0" + (d.getMonth() + 1)).slice(-2) 
				var td = ("0" + d.getDate()).slice(-2); 
				var yr = d.getFullYear(); 

				mon + "/" + td + "/" + yr 
			}
		} else { 
			if(sessionScope.POPerformBeginDate != null && sessionScope.POPerformBeginDate != "null" && sessionScope.POPerformBeginDate != ""){ 
				var dt:NotesDateTime = session.createDateTime(sessionScope.POPerformBeginDate); 
				dt.toJavaDate() modDoc.setValue("PerfDate1",dt); 
				var d = new Date(dt.toJavaDate()); 
				var mon = ("0" + (d.getMonth() + 1)).slice(-2) 
				var td = ("0" + d.getDate()).slice(-2); 
				var yr = d.getFullYear(); 

				mon + "/" + td + "/" + yr 
			} 
		} 
	} else { 
		var termBegin = @Text(modDoc.getItemValue("PerfDate1")); 
		if(termBegin != ""){ 
			var dt2:NotesDateTime = session.createDateTime(termBegin); 
			var d = new Date(dt2.toJavaDate()); 
			var mon = ("0" + (d.getMonth() + 1)).slice(-2) 
			var td = ("0" + d.getDate()).slice(-2); 
			var yr = d.getFullYear(); 

			mon + "/" + td + "/" + yr; 
		}
	}}]]></xp:this.value>
</xp:label>

Before we decided to convert to the new format, the ugliness of the code didn’t matter. It was used in one place (printing purchase order modifications) and it worked. Since I didn’t want to invent my own library function for computing the text value of the date in the new format, I searched for a better way to format the dates. I ran across Declan Lynch’s blog entry on using SimpleDateFormat. Unfortunately, that just points in the right direction, rather than providing working code. So, when I tried to implement that for displaying the labels correctly, I just couldn’t get it to work. This frustration led me to the simple solution: use convertDateTime on the labels. Duh!

						<xp:label id="performanceDateStartDisplay" style="color:black;">
							<xp:this.value><![CDATA[#{javascript:getComponent("performanceDateStart").getValue();}]]></xp:this.value>
							<xp:this.converter>
								<xp:convertDateTime pattern="dd-MMM-yyyy"></xp:convertDateTime>
							</xp:this.converter>
						</xp:label>

Now, on the printed purchase order modification, I also had changes in dates detailed in the text as a sentence. So, you’d see To Change the Period of performance from 01/04/2016 to 01/08/2106 to 01/11/2016 to 01/15/2016, which was not using our newly minted date format. I couldn’t figure out a way to use the converters within the text without creating several computed labels (each with a rendered formula) to display the text. Then, I remembered my dalliance with SimpleDateFormatter.

So, within that control, I brought in the package and created a function that gets the field value as a Vector using getItemValueDateTimeArray and formats it using my chosen SimpleDateFormat. The text string gets built with four calls to that function and returns our text To Change the Period of performance from 04-Jan-2016 to 08-Jan-2016 to 11-Jan-2016 to 15-Jan-2016

<xp:text id="revisedPerformanceRange">
	<xp:this.value><![CDATA[#{javascript:function getFormattedDate ( doc:NotesDocument, fieldName:String ) {
	importPackage(java.text);

	var dateFormatter:java.text.SimpleDateFormat = new SimpleDateFormat("dd-MMM-yyyy");
	var d:Date = new Date(@Today());

	if ( doc.hasItem (fieldName) ) {
		var valueVector:java.util.Vector = doc.getItemValueDateTimeArray(fieldName);
		var iterator = valueVector.iterator();

		while (iterator.hasNext()) {
			var itemvalue = iterator.next();
			if ((typeof(itemvalue)).endsWith("DateTime")) {
				d = new Date(itemvalue.toJavaDate());
				return dateFormatter.format(d);
			}
		}
	} else {
		return fieldName + " is not on the document"
	}

}

var modNotesDoc:NotesDocument = modDoc.getDocument();

var revisedPerformanceRangeText = "To Change the Period of performance from ";
	
revisedPerformanceRangeText = revisedPerformanceRangeText + getFormattedDate(modNotesDoc,"PerfDate1") + " to ";
revisedPerformanceRangeText = revisedPerformanceRangeText + getFormattedDate(modNotesDoc,"PerfDate2") + " to ";
revisedPerformanceRangeText = revisedPerformanceRangeText + getFormattedDate(modNotesDoc,"RevPerf1Date") + " to ";
revisedPerformanceRangeText = revisedPerformanceRangeText + getFormattedDate(modNotesDoc,"RevPerf2Date");

return revisedPerformanceRangeText;}]]></xp:this.value>
</xp:text>

Took some fiddling to figure it out, but gave me exactly what I wanted, two different ways.

Categories: Java, Server-Side Javascript, Xpages | Tags: , , , , , | 2 Comments

Writing and speaking about your code might actually make it better

I like to think I’m a smart guy, but I know there are many people out there who are smarter than I am. Sometimes, just thinking about Kathy and Julian showing us what we can do in Java in 10 Lines or Less helps one tighten up some code. Other times, sitting down to write about a code problem or a solution might actually make your code better.

Our workflow application builds a list of approvers at each step that’s built from three fields on the workflow step configuration document. That list is stored on the document being approved so that we don’t have to do a lookup to determine who can take action. Sometimes, the approvers might be in more than one of those fields and thus, with sloppy coding, end up in the approver list more than once. The workflow step configuration allows us to choose which of those three fields is used to notify approvers (it could even be all three fields). Since we recently decided to keep track of who is notified, since they are the primary approver(s), the workflow code is now recording the names of those notified as it sends the message. Because the system is already used in 15 projects and will be used in 70+ projects at time in the future, it must be a highly configurable workflow.

My coding challenge was simple. I wanted to display on the XPage the primary approver(s) and the other (proxy) approvers, as separate lists. This is probably a two-minute exercise for someone who knows what they’re doing, so it took me an hour.

Because I worried about the duplicates, I didn’t use the remove method of the java.util.Vector, which would only take out the first instance of the duplicate. I lamented this, since playing with arrays is a little more involved. Basically, I decided to build the array by looping through looking for matches. Then, I was puzzled about how to find array elements in a Vector, but I got over that when I realized that I could use @Unique to clean both and make them both arrays! OK, not the best idea, but it worked.

<xp:label id="recipientListLabel">
	<xp:this.value><![CDATA[#{javascript:
var recipients=procureDoc.getDocument().getItemValue("RecipientList");
return "Approvers: " + @Unique(@Name("[CN]",recipients)); }]]></xp:this.value>
</xp:label>
<xp:label id="proxyListLabel">
	<xp:this.value><![CDATA[#{javascript:
var recipients = @Unique ( procureDoc.getDocument().getItemValue("RecipientList") );
var allApprovers = @Unique ( procureDoc.getDocument().getItemValue("TSWFCurApprovers") ); 
ar proxies = [];
for ( approver in allApprovers ) { 
	if ( @IsNotMember( allApprovers[approver], recipients) ) { 
		proxies.push ( allApprovers[approver] ); 
	}
} 
var proxyList = @Implode(@Name("[CN]",proxies),", "); 
return "Proxy Approvers: " + proxyList; }]]></xp:this.value>
</xp:label>

I was pretty happy with that, because I thought it would take me a lot of extensive looping and nonsense (since a quick search hadn’t revealed an array minus array code snippet.

But, as I sat down to write about this and see if someone could better it (assuming that it would take someone two minutes), I returned to the documentation. I just wanted to double-check that remove was a “method” and that I wasn’t being foolish and calling it a “function” instead. So, then, I saw it. The code simplifier. removeAll was exactly what I was looking for and, I’d bet, something every Java coder worth their salt could have slapped on the problem in an instant. Learning curve.

<xp:label id="proxyListLabel">
	<xp:this.value><![CDATA[#{javascript:
var recipients:java.util.Vector = procureDoc.getDocument().getItemValue("RecipientList");
var allApprovers:java.util.Vector = procureDoc.getDocument().getItemValue("TSWFCurApprovers");
allApprovers.removeAll(recipients); 

var proxyList = @Implode(@Name("[CN]",allApprovers),", ");
return "Proxy Approvers: " + proxyList; }]]></xp:this.value>
</xp:label>

It’s always good to learn. When I was a Scoutmaster, one of the things I told the older Scouts was that by teaching skills, they ended up learning them even better themselves. So, as I sat down to write this, more to share the experience than to teach, I ended up learning more. It forced me to research a little, to make sure I was covering my bases and allowed me to cut some inelegant code from 8 lines to 5 (though I imagine the daring would simply cut it to 2 lines).

So, next time you or your boss thinks there’s no time for writing up your thoughts on coding, or no time to speak at conferences, or no time to share your ideas at the local user group meeting, remember that you’re likely to end up with better code even if you’re the only one who contributes. The process forces it on you.

Categories: Java, Server-Side Javascript, Xpages | Tags: , , , , , | 2 Comments

Syntax errors will be the death of us all in #XPages

I upgraded one of our databases overseas and was puzzled when they reported that they suddenly could no longer see the attachments to their purchase orders. Actually, when I heard it, it was just for a single purchase order.

You see, we’ve got our attachments all stored in a separate database for each project. This is nice because it reduces the risk of truncations, moves the big data out of the main database and generally calms down the Notes admin team. However, it does require that there is a solid link between the main document and any attachment documents. Our main link is a transactionKey field, which identifies not only which main document the attachment belongs to, but also where that attachment is displayed on the main document’s XPage.

I had a custom control that handled one of those display areas as a tab in a tabbed panel. We had decided to add a new document type (release orders, to go with purchase orders), so I wanted my custom control to be more generic, to handle multiple document types. There are a few values that were based on the document type. Rather than passing 4 or 5 properties, I decided to pass in 1 and compute the others. Brilliant, except, I forgot how Javascript works.

My “brilliant” code….

<xp:scriptBlock id="scriptBlock1">
	<xp:this.value><![CDATA[#{javascript:switch ( compositeData.mainDocumentType ) {
case "Purchase Order":
	viewScope.signedDocumentTypes = "Purchase Order or subcontract";
	viewScope.transactionType = "PurchaseRecord";
	viewScope.transactionTag = "po";
	viewScope.additionalDirections = " and all invoices and goods delivery receipts";
	break;
case "Release Order":
	viewScope.signedDocumentTypes = "Release Order";
	viewScope.transactionType = "ReleaseOrder";
	viewScope.transactionTag = "rel";
	viewScope.additionalDirections = "";
	break;
default:
	// thus far, BPA only, but could be any mainDocumentType with no spaces in the name
	viewScope.signedDocumentTypes = compositeData.mainDocumentType;
	viewScope.transactionType = compositeData.mainDocumentType;
	viewScope.transactionTag = @LowerCase(compositeData.mainDocumentType);
	viewScope.additionalDirections = "";
};}]]></xp:this.value>
</xp:scriptBlock>

So, every Purchase Order attachment got the signedDocumentTypes ‘Purchase Order’, the type ‘Purchase Order’, the tag ‘purchase order’ and no additional directions. Only took about 4 hours of looking at data, chasing the various design changes and reading code before I looked at that piece of code and realized my obvious mistake.

Sometimes, you just need a break;

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

Using the namePicker from the Extension Library in #XPages

One of the things that always seemed weak to me about my Domino web implementations was the selection and display of names. Now that XPages turns our work into the creation of actual web pages, the tools available are so much better and extend the flexibility of Notes development into the 21st century.

OK, saying “21st century” doesn’t sound as cool now that we’re halfway through the second decade, but since so much of what I’d done looked like it was designed in 1995, getting out of 20th century web development is a big step for me!

There are two control types from the Extension Library that are relevant here: the Dojo Name Text Box and the Name Picker.

The Dojo Name Text Box gives us that modern, Facebook-style name display, including a little “x” to delete the name from the list:

SelectTechnicalApprover

The Name Picker is very flexible. In my code example, I wanted users to be able to select from a view in the database, from the corporate NAB or, if they’re using XPiNC, from their personal address book. One of the nice thing about the picker is that if you supply multiple dataProviders, it provides a picklist of those choices, but if you only supply one (or if only one is ‘loaded’) then it doesn’t display the picklist of address books, allowing the user to just use the one NAB you’ve set as a dataProvider. It’s nice when you don’t have to tell the control the obvious things.

When you are setting up the namePicker, you need to select your dataProvider(s). There are three types (beanNamePicker, dominoNABNamePicker and dominoViewNamePicker) and the aggregator for using multiple providers (namePickerAggregator). As you can see in the example, if you choose the aggregator, you then identify your dataProviders inside that aggregator.

The dominoNABNamePicker is likely the one that’s most used, so also has a few pre-set configuration choices. While there are six parameters you could use, you can choose just to use two: addressBookSel and nameList.

  • For addressBookSel, you’re choosing between broad options: all, all-public, all-private, first, first-public, or db-name. Because our environment includes an address book for non-staff folks, I didn’t choose either all or all-public, which would have included that address book. So, I went with db-name, which allows you to specify the database name. The broader categories will work for you if you don’t have the requirement to leave out one of the address books.
  • For nameList, you also get some choices that are functional and make things easy for you: people, peopleByLastName, groups, or peopleAndGroups. This overrides two of the other parameters – groups and people, who are both boolean values. Using people as the nameList value returns people by first name, which can be a little annoying if you have thousands of choices, so it’s nice that peopleByLastName has beeen added.
  • The addressBookDb parameter only matters if you’ve selected db-name as the addressBookSel value. In my case, when the user is accessing our database in XPiNC on the local replica, their personal address book is names.nsf and the corporate one is minnames.nsf, so I’m doing a little trickery to give them all the options. I suppose I could have specified the corporate NAB and then used all-private to get to the personal address book, but I didn’t think of that first.
  • The loaded parameter is the most powerful one here for me, since it would allow me to provide the user more or fewer choices based on a formula. In this instance, the choice is only based on which client they’re using, but I could imagine basing it on roles, on values on the document or any variety of other things.
  • As mentioned above, the boolean values for groups and people are overridden by the value of nameList, so I’d avoid using them. Confusion is the likely result.

I was especially happy that using a view within the existing database in the namePicker was so simple. All I had to do was create a view that had the values I wanted in the first column, sorted ascending (I assume descending works as well, but that would be user-unfriendly). The dominoViewNamePicker is simple – really you only need to specify the databaseName and viewName. My samples view was in the same database, but you can point it elsewhere. There is a loaded parameter for your use and you can choose a label for the pick list when using multiple dataProviders.

							<xe:djextNameTextBox id="approversNameTextBox" value="#{deliverableDoc.Approvers}">
							</xe:djextNameTextBox>
							<xe:namePicker id="approversNamePicker" for="approversNameTextBox" listHeight="300px" listWidth="150px"
								pickerText="Click to select technical approver" rendered="#{javascript:deliverableDoc.isEditable();}">
								<xe:this.dataProvider>
									<xe:namePickerAggregator>
										<xe:this.dataProviders>
											<xe:dominoViewNamePicker label="Contacts" viewName="ContactsNotesName">
												<xe:this.databaseName><![CDATA[#{javascript:getDb("sharedResourcesDb",true);}]]></xe:this.databaseName>
											</xe:dominoViewNamePicker>
											<xe:dominoNABNamePicker nameList="people" addressBookSel="db-name" addressBookDb="names.nsf">
											</xe:dominoNABNamePicker>
											<xe:dominoNABNamePicker nameList="people" addressBookSel="db-name" addressBookDb="minnames.nsf">
												<xe:this.loaded><![CDATA[${javascript:@ClientType() == "Notes";}]]></xe:this.loaded>
											</xe:dominoNABNamePicker>
										</xe:this.dataProviders>
									</xe:namePickerAggregator>
								</xe:this.dataProvider>
							</xe:namePicker>

Unfortunately, I know very little about beans, so I’ll leave discussion of that to someone else, or to a future post if I can follow Russ Maher’s example and get into using Beans wherever they’re useful. The more tools you have in the box, the quicker and more flexibly you can get your work done.

Categories: Extension Library, Utilities, Xpages | Tags: , , , , , , | 2 Comments

Quick look at dialogContent and dialogButtonBar in #XPages

One of the challenges of XPages is figuring out what controls actually do. In a prior post, I wrote about using a dialog to return values to the XPage. It never occurred to me to use dialogContent and dialogButtonBar in the dialog. Today, when I was creating another dialog, I used the palette to find the dialog control and noticed those two. I wondered what they did, since, in my example, I hadn’t used them and my dialog displayed easily.

I checked the documentation, which really doesn’t help, stating simply that the dialogButtonBar “Contains buttons in a dialog.” That sure doesn’t tell me why I should use it instead of just putting my controls right into the dialog.

So, I did a little test.

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xe="http://www.ibm.com/xsp/coreex">
	<xp:link escape="true" text="Add a Linkage" id="addLinkageLink">
		<xp:image url="/plus.png" id="addLinkageImage" style="position:relative;left:2px;top:-2px;margin-right:5px;">
		</xp:image>
		<xp:eventHandler event="onclick" submit="false">
			<xp:this.script><![CDATA[XSP.openDialog('#{id:linkageDialog}');]]></xp:this.script>
		</xp:eventHandler>
	</xp:link>
	<xp:link escape="true" text="Add without content control" id="link1">
		<xp:image url="/plus.png" id="image1" style="position:relative;left:2px;top:-2px;margin-right:5px;">
		</xp:image>
		<xp:eventHandler event="onclick" submit="false">
			<xp:this.script><![CDATA[XSP.openDialog('#{id:contentFreeDialog}');]]></xp:this.script>
		</xp:eventHandler>
	</xp:link>
	<xp:br></xp:br>
	<xe:dialog id="linkageDialog" title="Add linkage">
		<xe:dialogContent id="dialogContent1">
			<xp:label value="Label" id="label1"></xp:label>
			<xp:inputText id="inputText1"></xp:inputText>
		</xe:dialogContent>
		<xe:dialogButtonBar id="dialogButtonBar1">
			<xp:button value="OK" id="button1"></xp:button>
			<xp:button value="Cancel" id="button3">
				<xp:eventHandler event="onclick" submit="false">
					<xp:this.script><![CDATA[XSP.closeDialog('#{id:linkageDialog}');]]></xp:this.script>
				</xp:eventHandler>
			</xp:button>
		</xe:dialogButtonBar>
	</xe:dialog>
	<xe:dialog id="contentFreeDialog">
		<xp:label value="Label" id="label2"></xp:label>
		<xp:inputText id="inputText2"></xp:inputText>
		<xp:button value="OK" id="button2"></xp:button>
		<xp:button value="Cancel" id="button4">
			<xp:eventHandler event="onclick" submit="false">
				<xp:this.script><![CDATA[XSP.closeDialog('#{id:contentFreeDialog}');]]></xp:this.script>
			</xp:eventHandler>
		</xp:button>
	</xe:dialog>
</xp:view>

The dialog using the dialogContent and dialogButtonBar controls looks far nicer. I did put the title on the one without those sub-controls in order to make the disparity even more stark. When using the sub-controls, the placement of the content and the buttons is handled for you, without requiring the additional formatting I had to do in the prior post using a dialog. So, here’s the ugly one, which doesn’t use the sub-controls and will require some formatting:

DialogWithoutSubControls

And here’s the pretty one, with the sub-controls in use:

DialogUsingSubControls

So, using the dialogContent and dialogButtonBar controls makes your life easier and your users happier.

Categories: Extension Library, Xpages | Tags: , , , , | Leave a comment

Mastering #XPages: Buy the eBook

I’ve had my copy of Mastering XPages (2nd Edition) for a little while. It’s a darn heavy book, but because of that, it’s packed with information. More packed that the 1st Edition? Why, yes, yes it is. Mine is in hard cover and the 1100-plus pages is a little bit daunting to carry around. Nonetheless, there is nothing quite like it.

I’ve always been a guy who learns well from physical books. I like the feel of them. I like the structured approach to learning that they provide. The code examples in the text, along with great images, really provide a lot on insight to what you can do with XPages. I’d say it compares well with reading a variety of blogs because of the depth and structure provided. I can and do read dozens of blogs, but they don’t have any comprehensive structure. For example, I write mostly about the problems that I’ve encountered coding in XPages. While that might teach you a lot about XPages, it’s got no structure at all. If you read Marky’s blog, you’ll learn a lot, and often in a structured manner about the topic he’s dealing with, but it won’t take you from ground zero to XPages developer. Similarly, the extensive XPages library over at Notes in 9 is fantastic (and you ought to be watching all of those videos), but there’s not necessarily a comprehensive plan, nor a logical learning order. I love Paul Della-Nebbia’s video series, Intro to XPages, as it’s a great place to start learning, but you won’t get 21 chapters of knowledge there.

Now, before you start thinking it’s a massive slog of text with only hard-copy code samples, brace yourself. Just like the blogs noted above, you can download sample applications that go along with the examples in the book to help you learn all aspects of XPages.

In the months I’ve owned it, I’ve read several chapters straight through, used one chapter (18 – Internationalization) to add a huge capability to our applications with a mere hour of research and searched through the massive text for little bits and pieces.

This brings me to the electronic version. I’m old. I’ve admitted above to loving books. As I was pondering my review, I thought I needed to try the electronic version. So, using the code supplied in the hard-copy of the book, I added the electronic version to my tablet (Motorola Xoom that’s getting a bit long-in-the-tooth). I searched a little, which definitely beats the heck out of the hard copy’s index, since you can not only find more, but also jump directly to that spot in the text. I sat and read a little — I’ve already established that I’m willing to read books on it with military history — and it’s very nice. When you get into books over a thousand pages, it’s so much easier to tote around an electronic copy in your backpack, or read on the Metro, or pluck it out of your carry-on while flying to MWLUG. I highly recommend the electronic version. Depending on finances, I might not worry about the hard copy next time and simply get my hands on the e-book.

This edition adds in various changes from the release of the 1st edition — over 3 years of changes. It’s written in the irrepressible style that won Marky’s approval. It’s a jam-packed learning experience. If you don’t have it already, do yourself a favor and get a copy today. Try the electronic version. You’ll thank me.

Categories: Xpages | Tags: , , , , , , | 3 Comments

Create a free website or blog at WordPress.com.

%d bloggers like this: