Skip to main content

Custom component with AjaxBehavior

10 replies [Last post]
denbo
Offline
Joined: 2008-03-21
Points: 0

I'm looking for a (simple/self-contained) example for writing a custom component (not a composite component) using AjaxBehavior. I checked the Mojarra sources and the web but I couldn't find the solution to my problem.

The AJAX event is sent to the server and everything looks good but the listener method specified in the tag is never called. When I try the same with a tag it works perfectly.

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
driscoll
Offline
Joined: 2003-06-06
Points: 0

Hopefully you can see this, as the forum seems a bit broken right now.

You have several errors in your code, some trivial (using getId instead of getClientId, as well as leaving out the name attribute), and some really serious (missing decode section). Not surprising, since, as you note, there isn't an example to follow.

Here's some working code - I'll blog this up later. (Also, note that what you want to do in *this* example is probably better done in a composite component.)

@FacesComponent(value = "mycustom")
public class MyCustom extends UIComponentBase implements ClientBehaviorHolder {

@Override
public String getFamily() {
return "custom";
}

@Override
public void encodeEnd(FacesContext context) throws IOException {

ClientBehaviorContext behaviorContext =
ClientBehaviorContext.createClientBehaviorContext(context,
this, "click", getClientId(context), null);

ResponseWriter responseWriter = context.getResponseWriter();
responseWriter.startElement("div", null);
responseWriter.writeAttribute("id",getClientId(context),"id");
responseWriter.writeAttribute("name", getClientId(context),"clientId");
Map> behaviors = getClientBehaviors();
if (behaviors.containsKey("click") ) {
String click = behaviors.get("click").get(0).getScript(behaviorContext);
responseWriter.writeAttribute("onclick", click, null);
}
responseWriter.write("Yo! Click me already!");
responseWriter.endElement("div");
}

@Override
public void decode(FacesContext context) {
Map> behaviors = getClientBehaviors();
if (behaviors.isEmpty()) {
return;
}

ExternalContext external = context.getExternalContext();
Map params = external.getRequestParameterMap();
String behaviorEvent = params.get("javax.faces.behavior.event");

if (behaviorEvent != null) {
List behaviorsForEvent = behaviors.get(behaviorEvent);

if (behaviors.size() > 0) {
String behaviorSource = params.get("javax.faces.source");
String clientId = getClientId(context);
if (null != behaviorSource && behaviorSource.equals(clientId)) {
for (ClientBehavior behavior: behaviorsForEvent) {
behavior.decode(context, this);
}
}
}
}
}

@Override
public Collection getEventNames() {
return Arrays.asList("click");
}

@Override
public String getDefaultEventName() {
return "click";
}
}

Since "click" is now the default, you can just do something like:



denbo
Offline
Joined: 2008-03-21
Points: 0

Thanks, it works perfectly.
Looking forward to your blog entry!

driscoll
Offline
Joined: 2003-06-06
Points: 0

Blog entry is finally up, now that the bugs have stopped flowing in so quickly on the forum...

http://weblogs.java.net/blog/driscoll/archive/2009/10/09/jsf-2-custom-ja...

driscoll
Offline
Joined: 2003-06-06
Points: 0

As far as I know, no such example exists (yet).

I thought about writing one for you, but I realized looking over your question that I didn't really understand what you were doing - I'd hate to do all that work and not answer your question.

So: what, exactly, are you writing? A custom AjaxBehavior? A listener for f:ajax? Both?

denbo
Offline
Joined: 2008-03-21
Points: 0

Basically, I wanted to see how I can write my own ajaxified component using the new AjaxBehavior mechanism.

The following is based on guesses and try-and-error, but at least the AJAX request is sent to the server. However, the event is not passed to the method specified in the "listener" attribute of the f:ajax tag.

So far, I have:
----- begin component
@FacesComponent (value="CustomAjaxComponent")
public class CustomAjaxComponent extends UIComponentBase implements ClientBehaviorHolder {

@Override
public String getFamily() {
return null;
}

@Override
public void encodeEnd(FacesContext context) throws IOException {

ClientBehaviorContext behaviorContext = ClientBehaviorContext.createClientBehaviorContext(context, this, "click", getId(), null);

ResponseWriter responseWriter = context.getResponseWriter();
responseWriter.startElement("div", null);
responseWriter.writeAttribute("id", getId(), null);
responseWriter.writeAttribute("onclick", getClientBehaviors().get("click").get(0).getScript(behaviorContext), null);
responseWriter.write("click me!");
responseWriter.endElement("div");
}

@Override
public Collection getEventNames() {
return Arrays.asList("click");
}

}
----- end component

and an XHTML page where I use it:
----- begin XHTML page
...



...
----- end XHTML page

driscoll
Offline
Joined: 2003-06-06
Points: 0

Thanks, that's very helpful.

Just to check (I'm not free to try this right this second): If you leave off the listener, and just exec @form render @form, that works, right?

Also, you didn't add anything into the faces-config.xml, did you?

denbo
Offline
Joined: 2008-03-21
Points: 0

Exactly, the form is re-rendered correctly, no matter if I have the listener attribute or not.
And I don't even have a faces-config.xml, thanks to you heroes ;)

driscoll
Offline
Joined: 2003-06-06
Points: 0

OK, I'll either look at this tonight or tomorrow - it's entirely possible that you've uncovered a bug. I'll let you know.

driscoll
Offline
Joined: 2003-06-06
Points: 0

It turns out that I haven't had a chance to get to this yet - I've had other issues to deal with (including a failing demo for a talk I was giving) that took up a good deal of my time.

I'll now try to get back to you on Monday. Sorry for making you wait.

denbo
Offline
Joined: 2008-03-21
Points: 0

No problem at all, Jim!