Skip to main content

JSF 2.0: Writing a Spinner component

Posted by driscoll on November 8, 2008 at 3:56 PM PST

In a previous posting, I described the basic structure of a JSF 2.0 composite component.


Here's a slightly more complex example of writing a component using JSF 2.0's Composite Component feature. The Spinner component takes only 30 lines to create the classic "entry field with buttons" spinner as a component you can use in any JSF page.


First a description of what a "Spinner" is, in case you've never heard that name before. Put simply, it's an input field, allowing you to type in (typically numeric) data, with a set of buttons to one side that let you increment that data with a button push. In our example, we'll have an input field with four buttons arranged horizontally to it's right. A better configuration would be to have the buttons vertical - but that will require a bit of css magic, so we'll leave that for another time. Today, we'll just discuss the basic logic of the component, we'll make it pretty in a future post.


So, without further introduction, here's the 30 lines you need to make a Spinner. I'll fill you in on what's happening in each line afterward.







    
    


    
    
    
    
    
    





We'll go through this line by line: Doctype and namespace declarations are unsurprising, just be sure to declare the "composite" namespace, as well as any other namespace that you'll need for the components that you use (we need the core html namespace "h", so we declare that too.


Next, we declare the interface of the component: in other words, the arguments you pass to it as attributes. We declare two: value, which will act just like the value in a an inputText tag, and increment, which will be an optional attribute that specifies how much the value changes when we press the buttons.


After that, we have the implementation section: This is the part that will be output into the using page, in a template-like fashion. For us, it's essentially in two parts - the JSF components, and a little bit of JavaScript that they invoke.


The components are just standard JSF components, used in standard ways - one thing to note is that the inputText has a value which is set to an EL expression value="#{cc.attrs.value}" . As in my previous example, this just resolves to whatever is passed to the component's tag's value attribute. Since this is an input text, that value expression needs to be writable, so we'd call this tag with code that looked like:




Where number is a bean that has both setNumber and getNumber methods.


The buttons are typical JSF buttons that call Javascript. Since the Javascript function they call returns false, they never submit anything to the server- they just change the value of the inputText field with Javascript.


The Javascript itself has a couple of bits that are specific to this being inside a component: The first is this line:


var increment = Number("#{cc.attrs.increment}");


As you can see, we got EL right in the Javascript code. This EL substitutes out at runtime into the value of the increment attribute for the spinner tag. We wrap it in quotes and cast it to a number just to be safe, then test it. If it's not a valid entry, we'll use a default value.


The next interesting line is:


var entry = document.getElementById("#{cc.clientId}"+":"+"number");


One of the trickier parts of using Javascript inside composite components is the fact that id values are dyamically generated at runtime - as they must be, since we may have this code more than once in the page. Dynamically created id's are the only way to make sure that the id's are unique in the page. So, we need to determine the id of any component we act on in a similar dynamic manner. The clientId will be set to the id of the component at runtime, and it will prepend all the id's that you assign subcomponents, with a separator character in between. In the current implementation, ":" will always be that separator character, but for FCS, we'll have to determine that dynamically too.


Lastly, here's how to use this component in a page (just as a reminder):


  1. Put a reference to the composite component in your page - xmlns:ez="http://java.sun.com/jsf/composite/spinner"
  2. Name your file as resources/spinner/spinner.xhtml
  3. Use a tag that looks like:

And that's all. Please feel free to ask questions... more examples to come.

Related Topics >>

Comments

If you have several spinners, then this will not work. Why? Because you're using the same function name, even though it's declared multiple times. I'm going to talk about how to program for multiple components on a page with javascript in my next blog post. I left that out because, frankly, it gets complicated - which isn't the fault of the component design process, but simply a side effect of how the web deals with data.

What if you have several spinners with different increment on the same page?

I'll cover those questions in future blog postings - I'm trying to keep the examples to bite -sized pieces. In the meantime, if you want to skip ahead, you can go to the Project Mojarra codebase, and look under jsf-demo/ezcomp00 for an example that does a *lot* of stuff. There's also basic-ezcomp and basic-ajax for the examples I've already covered.

Great work!! That's easy. 1. If custom component has to also do validation or wants to have some actionListner registered, will that be on same lines (like value and increment attributes)? OR somewhat different 2. What if we want to bundle js and images and css in seprate files and distribute, how will that be done> I think this all is going to get v easy now.

Hi Jim, I figured out the problem as I have to change all "#{compositeComponent}" with "#{cc} So it will be "#{cc.clientId}" instead of "#{compositeComponent.clientId} Thanks

Hi, I was trying to run the sample spinner application. I am getting "" for #{compositeComponent.clientId} Coz of this I am not able to run the application. I am using GlassFish v3 with JSF 2.0 BETA1. Could you please tell me why it is getting blank value? Is it a Bug? Regards -Prajeesh Nair