JSF Primefaces outputLabel get value is null - jsf

I have button on my view. When I click it there is an action handler where I'm getting view root from faces context and then I get form. Then I loop through facets and children of the form and I look for OutputLabel instances. When I get it I'm trying to getValue from it. But it's always null, but on rendered page there is value. OutputLabel (p:outputLabel) is placed in composite component...
What do I wrong? Some ideas?
// EDIT
public void buttonHandler(){
FacesContext facesContext = FacesContext.getCurrentInstance();
UIForm form = facesContext.getViewRoot().findComponent("formId");
List<String> list = new ArrayList<>();
collectLabels(form, list);
}
public void collectLabels(UIComponent component, List<String> list){
Iterator<UIComponent> iterator = component.getFacetsAndChildren();
while(iterator.hasNext()){
UIComponent child = iterator.next();
collectLabels(child, list);
}
if(component instanceof OutputLabel){
list.add(((OutputLabel)component).getValue());
}
}
I'm not at work now. So I don't remember my composite component code now. But it contains <p:outputLabel for="#{cc.attr.id}" value="#{cc.attr.labelValue}" />

Related

How to get a UIComponent by its component id in icefaces

I'm trying to access an icefaces component, exactly an Accordion so i can set its activeIndex from my bean. the problem is that the returned value is always null. this is my code.
public static UIComponent findComponentInRoot(String id) {
UIComponent component = null;
FacesContext facesContext = FacesContext.getCurrentInstance();
if (facesContext != null) {
UIComponent root = facesContext.getViewRoot();
component = findComponent(root, id);
}
return component;
}
public static UIComponent findComponent(UIComponent base, String id) {
if (id.equals(base.getId()))
return base;
UIComponent kid = null;
UIComponent result = null;
Iterator kids = base.getFacetsAndChildren();
while (kids.hasNext() && (result == null)) {
kid = (UIComponent) kids.next();
if (id.equals(kid.getId())) {
result = kid;
break;
}
result = findComponent(kid, id);
if (result != null) {
break;
}
}
return result;
}
and i call this method like this:
Accordion acco = (Accordion)findComponentInRoot("menuFormId:menu");
my page look like this or to say a part of it:
<h:form id="menuFormId">
<icecore:singleSubmit />
<ace:accordion id="menu" collapsible="true" autoHeight="false" >
<ace:accordionPane id="system" title="#{msgs.LABEL_ADMINISTRATION}"
rendered="#{navigationCtrl.functionList['GESUTAD'] or navigationCtrl.functionList['GESPROF'] or navigationCtrl.functionList['GESUTTOM'] or navigationCtrl.functionList['SYNCPRC']}">
<div class="divLinkStyle">
<ice:commandLink rendered="#{navigationCtrl.functionList['GESPROF']}" styleClass="linkMenu" action="#{navigationCtrl.redirectConsulterProfil}"
onmouseover="this.style.backgroundColor='#DEEDF8'" onmouseout="this.style.backgroundColor='#FFFFFF'">
<h:graphicImage value="../resources/images/util.png" />
<h:outputLabel value="#{msgs.LABEL_GESTION_PROFIL}" style="cursor: pointer;" />
</ice:commandLink>
</div>
...
Any ideas ?
my bean is session scoped.
i'm using icefaces 3.3.0 and jsf 2.2
You're confusing component ID with client ID. You're passing a client ID "menuFormId:menu" instead of component ID "menu" to your utility method, while the utility method actually finds the component by component ID instead of client ID.
Just use UIViewRoot#findComponent().
public static UIComponent findComponentInRoot(String id) {
return FacesContext.getCurrentInstance().getViewRoot().findComponent(id);
}
Unrelated to the concrete problem. You're making here a design mistake. The model should not be interested in the view. It should be the other way round. Set the activeIndex as a bean property and let the view hook on it the usual way.
<ace:accordion ... activeIndex="#{bean.activeIndex}">
In any case you're trying to grab/create/bind/manipulate/whatever a physical UIComponent instance in a backing bean class, you should absolutely stop coding and think twice if you're really doing things the right way. Ask if necessary at Stack Overflow if you can't figure out the right way.

Programatically add Parameter to HtmlCommandLink using a Phase Listener

I need to add a component (UIParameter) to a HtmlCommandLink component dinamically through a Phase Listener.
What I want to achieve is that every element <h:link outcome="out"> renders as <a href="out_url_parsed + ?param=paramvalue">.Where "param" is my component.
I've tried using this
private void addElement(final PhaseEvent event, final Class clazz, final UIComponent component) {
final FacesContext fcontext = event.getFacesContext();
UIViewRoot root = fcontext.getViewRoot();
if (root == null) {
return;
}
root.visitTree(new FullVisitContext(fcontext), new VisitCallback() {
#Override
public VisitResult visit(VisitContext context, UIComponent target) {
if (clazz.isInstance(target)) {
LOGGER.info("Element Found");
UIParameter parameter = new UIParameter();
parameter.setValue("willberonadom");
parameter.setId("sessiontoken");
target.getChildren().add(parameter);
}
return VisitResult.ACCEPT;
}
});
}
But it's not working. The element is actually found on the tree but the UIParameter does not render.
I've found that the UIViewRoot only has child elements after RENDER_RESPONSE phase. So i think this is why my added element is not rendered at the end of the process.
I'm sure I can add this param editing the views but I don't want to do that since it must be present on all h:link in the application and must be present on any other new added too. So I consider this as a better approach to avoid missing tags
On a similar case I've managed to add input hidden elements to every form on view with this code...
HtmlInputHidden hiddenToken = new HtmlInputHidden();
hiddenToken.setId("sessiontoken");
hiddenToken.setValue("willberandom");
hiddenToken.setRendered(true);
root.addComponentResource(event.getFacesContext(), hiddenToken,"form");
But it doesn't work on anchor tags
There are several mistakes:
You want to add a parameter to a HtmlCommandLink component which represents <h:commandLink>, but you're giving an example with <h:link>, which is represented by HtmlOutcomeTargetLink. What exactly do you want?
A PhaseListener on beforePhase() of RENDER_RESPONSE may be too late on GET requests which would only build the view for the first time during render response. At the moment your PhaseListener runs, the UIViewRoot would have no children at all. You'd better hook on view build time instead. For that, a SystemEventListener on PostAddToViewEvent is the best suitable.
You're setting the parameter name as an id instead of name. Use UIParameter#setName() instead of UIParameter#setId().
Provided that you actually meant to add them to <h:link> components, then here's a kickoff example how you can achieve that with a SystemEventListener.
public class YourSystemEventListener implements SystemEventListener {
#Override
public boolean isListenerForSource(Object source) {
return source instanceof HtmlOutcomeTargetLink;
}
#Override
public void processEvent(SystemEvent event) throws AbortProcessingException {
UIParameter parameter = new UIParameter();
parameter.setName("sessiontoken");
parameter.setValue("willberonadom");
((UIComponent) event.getSource()).getChildren().add(parameter);
}
}
(if you actually want to apply them on <h:commandLink> as well, just extend the isListenerForSource() check with a || source instanceof HtmlCommandLink)
In order to get it to run, register it as follows in faces-config.xml:
<application>
<system-event-listener>
<system-event-listener-class>com.example.YourSystemEventListener</system-event-listener-class>
<system-event-class>javax.faces.event.PostAddToViewEvent</system-event-class>
</system-event-listener>
</application>

jsf dynamic component that restores state

I am trying to display HtmlInputText dynamically in a JSF page. However, I am getting
javax.faces.FacesException: Cannot add the same component twice: j_idt10:hitDyn
During the first request to the page the input text renders well. That exception happens during postback of the page, when I enter some text in the input component and press Enter.
In the .xhtml page, I have the following code:
<h:form>
<h:outputLabel value="Welcome!"></h:outputLabel>
<f:metadata>
<f:event type="preRenderView" listener="#{dynamicBacking.addDynComp}" />
</f:metadata>
<h:panelGroup id="dynOuter"></h:panelGroup>
</h:form>
In the backing bean, I have the following code:
#ManagedBean(name="dynamicBacking")
public class DynamicBacking {
public void addDynComp() {
Application app = FacesContext.getCurrentInstance().getApplication();
HtmlInputText hit = (HtmlInputText)app.createComponent(HtmlInputText.COMPONENT_TYPE);
hit.setId("hitDyn");
UIComponent parent = findComponent("dynOuter");
if( parent != null ) {
parent.getChildren().add(hit);
}
}
public UIComponent findComponent(final String id) {
FacesContext context = FacesContext.getCurrentInstance();
UIViewRoot root = context.getViewRoot();
final UIComponent[] found = new UIComponent[1];
root.visitTree(new FullVisitContext(context), new VisitCallback() {
#Override
public VisitResult visit(VisitContext context, UIComponent component) {
if(component.getId().equals(id)){
found[0] = component;
return VisitResult.COMPLETE;
}
return VisitResult.ACCEPT;
}
});
return found[0];
}
}
I guess that there is some problem with restoring the state of the dynamic component in a postback. Am I adding the dynamic component too late in the lifecycle of the JSF page? I know that in ASP.NET I could add a dynamic control during Page.Load phase. But I can't so far figure out how to achieve the same in JSF. Please, help!
The exception appears because the component is added in the tree on the initial page load. When performing a postback your listener gets called again and it tries to add another component with the same id and this causes the exception. A solution of the issue is to check if the request is NOT a postback when adding the component. The following code shows how to check for postback:
if (FacesContext.getCurrentInstance().isPostback()) {....

JSF: How to attach an actionListener to component created programatically?

I have to create some commandLinks dynamically and attach some action listener to it, So I've put <h:panelGrid> on the JSP page and used such code to add the commandLinks and to assign action listeners to:
public ManagedBean(){
List<UIComponenet> child = panelGrid.getChilderen();
list.clear();
List<MyClass> myList = getSomeList();
for (MyClass myObj : myList){
FacesContext ctx = FacesContext.getCurrentContext();
HtmlCommandLink cmdLink = (HtmlCommandLink) ctx.getApplication.createComponent(HtmlCommandLink.COMPONENT_TYPE);
cmdLink.setValue(myObj.getName());
cmdLink.setActionLinstner(new ActionListener(){
public void processAction(ActionEvent event) throws AbortProcessingException{
System.out.println (">>>>>>>>>>>>>>>>>I am HERE ");
}
});
child.add(cmdLink);
}
}
But unfortunately, when I press this commandLinks, an exception thrown! How can I add component event listeners at runtime?
(Note, the code above my contain syntax/compilation errors as I just wrote).
First, you need to manually assign ID to any dynamically created UINamingContainer, UIInput and UICommand components. Otherwise JSF can't locate them in the component tree based on the request parameters, because it wouldn't match the autogenerated ID's.
Thus, at least do:
HtmlCommandLink link = new HtmlCommandLink();
link.setId("linkId");
// ...
Second, you're supposed to create an ActionListener as MethodExpression as follows:
FacesContext context = FacesContext.getCurrentInstance();
MethodExpression methodExpression = context.getApplication().getExpressionFactory().createMethodExpression(
context.getELContext(), "#{bean.actionListener}", null, new Class[] { ActionEvent.class });
link.addActionListener(new MethodExpressionActionListener(methodExpression));
// ...
...and of course have the following method in the backing bean class behind #{bean}:
public void actionListener(ActionEvent event) {
// ...
}
All the above dynamic stuff basically does the same as the following raw JSF tag:
<h:commandLink id="linkId" actionListener="#{bean.actionListener}" />
I had the same problem.
Transient components do not work with actionListeners.
Do not call
FacesContext.getCurrentInstance().getViewRoot().setTransient(true);
or
component.setTransient(true);
As soon as I removed it, it was OK.

JSF ResponseWriter custom components

I know about startElement, endElement, and writeAttribute methods on ResponseWriter. My problem is that I want to for example output a h:commandLink by declaring it like HtmlCommandLink link = new HtmlCommandLink(); .
How can I output other UIComponents like this in my own component? I might want to use some RichFaces ajax stuff in my components aswell so hoping I can avoid making it all by scratch.
Edit: What I'm trying to do is create my own tag library with the following tag <myTags:commentTree>. Every comment have a reply button, when the reply button is clicked I render the reply form beneath the comment. Once that is rendered, I would like to output for example the richfaces <a4j:commandButton> component. This have to be done inside my own java tag file which Ive called for CommentsTreeUI.java.
Normally I output all my elements that display the forms and buttons with writer.startElement("input", myComponent); writer.writeAttribute("type", "button", null); but if I could instead do for example startElement("a4j:commandbutton", myComponent) that would help my ALOT since it has all the built in ajax features etc.
Any clues?
This problem was solved by adding new components by using
HtmlCommandButton button = new HtmlCommandButton();
button.encodeAll(context);
You can do something like this:
HtmlCommandLink link = new HtmlCommandLink();
getChildren().add(link);
It does depend on what you want to do with the child components though i.e. if you want them surrounded with custom HTML (in an HTML list, for example) you will need something a bit more complex.
One approach to making composite controls is to use the binding attribute to associate the tag with your own code:
<f:view>
<h:form>
<h:panelGroup binding="#{compositeControlBean.panelGrid}" />
</h:form>
</f:view>
The bean configuration in faces-config.xml:
<managed-bean>
<managed-bean-name>compositeControlBean</managed-bean-name>
<managed-bean-class>
composite.CompositeControlBean
</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
The bean code:
/**
* Configure this bean in request scope as "compositeControlBean".
*/
public class CompositeControlBean {
private transient UIComponent panelGrid;
public UIComponent getPanelGrid() {
if (panelGrid == null) {
panelGrid = createCompositePanel();
}
return panelGrid;
}
public void setPanelGrid(UIComponent panelGrid) {
this.panelGrid = panelGrid;
}
private UIComponent createCompositePanel() {
initContextMemebers();
UIComponent commandLink = createCommandLink();
String id = view.createUniqueId();
UIComponent panelGrid = application
.createComponent("javax.faces.HtmlPanelGroup");
panelGrid.setId(id);
panelGrid.setRendererType("javax.faces.Group");
panelGrid.getChildren().add(commandLink);
return panelGrid;
}
private UIComponent createCommandLink() {
// create control
String id = view.createUniqueId();
UIComponent commandLink = application
.createComponent("javax.faces.HtmlCommandLink");
commandLink.setId(id);
commandLink.setRendererType("javax.faces.Link");
// set attributes (bind to printHello method)
Map<String, Object> attributes = commandLink
.getAttributes();
MethodExpression action = expressionFactory
.createMethodExpression(elContext,
"#{compositeControlBean.printHello}",
String.class, new Class<?>[0]);
attributes.put("value", "print hello");
attributes.put("actionExpression", action);
return commandLink;
}
private transient FacesContext context;
private transient Application application;
private transient ELContext elContext;
private transient ExpressionFactory expressionFactory;
private transient UIViewRoot view;
private void initContextMemebers() {
context = FacesContext.getCurrentInstance();
application = context.getApplication();
elContext = context.getELContext();
expressionFactory = application.getExpressionFactory();
view = context.getViewRoot();
}
public String printHello() {
System.out.println("Hello");
return null;
}
}

Resources