Showing posts with label JSF Custom Components. Show all posts
Showing posts with label JSF Custom Components. Show all posts

Monday, October 19, 2009

Oracle Open World Aftermath

I definitely owe a blog entry at this point!

Just came back from Open World, and boy: is my brain tired!! (ba-dum-CRASH)

Anyway. I had a request that I make some specific entries on my blog from someone in the audience that came up to visit after my presentation.

He wanted to know how to do two things that I mentioned:
1. how to use the phase listener to get resource files in a custom component, and
2. how to use a renderer to divide up functionality.

These are somewhat meaty topics but I will try to be concise.

I. PhaseListener "magic"
If you have Schalk and Burns's
JavaServer Faces: the complete reference then just (re)read Chapter 11.

If you do not...here comes the paraphrase. The general idea is that you need to add a phase listener to your application in the normal manner: by adding a few lines to your faces-config.xml file. Note that if you have a custom component in JSF, then your jar has in it in its META-INF directory a faces-config.xml file, where you can reference this phase listener. JSF guarentees that this will be read first before the one in your application, I believe, provided this jar is in your WEB-INF/lib directory of your application.

Anyway you take your added phase listener, and make it listen for the RESTORE_VIEW phase by overriding the getPhaseId() method, and returning PhaseId.RESTORE_VIEW constant.

So let us say for example that your component has a need to have javascript code, that you want to keep in a sepparate file...because there is too much code to render on the html page itself (or you are a "tidy" individual). So your component will encode/render a javascript "include" (i.e.,<script src="somename" type="text/JavaScript"></script>

). So you encode this line onto the page. Then when this page is "interpreted", and issued as a request, your phaseListener can be ready. Your phaseListener has knowledge that at some point after the RESTORE_VIEW phase is complete, the FacesContext.getViewRoot().getViewId() will contain "somename". At that point, the phase listener can use getClass().getResource("somename's filename"), to get a url. This url can be connected-to and read from, an inputStream, then an inputStreamReader, then a BufferedReader. Meanwhile you open the outputWriter from the response on the FacesContext's ExternalContext, create an outputStreamWriter, then do a while loop reading lines from the buffered reader and writing lines to the outputStreamWriter, until there are no more lines to write. By the way the FacesContext is available from the PhaseEvent that your receive as a parameter. And with this FacesContext, you finish with a call to its responseComplete() method.

II. Renderer.

This is easier. If you have a custom component that has no renderer you can move your encode*() methods and decode() method to the a custom Renderer class. The main changes you will need to make are because you now no longer say "this" when you need to reference your component. In your new renderer class (which BTW extends "Renderer") the encode*() and decode methods receive your component as a parameter, so you much change your "this" references to use this parameter.

You must also augment the faces-config.xml, to have a element with a sub-element which shows the family, type, and class of your renderer. Type can be anything, but whatever it is must now also be returned by getRendererType() overridden method in your tag handler class.

Also you will need to guard against receiving a null FacesContext or a null Component value for your parameters. This is in the spec so don't shoot me; I'm just the messenger.

Sir, I hope, whoever you said you were, that this blog entry, approaches meeting your custom-component writing needs or curiosities, or whatever drove your question. I bid you: good day!

I bid the same for the rest of you as well.

Wednesday, August 19, 2009

speaking at NoVAJUG

If you live/work in Virginia, I will be be giving a preliminary viewing of my Oracle Open World presentation on JSF Custom Component basics.

Here is more info on the matter: http://novajug.wordpress.com/2009/08/19/sept-9-lets-make-some-jsf-custom-components-by-michael-a-fons/

Also here is the NoVAJUG website: http://novajug.org/

Tuesday, July 14, 2009

Custom Components in JSF and af:iterator

I have something to report that I did not realize about custom UI components in JSF (lots of things probably...)

Consider the following scenario: you put a custom component in a af:iterator component. Can you answer the following questions?
  1. How many instances of your custom component that you included in that af:iterator loop will be created?
  2. If your component's function is to print a different kind of input field or widget depending on the parametric input given to the component tag, what might you need to do with any attributes in your component?

The answer to 1 is: one. One for each af:iterator. So if your iterator loop brings back 1000 records from the database, it loops 1000 times and "prints" your component 1000 times, but there is ever only one instance of the component.

For question 2...let us say that your component offers the printing of 4 different kinds of components depending on the value of its "type" tag attribute. So in that page you will have 1000 fields printed one for each af:iterator loop. If your component only rendered UIOutput components then it would suffice to have one property in your component class for each type of component.

But we are doing input components, so decode will be involved. That complicates things. JSF will generate a client id for each field that you generate with your component. If it is in the af:iterator loop it will look something like: formname:iteratorname:n:yourcomponentid -- where n is the zero-based loop index of your af:iterator.

On a JSF postback/decode, each field first runs the decode method. Then each field runs the encode methods for the component. In between the decode runs and the encode runs you need to store the submitted value of each component. I used a HashMap with client id as the key, and the component as the value. If you store everything away in decode you will have whatever you need to do the encode when it occurs for the postback.

Hope this helps someone.

Wednesday, July 8, 2009

af:iterator

I was trying to use af:forEach with a custom component I made. This custom component is a bit like the declarative components in ADF Faces Rich Client components, in that it renders different components depending on what type you send it. I was trying to pass a value binding through an attributes and then trying to evaluate the value of this in the component at encode time, but the af:forEach request scope "var" variable could not be evaluated by the property resolver. af:iterator, I am happy to say, for some reason did something different however so the "var" was more accessable to the child custom component.

There was another project also where af:forEach really was acting up, where I wish I had known about af:iterator.

The inspiration to try this component came from Duncan Mills blog entry http://groundside.com/blog/DuncanMills.php?blog=6&c=1&page=1&more=1&title=jsf_and_the_foreach_loop&tb=1&pb=1&disp=single

Wednesday, June 17, 2009

Oracle Open World 2009 Campaign

All,
Thank you so much for your votes on my Oracle Open World presentation proposals. Many heavy hitters in the ADF community have voted for this presentation. It is on JSF Custom Components: Frank Nimphius, Grant Ronald, Peter Moskovits, and the list goes on and on.
I have noticed that Oracle Mix has been a bit flaky at time for the links that I distributed. If you wanted to vote but could not see the links, please try the proposal page; you should have better luck. At this time I am on the second page of Top proposals:
https://mix.oracle.com/oow/proposals/
I am very excited to be involved with this campaign. :-)
Michael Fons


Hi. I want to speak at Oracle Open World 2009. My paper is on creating JSF Custom Components.

Please read my abstract and vote for my paper at
https://mix.oracle.com/oow/proposals/10362

Note: in order for your vote to count, you will need to ...
1. Have/log into an Oracle Mix account (follow the appropriate links if you don't already have an Oracle Mix account; good site to check out, anyway)
2. Vote for at least 3 papers.

To help with 2. above, please also vote for a friend of mine...
https://mix.oracle.com/oow/proposals/10432

And then...your vote #3 can be random or pointed...

Thanks for your time.