Previous|Next

Today we're looking at custom component creation within an NSF. We're not talking about custom controls, though creating a custom control before creating a component is definately a good way to prototype a custom component. Plus the DDE Builder does kind-of turn our custom controls into components, so they are kind-of custom components, but not really. What we're really talking about is a component that shows up in the DDE Component Palette and has it's own properties, methods and visual representation within designer and on an XPage and is serializable. This component can be used in multiple applications without any modification and may require a complex type in order to function (if you're familiar with the application layout in the extension library, all of the different nodes you can add to the various properties are complex types).

So you're asking, but don't I have to include these in a plugin that lives on the server? Well, you can certainly do that and by including your component in a library that resides on the server you won't have to move your custom component from application to application, it would just be available. But that is a whole different topic altogether, we're wanting to create our component in an NSF so we don't have to bug an administrator to put files on the server for us.

Since this will be a rather lengthy article, we will cover this topic in multiple posts. In part 1 we'll setup our DDE environment to allow us to create java class files and come up with what the properties of the component will be. And if it's not too long of a post, going ahead and creating our component class file.

To start with we'll need to setup our NSF so that we have a place to put our class files. So, go ahead and create a new application and in DDE add the package explorer view to your perspective. You do this by clicking "Window\Show Eclipse Views\Other" and in the resulting dialog navigate to "Java\Package Explorer" and click OK. You should now have a new tab next to the Application Explorer called Package Explorer. Go ahead and open that up if you haven't already.

Navigate to your new application and open up the WebContent folder, right click the WEB-INF folder and select "New\Folder" (if you don't have this option click other and select folder from the dialog). Name your folder "src". Now right click the "src" folder and select "Build Path\Use as Source Folder". This will cause the folder to disappear from WebContent\WEB-INF\src and be moved towards the top of the folder list into "WebContent/WEB-INF/src". This is where we'll be placing all of our java files for this series. As a side note, if you're using 8.5.3 you really don't have to do this. You can use the Java section underneath the code category. But I don't like how it organizes the class files as it doesn't "categorize" the class files underneath their respective packages. But that's my personal preference.

Right click the "WebContent/WEB-INF/src" folder and select "New\Package" (if you don't have this option click other and select package from the dialog). This will be where our component classes are stored. I'm naming mine "com.keithstric.components". Follow the same process and add another package called "com.keithstric.renderkit" and this will be where our renderers are stored.

This component will be for a contact type application with some basic information. So for reusability and simplicity sake we'll create a component with a few fields within a fieldset. This should allow us to demonstrate a couple of different methods and show that a component doesn't have to be just a single html element but can be something more complex also.

Now that we've got our basic structure setup we can start writing some code. We'll start off with the component class. This class should have a few different properties and all of these properties will have getters and setters. The properties will be:

  • Boolean showFirstName
  • Boolean showLastName
  • Boolean showEmailAddress
  • Boolean showPhoneNumber
  • String firstName
  • String lastName
  • String emailAddress
  • String phoneNumber

We'll be defining these properties and then let eclipse create getters and setters for us. So, let's start our class by right clicking the com.keithstric.components package we created earlier and pick "Class" from the drop down menu (If you don't have this option click other and select class from the dialog). Give it a name of CommonContactInfo and set the "Super class" to javax.faces.component.UIComponent and click the "Finish" button. This should create a new .java file in our com.keithstric.components package and our class will have a name of com.keithstric.components.CommonContactInfo.

Once that is completed we can now define our properties like below:

private static final String RENDERER_TYPE = "com.keithstric.CommonContactInfo";
private static final String COMPONENT_FAMILY = "com.keithstric";

private Boolean showFirstName;
private Boolean showLastName;
private Boolean showEmailAddress;
private Boolean showPhoneNumber;
private String firstName;
private String lastName;
private String emailAddress;
private String phoneNumber;

Once that is done, right click one of the properties and select "Source\Generate Getters and Setters" from the drop down menu. This will generate all of our getters and setters for us. You can also define a code template to help us stick to any company standards that may be present. I generally try to stick with the IBM standard and format my getters like:

public String getFirstName() {
	if (this.firstName != null) {
		return this.firstName;
	}
	ValueBinding _vb = getValueBinding("firstName");
	if (_vb != null) {
		return (String) _vb.getValue(getFacesContext());
	} else {
		//Or return a default value of some sort, can be hard coded or
		// determined programmatically
		return null;
	}
}

public void setFirstName(String firstName) {
	this.firstName = firstName;
}

Next up for the component class is to define the constructor and override the "getFamily()" method like below. Take note of the "RENDERER_TYPE" and "COMPONENT_FAMILY" constants used in the constructor where we set the renderer type and then in the getFamily() method. This is VERY IMPORTANT as it tells the server via the faces-config and xsp-config which renderer class to use to render this component. If you forget this, your component will not render, or at least won't use your renderer class:

public CommonContactInfo(){
	setRendererType(RENDERER_TYPE);
}

@Override
public getFamily() {
	return COMPONENT_FAMILY;
}

So, we should end up with this:

package com.keithstric.components;

import javax.faces.component.UIComponent;
import javax.faces.el.ValueBinding;

public class CommonContactInfo extends UIComponent {

	private static final String RENDERER_TYPE = "com.keithstric.CommonContactInfo";
	private static final String COMPONENT_FAMILY = "com.keithstric";

	private Boolean showFirstName;
	private Boolean showLastName;
	private Boolean showEmailAddress;
	private Boolean showPhoneNumber;
	private String firstName;
	private String lastName;
	private String emailAddress;
	private String phoneNumber;
	
	public CommonContactInfo(){
		setRendererType(RENDERER_TYPE);
	}

	@Override
	public getFamily() {
		return COMPONENT_FAMILY;
	}

	public Boolean isShowFirstName() {
		if (showFirstName != null) {
			return showFirstName;
		}
		ValueBinding _vb = getValueBinding("showFirstName");
		if (_vb != null) {
			return (Boolean) _vb.getValue("showFirstName");
		} else {
			return null;
		}
	}

	public void setShowFirstName(Boolean showFirstName) {
		this.showFirstName = showFirstName;
	}

	public Boolean isShowLastName() {
		if (showLastName != null) {
			return showLastName;
		}
		ValueBinding _vb = getValueBinding("showLastName");
		if (_vb != null) {
			return (Boolean) _vb.getValue("showLastName");
		} else {
			return null;
		}
	}

	public void setShowLastName(Boolean showLastName) {
		this.showLastName = showLastName;
	}

	public Boolean isShowEmailAddress() {
		if (showEmailAddress != null) {
			return showEmailAddress;
		}
		ValueBinding _vb = getValueBinding("showEmailAddress");
		if (_vb != null) {
			return (Boolean) _vb.getValue("showEmailAddress");
		} else {
			return null;
		}
	}

	public void setShowEmailAddress(Boolean showEmailAddress) {
		this.showEmailAddress = showEmailAddress;
	}

	public Boolean isShowPhoneNumber() {
		if (showPhoneNumber != null) {
			return showPhoneNumber;
		}
		ValueBinding _vb = getValueBinding("showPhoneNumber");
		if (_vb != null) {
			return (Boolean) _vb.getValue("showPhoneNumber");
		} else {
			return null;
		}
	}

	public void setShowPhoneNumber(Boolean showPhoneNumber) {
		this.showPhoneNumber = showPhoneNumber;
	}

	public String getFirstName() {
		if (firstName != null) {
			return firstName;
		}
		ValueBinding _vb = getValueBinding("firstName");
		if (_vb != null) {
			return (String) _vb.getValue("firstName");
		} else {
			return null;
		}
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		if (lastName != null) {
			return lastName;
		}
		ValueBinding _vb = getValueBinding("lastName");
		if (_vb != null) {
			return (String) _vb.getValue("lastName");
		} else {
			return null;
		}
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public String getEmailAddress() {
		if (emailAddress != null) {
			return emailAddress;
		}
		ValueBinding _vb = getValueBinding("emailAddress");
		if (_vb != null) {
			return (String) _vb.getValue("emailAddress");
		} else {
			return null;
		}
	}

	public void setEmailAddress(String emailAddress) {
		this.emailAddress = emailAddress;
	}

	public String getPhoneNumber() {
		if (phoneNumber != null) {
			return phoneNumber;
		}
		ValueBinding _vb = getValueBinding("phoneNumber");
		if (_vb != null) {
			return (String) _vb.getValue("phoneNumber");
		} else {
			return null;
		}
	}

	public void setPhoneNumber(String phoneNumber) {
		this.phoneNumber = phoneNumber;
	}	
	
}

You now have a component definition class with the preliminary structure defined and something to work with. In the next installment we'll create a renderer that will define how the component is represented in the browser. We'll also be coming back to this class to add the Save & Restore State methods with a short discussion around those methods which make the serialization of the component possible.

While just cruising around the web wasting time I stumbled across a grid component on sitepen's site for an alternative to the dojo grid. To me it looks very similar to a jquery grid. But I thought this was very interesting and may play with it at some point in time.

Today while working on a solution for this post on the XPages Forum I couldn't find any API documentation on a DynamicViewColumn which is a child of the Dynamic View Panel included in the extension library. This prompted me to come up with this crude custom control that will give you a drop down box of the component tree's components and then when you make a selection it'll spit out the java class name and all of it's methods with each method's argument types. This should prove to be a very handy custom control when you can't find a property/method of a component. I'm sure I'll think of more to add to this component, but in the mean time, here it is for your enjoyment.


	
		
			
				
			
			
			
		
		
			
				
			
		
		
		
			
				
					
					
				
					
					
			
			
				
					
					
					
					
				
					
					" + method.getName() + "("
			var params = [];
			params = method.getParameterTypes();			
			for (var j = 0;j < params.length;j++) {
				delim = "";
				if (j > 0 && j != (params.length -1)) {
					delim = ", ";
				}
				var methodParam:java.lang.Class = params[j];
				html = html + methodParam.getSimpleName() + delim;
			}
			html = html + ")
";
		}
	}
}
return html;}]]>
						
		
	


Read More...

We've added 2 new family members to the Strickland household (they don't have names yet). They should make fine additions to the family. We used to have a 6 1/2 foot iguana named Puff that pretty much had run of the house. He was certainly cool. But we resurrected my 75 gallon tank to the new bearded dragons. Since they're babies hopefully we can keep the tank hot enough, but we will one way or the other. But here they are.

Orange Bearded Dragon

Green Bearded Dragon

Today I released version 0.7.2a of XBlog. This version includes updates to the Sencha Touch site and the license for using Sencha Touch. New features included are:

  • Bottom Toolbar
    • Home Link
    • Full Archive Link
    • Downloads Link
    • Top 10 Link
  • Sencha Back Button improvements
  • Scrolling issue in Sencha Touch site fixed
  • License Update using the Sencha Touch Exception for Applications

As an early christmas present my wife got me a new Samsung Galaxy S II from T-Mobile. This is by far the best gadget I've ever owned. My initial impression is WOW! It's very fast, much faster than my old myTouch or the LG that replaced the myTouch when it started over-heating.

The T-Mobile version of the Galaxy S II is a little different than the stock Galaxy S II. It sports a 1.5 GHZ Dual Core Processor (as opposed to a 1.2 GHZ Dual Core), a huge 4.5" Super AMOLED screen (as opposed to a 4.3" screen) with Gorilla Glass and a very slim, light case (it's lighter than my LG which is smaller) and a 1850mah battery (as opposed to 1650mah). The battery life so far is ok, not great it was useful for about 8 hours, which I guess is about the norm for android phones, at least the ones I've owned. It also sports an 8 MP camera on the back with an led flash and a 2 MP front facing camera.

The UI is great, it's using the Samsung TouchWiz 4.0 launcher. It's a very smooth UI and totally customizable. It comes with the Swype keyboard and a samsung keyboard which is very close to the Android Ice Cream Sandwich keyboard. It's running Android 2.3.5 but will hopefully be updated to Android 4.0 (Ice Cream Sandwich). 

So hopefully I'll get a lot of use out of this phone and it'll last the whole two years that we re-signed the contract for. We've been very happy with T-Mobile and have no plans of switching carriers as you can't find better customer service from a cell phone company. But if you're in the market for a new phone, take a look at the Samsung Galaxy S2, you won't be disappointed.

I've recently incorporated a VERY simple Sencha Touch application into XBlog, if you visit this blog with a mobile device you will see it in action. The reason for this was because when attempting to read one of my favorite blogs on my Android phone I got really frustrated that it was so small and having to scroll up, down, left and right just to read a short post. This prompted me to look at my blog on my Android and it was even worse! So, we now need a mobile front end for XBlog.

Now for a normal Sencha Touch application seems everyone places their javascript files in a directory called app and within that app.js. The Sencha touch framework is placed in a lib directory and then an index.html file. This actually poses a couple of problems for an XPage application. First, how do you get a visitor to the index.html file when they enter the application with a mobile device? Sure, we could redirect them, but that's not cool and some browsers complain about being redirected. Next issue is if we run this from a javascript file that is not running within the context of an XPages application, how do we get data from our application?

To address both of these issues, there is a component that is not included in the component palette (it may be there it's just I don't know the name of it) which is an <xp:scriptBlock> component. This component allows you to place a client side javascript script on your xpage. This actually fits the bill for the issue of our client side javascript running outside the context of an XPage application by placing our application within the context of an XPage application. And since it's on an XPage, no need to redirect to index.html. All thats left to getting everything working is to include all the other needed javascript and css files as resources to the page. As a bonus we can set the "loaded" property accordingly to only be loaded when someone visits the site with a mobile device for our script block and the included resource definitions.

So let's take a look at the setup here. I've added the sencha framework in WebContent\sencha\lib\ and the only javascript file that I placed in this directory structure is within WebContent\sencha\app\models\ you can also find WebContent\sencha\app\app.js and WebContent\sencha\index.html however these files are basically a formality to traditional sencha touch apps. I've also added a script blog component to a custom control which contains our sencha touch application. I want to veer off course for a second at this point and comment about Sencha Touch in general. As an application framework, the use of Ext makes it very straight forward to program with. Everything is an object and using basic javascript skills it's easy to implement a sencha touch application. The Mobile UI for XBlog only took a few hours to implement. It was getting data and learning how models, stores and proxies work in relationship to sencha touch that took the most time. Once I learned the basic premise of the data store model it was just a matter of creating my own REST service and pointing my data store to that REST service.

Let's take a look at the javascript which is pretty much the entire UI of the XBlog Mobile site:

Ext.regApplication({
	name: "XBlogMobile",
	defaultTarget: "viewport",
	models: ['BlogListModel'],
	stores: ['BlogListStore'],
	launch: function() {
		//This is the data store with proxy to the REST service
		Ext.StoreMgr.register(new Ext.data.Store({
			storeId: 'BlogListStore',
			model: 'BlogListModel',
			autoload: true,
			proxy: {
				type: 'rest',
				url: Ext.getHead().dom.baseURI + "/default.xsp?" + xblogMobileRest.extraArgs + "&type=BLOG",
				format: 'json'
			}
		}));
	
		//This is the title bar at the top of the viewport
		this.listTitleBar = new Ext.Toolbar({
			title: "#{javascript:XBlogUtils.getConfigValue('BlogName',0).toString();}",
			items: [{
				id: "SenchaBackButton",
				text: 'back',
				ui: 'back',
				handler: function() {			
					XBlogMobile.viewPort.setActiveItem('blogList', {type: 'slide', direction: 'right'});
					var backButton = Ext.getCmp("SenchaBackButton");
					if (backButton) {
						backButton.hide();
					}
				}				
			}]
		});
		var backButton = Ext.getCmp("SenchaBackButton");
		if (backButton) {
			backButton.hide();
		}
		
		//This is the panel that we navigate to when someone clicks an entry in the blog list
		this.postPanel = new Ext.Panel({
			id: "postPanel",
			scroll: 'vertical',
			tpl: "<div class='MobileBlogTitle'>{title}</div><div class='MobileBlogDate'>{createdDate}</div><div class='MobileBlogEntry'>{content}</div>"
		});
		
		//This is the list of blog posts
		this.listPanel = new Ext.List({
			id: 'blogList',
			store: 'BlogListStore',
			scroll: 'vertical',
			itemTpl: "<div class='MobileBlogListItem'><span class='MobileBlogListTitle'>{title}</span><br><span class='MobileBlogListDate'>{createdDate}</span></div>",
			onItemDisclosure: function(record, btn, index) {
				var backButton = Ext.getCmp("SenchaBackButton");
				if (backButton) {
					backButton.show();
				}
				XBlogMobile.postPanel.update(record.data);
				XBlogMobile.viewPort.setActiveItem("postPanel");
			}
		});
		
		//This is our actual viewport panel that holds everything
		this.viewPort = new Ext.Panel({
			fullscreen: true,
			scroll: false,
			layout: 'card',
			cardSwitchAnimation: 'slide',
			dockedItems: [XBlogMobile.listTitleBar],
			items: [XBlogMobile.listPanel, XBlogMobile.postPanel]
		});
			
		Ext.getStore("BlogListStore").load();
	}
});

For the data store I included it here so that I could use EL to make SSJS calls to get some of the more pertinent information easily. Normally the data store would be it's own file. So, this covers almost everything except the data model and REST service. The data model just defines the structure of our data and is a very simple file:

 

Ext.regModel('BlogListModel', {
	fields: [
	         {name: 'title', type:'string'},
	         {name: 'createdDate', type: 'string'},
	         {name: 'content', type: 'string'},
	         {name: 'unid', type: 'string'}
	]
});

I know that all this isn't that complex but I think that's the beauty of Sencha Touch, it makes for very clean, simple code. Plus it's a fun framework to mess with. Now, if you'll notice in the top most code snippet there is a data store which includes a proxy item. The proxy is what tells the store the url to call to get it's data. In this case that url is the url or our REST service along with what we're looking for (i.e. BLOG, FULLARCHIVE or ARCHIVE note only BLOG is currently implemented). I'm sure this will all change in the future to support more data being sent to the browser, but for now simple is good. I would like to make the way data is retrieved to be more lazy and provide some paging in order to navigate all of the blog posts instead of the top 10.

So hopefully this will prompt you to take a look at Sencha Touch for your applications. To me it's a very elegant framework that isn't too difficult to use. The biggest hurdle is learning it, but once you take the plunge it's not very difficult, the hardest part is getting data. As for issues I encountered.... well the only real issue was scrolling (which sadly wasn't fixed in the 0.7.1a release) but the fix was just adding a couple of missing properties to the appropriate sencha components.

While developing XBlog one of the requirements was that the layout of posts, comments, permalink pages, pages, archives and downloads needed to be customizable by the blog owner. This would allow the blog owner to make XBlog look exactly as (s)he imagined. The problem is, how do you pull this off and yet still have the application perform as one would expect without a massive amount of requests going back and forth between the browser and server? While this task seems a little daunting it is doable and performs pretty well. Also, I need to credit Declan Lynch and BlogSphere with this idea, so it's not something new.

To start with, you need to come up with a way to plug in the proper values (i.e. title, date, author, etc.) in the location that the blog owner desires. The way I accomplished this was to define some "Hot Tags". These tags look something like "<$Title$>". This hot tag is stored in a document that says the <$Title$> tag should be replaced with the value of the title field from the post form. You can also say this is custom HTML and replace the <$Title$> tag with the custom HTML. We'll come back to the hot tags in a moment.

Now that we've got a way to place actual values into our HTML we need to come up with an HTML "template" to use for each type of component and be able to determine what the state of the page is that's being viewed so we know which template to use. Let's look at a blog entry template that is used on this site for each blog entry in the list of blog entries:

<div class="entry">
	<div class="header">
		<h2 class="entryHeader">
			<a href="<$Permalink$>"><$Title$></a>
		</h2>
	</div>
	<div class="authorHeader">
		<span class="entryAuthor authorHeaderItem"><$Author$></span>
		<span class="dateContainer authorHeaderItem"><$CreatedDate$></span>
		<span class="tagsContainer authorHeaderItem">
			<$TransCategories$><$Tags$>
		</span>	
		<span class="viewedContainer authorHeaderItem">
			<$ViewNumber$> Views	
		</span>
		<span class="numCommentsContainer authorHeaderItem">
			<a href="<$FullPermalink$>#comments" title="<$Title$>"><$NumberComments$> <$TransComments$></a>
		</span>
	</div>	
	<div class="entryBody"><$Content$>	</div>
	<div class="entryFooter">		
		<div class="addthis_toolbox addthis_default_style" addthis:url="<$FullPermalink$>">
			<$SocialToolbar$>
		</div>
		<div class="footerItem attachmentItem"><$Attachments$></div>
		<div class="footerItem readMoreText">
			<a href="<$Permalink$>"><$TransReadMore$></a>
		</div>
	</div>
</div>

Using a template like this allows you to say that every post in a blog entry list will have this structure. This structure is stored in the applicationScope so we don't have to keep making a request to the document to determine what the default html for a blog entry will be. Since it's only stored once, you also don't have to worry about scalability because this is being stored for every post for every user (guess how I know this is an issue). It's stored once and used by everything and everyone. We're able to do this because once you determine this basic template it's probably not going to change very much and it's not specific to the current user, but to all users.

We've now got our default html and a way to determine what values should be placed where. We now need to setup our values in order to replace the pertinent tags with the proper value. We do this by putting a repeat control on the page that is bound to the view which contains our blog entries. This repeat only has a "Post" component within it that get it's property values from the fields on the document. So it's just a matter of getting the values from the document being processed by the repeat.

Since we've got our default html, a way to determine what value should be placed where and we've got all of our data we can now start replacing the hot tags with the value from the "Post" component. To accomplish this we do a lookup to our hot tags view and get all the hot tags for a "Post". We then follow the steps below:

  • We create a hash map (a hash map is just a container of key/value pairs) that contains the tag as the key and the value is the value from the Post component
  • We loop through the hash map and just do a replace of the hash map key (tag) with the value for that key within the default html
  • We then clean up any left over tags that may be present
  • This html is what is sent to the browser
Keep in mind that this all happens in the rendering portion of the component as that takes away having to keep track of the document we're working with. Also handling this during the rendering of the component makes it easy to keep up with what you're working with and reduces the if/if else/else statements if you try to handle every type of content within the same method or class.

Here's the short snippet of code that does our replacement:

@Override
public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
	super.encodeBegin(context, component);
	Post post = (Post) component;
	postHtml = post.getDefaultHtml();
	hotTags = getHotTagMap(post);
	for (String replaceTag : hotTags.keySet()) {
		if (hotTags.get(replaceTag) != null) {
			postHtml = postHtml.replace(replaceTag, hotTags.get(replaceTag).toString());
		}
	}
}

And that's how you setup an html template that is totally customizable, scalable, easily modified and maintainable. Of course there are some special considerations to take into account like for attachments there may be many that we need to account for, etc but the concept is still the same. Since you're already reading this you've seen a demo of it in action as this is how all the content in XBlog is determined. So hopefully you can get some mileage out of this concept in your own application.

XBlog 0.7a released to OpenNTF. This version includes several new features:

  • Posts, Comments, Pages and Archive items all componentized which resulted in a slight performance gain
  • Search incorporated in both the front-end and back-end websites
  • Full Archive now available
  • Small Client Side API implemented
  • All Downloads section added

Items fixed in this release:

  • Sorting arrows for Links
  • Hot Text replacement only occurs in content of a post, not all html, fixes breakage of html due to text replacement
  • Emoticon replacement only occurs in content of a post and comment, not all html, fixes breakage of html due to text replacement
  • Reset of scoped variables now happens during save operation for pertinent pages
  • Java Security issue corrected (403 error)
  • Number of RSS Items to show fixed
  • Changed RSS from Atom to RSS 2.0 for inclusion in PlanetLotus aggregator
  • Fixed many RSS issues

This release is what the first release should've been. It should be a very stable release. All of these new features can be seen right here on keithstric.com

So, I've been cleaning up a lot of my "first starting" mess here in XBlog. The Posts, Comments and Pages are now "componentized" as opposed to being just generated HTML. Don't get me wrong there is still a lot of "generated" HTML here, but it's at least getting to be more manageable. But during this clean up I decided to implement a client side API of sorts and the best way to do that is to create a JavaScript Singleton to hold all the CSJS functions and properties as that would provide the most robust solution to provide other people the ability to expand XBlog.

What is a singleton? Well here's a rather broad definition from Wikipedia

In software engineering, the singleton pattern is a design pattern used to implement the mathematical concept of a singleton, by restricting the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system. The concept is sometimes generalized to systems that operate more efficiently when only one object exists, or that restrict the instantiation to a certain number of objects.

I don't know about you but after reading that I really don't know any more than what I started with. But in general terms a singleton is a Javascript class. It's also a global object of methods and properties. A good example of a singleton is to take a look in Firebug, under the DOM tab and you should see the singleton/names space for dojo, dijit, XSP, etc.
FireBugSingletons.jpg

The pattern for a singleton is very straight forward and rather ingenious if you ask me.

/*
 * This is an example of a singleton
 */
var MyAPI = function() {
	var outside = "I'm running, jumping and playing outside";

	// This is object will be our public API
	var myapi = {
		someProperty: "Hello World!",
		someMethod: function() {
			alert(this.someProperty + " Go do something outside, " + outside);
		}
	}
	return myapi;
}();

I admit that's not a very complex example but should show the overall theory quite well. We've got a variable (MyAPI) that's actually a function that runs because of the "()" after the closing }. When this function runs it builds our "myapi" object and returns it which then becomes our public API with a name of "MyAPI" which is accessible from CSJS. Also notice we've got a variable (outside) which is above our myapi object. This variable will still be accessible from myapi via code within myapi but not from code outside of myapi, so you couldn't build a script in an xpage to modify the "outside" variable.

Now that we've got our client side API and we add the Script Library this is in as a resource to an xpage, or better yet a resource available to the entire application via the theme. You can now get to all the methods and procedures in this object from CSJS anywhere. The syntax would be:
MyAPI.someMethod();

So to see our API in action let's take a look at firebug and we should now see our MyAPI namespace represented in the DOM tab.
Firebug-MyAPI.png

And if you put some code (MyAPI.someMethod();) in the Javascript console of firebug and run it against MyAPI it should function just as we would expect it to by throwing up an alert.
HelloWorld-MyAPI.png

This makes it possible to include all your client side logic within a name space of your preference, say the application name or internal abbreviation. Also, it's more of a class like structure with all the benefits of using classes and it makes it much easier to manage your API and easier to troubleshoot issues when they arise. Talking of issues, you will probably want to put some error handling in place to spit out meaningful error messages.

So I hope this shows how easy it is to create your own client side API and the benefits of doing so. If you want to see this live you can see the demo here.

Page12345678...

Disclaimer

The opinions and ideas posted on keithstric.com are not necessarily the opinions and ideas of my employer. The solutions, techniques and code provided here are not guaranteed or warranted in any way and are free for you to use at your own risk.

Copyright 2002-2011 Keith Strickland
Powered by XBlog