I have 2 elements, banana and an outputText, where banana is a custom JSF component and in banana renderer, I would like to generate HTML enclosing the specified element.
xhtml:
<h:outputText id="theApple" value="This is an Apple" />
.
.
.
<my:banana for="theApple" link="http://www.banana.com" />
bananaRenderer(encloses the target element in anchor links):
@Override
public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
ResponseWriter responseWriter = context.getResponseWriter();
Banana banana = Banana.class.cast(component);
responseWriter.startElement("a", null);
responseWriter.writeAttribute("id", banana.getClientId(context), null);
responseWriter.writeAttribute("href", banana.getLink(), null);
}
@Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
ResponseWriter responseWriter = context.getResponseWriter();
Banana banana = Banana.class.cast(component);
responseWriter.endElement("a");
}
What I want to achieve:
<a href="http://www.banana.com">This is an Apple</a>
.
.
.
As you can see I want to encode not on the banana component but on the element it targets using "for" attribute. The closest example I saw is PrimeFaces tooltip, but I can't quite grasp how it utilizes the "for" attribute. http://www.primefaces.org/showcase/ui/overlay/tooltip/tooltipOptions.xhtml#
If someone can point me to the right direction, it would really be appreciated. Thank you.
You can manipulate the component tree during PreRenderViewEvent
. During that moment, all components are guaranteed to be present in the tree, and it's guaranteed safe to manipulate the component tree by adding/moving/removing components in the tree. You can find other components in the tree by UIComponent#findComponent()
. You can traverse the component tree using UIComponent#getParent()
and UIComponent#getChildren()
. The getChildren()
returns a mutable list which is guaranteed safe to manipulate during PreRenderViewEvent
.
Given that you'd like to wrap a given component with a hyperlink, and there exist already a JSF hyperlink component, the <h:outputLink>
, it's easier to extend and reuse it to keep the code simple and DRY. Here's a basic kickoff example:
@FacesComponent(createTag=true)
public class Banana extends HtmlOutputLink implements SystemEventListener {
public Banana() {
getFacesContext().getViewRoot().subscribeToViewEvent(PreRenderViewEvent.class, this);
}
@Override
public boolean isListenerForSource(Object source) {
return source instanceof UIViewRoot;
}
@Override
public void processEvent(SystemEvent event) throws AbortProcessingException {
String forClientId = (String) getAttributes().get("for");
if (forClientId != null) {
UIComponent forComponent = findComponent(forClientId);
List<UIComponent> forComponentSiblings = forComponent.getParent().getChildren();
int originalPosition = forComponentSiblings.indexOf(forComponent);
forComponentSiblings.add(originalPosition, this);
getChildren().add(forComponent);
}
}
}
<!DOCTYPE html>
<html lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:my="http://xmlns.jcp.org/jsf/component"
>
<h:head>
<title>SO question 38197494</title>
</h:head>
<h:body>
<h:outputText id="theApple" value="This is an Apple" />
<my:banana for="theApple" value="http://www.banana.com" />
</h:body>
</html>
Do note that explicit children.remove()
call is not necessary when you already perform a children.add()
. It will automatically take care about the child's parent.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With