JSF 2.2: Passing bean attribute as ValueExpression to composite - jsf

I want to pass a non-managed (non-String) object as an attribute on a dynamically added composite component, and have it survive the session.
The JSF 2.2 ViewDeclarationLanguage#createComponent handles dynamic non-string attribute values to composite components differently than the older Mojarra dependent code (Application#createComponent). I can't find the approach that works completely with the JSF 2.2 technique, but it's probably me.
[I'm trying to remove Mojarra dependencies by converting to MyFaces (and also working around some other Mojarra issues). I'm using JSF 2.2, CDI Weld 2.2.16, Tomcat v8.0]
I'm instantiating different composite components like these programmatically (notice the bean attribute):
<cc:interface>
<cc:attribute name="bean" required="true" type="com.aadhoc.cvc.spikes.extensionsapproach.ExtensionBeanInterface"/>
</cc:interface>
<cc:implementation>
<h:panelGrid columns="2">
<h:outputText value="Title:"/>
<h:inputText value="#{cc.attrs.bean.title}"/>
</h:panelGrid>
</cc:implementation>
In the older Mojarra dependent approach, I instantiate the non-managed bean object, and add it directly to the composite component as an attribute and it works great (I'm using #BalusC's great but Mojarra dependent sample code from OmniFaces Component#includeCompositeComponent):
ExtensionBeanInterface bean = Class.forName(className).newInstance();
attributes = new HashMap<String, Object>();
attributes.put("bean", bean); // Using bean object itself
[..]
UIComponent composite = application.createComponent(context, resource);
composite.getAttributes().putAll(attributes);
[..]
In JSF 2.2, I've found that I must pass a String ValueExpression instead of my bean object directly. I'm currently using this technique, and can't get it quite right:
FacesContext context = FacesContext.getCurrentInstance();
ELContext elContext = context.getELContext();
ValueExpression beanValExp = context.getApplication().getExpressionFactory()
.createValueExpression(elContext, "#{customBeanVE}", ExtensionBeanInterface.class);
beanValExp.setValue(elContext, bean);
String beanValExpStr = beanValExp.getExpressionString();
attributes = new HashMap<String, Object>();
attributes.put("bean", beanValExpStr); // Using VE instead of bean object
UIComponent composite = context.getApplication().getViewHandler()
.getViewDeclarationLanguage(context, context.getViewRoot().getViewId())
.createComponent(context, taglibURI, tagName, attributes);
[..]
This works great on the first "add composite", but on any following form submit, I get:
/resources/com/aadhoc/cvc/spikes/extensionsapproach/components/House.xhtml
#16,49 value="#{cc.attrs.bean.title}": Target Unreachable, 'bean'
returned null
I've verified that the composite's required and type attributes are working fine, and that the #{cc.attrs.bean.title} is initially showing the bean's title. I verified with a static use of the composite component that refreshes work fine.
What's the deal, and how can I handoff the bean object so that it survives with the composite across the session?

I had this working great in Mojarra. I could put the bean object in the attribute value, and all was wonderful. Trying MyFaces, I needed to change/update my approach, and I now needed to use EL strings instead of direct object references.
Since all was working with just putting bean object into attributes Map, I wanted a magical yet elegant place to put bean objects and have them survive. I could have put them into the "global" session Map (like this: How to save an object into JSF Session), but it wasn't clean enough. I then put them into my one main session state bean (modelerBean), and it was right. This is how I saved the bean, and how I pointed to it with an EL string. No need to create ValueExpression or MethodExpression objects or register special mappings. This JSF 2.2 compatible approach worked for me in both Mojarra and MyFaces.
public void onAdd(ActionEvent ev) {
[..]
ChosenBean chosenBean = new ChosenBean();
chosenBean.setComponentId(id);
chosenBean.setBean(bean);
modelerBean.addChosen(chosenBean);
[..]
String el = "#{modelerBean.getChosen('"+id+"').bean}"
attributes.put(MODELER_EXTENSION_BEAN_ATTRIBUTE_NAME, el);
[..]
I decided on this after reading #BalusC's post: How do I get and set an object in session scope in JSF?
Note, my experience with #BalusC's two "add composites dynamically" approaches (How to programmatically or dynamically create a composite component in JSF 2) is that you should definitely use the JSF 2.2 approach if you can. The old Mojarra approach does work if you aren't in JSF 2.2. Once I modified my code to have the JSF 2.2 approach work, the old Mojarra approach would break in strange ways.

Related

Attribute "var" invalid for tag selectItems according to TLD

I'm working with h:selectOneMenu and want to set label and value for it then the UI option list will display label and in my Bean can get the selected value. I have this code working fine:
<h:selectOneMenu value="#{myBean.selected}">
<f:selectItems value="#{myBean.myList}"/>
</h:selectOneMenu>
But problem appear when I try to add var to set label and value by adding the code below:
<h:selectOneMenu value="#{myBean.selected}">
<f:selectItems value="#{myBean.myList}" var="field"
itemLabel="#{field.label}" itemValue="#{field.value}"/>
</h:selectOneMenu>
The error show Attribute var invalid for tag selectItems according to TLD.
My question are:
Can I resolve this?
If "YES" then "HOW"?
If "NO" then any other way for me to done this?
Thanks
Attribute var invalid for tag selectItems according to TLD
The <f:selectItems var> attribute was introduced in JSF 2.0 for Facelets. This error suggests either you're using legacy JSF 1.x instead of the newer JSF 2.x where <f:selectItems> lacks the var attribute, or you're using legacy JSP instead of its successor Facelets as JSP is deprecated since JSF 2.0 and didn't get any of new JSF 2.0 tags/attributes. The exact error message is due to the JSP-specific term "TLD" however recognizable to come from JSP parser, which in turn confirms more that you're actually using JSP instead of Facelets.
In order to use <f:selectItems var>, you need to make sure of the following:
You're using JSF 2.x instead of JSF 1.x.
You're using Facelets instead of JSP.
If you can't upgrade/migrate, then you need to fall back to legacy SelectItem approach in backing bean. You can convert from your List<Field> to a List<SelectItem> as below:
private List<Field> fields;
private List<SelectItem> selectItems; // +getter
#PostConstruct
public void init() {
fields = yourFieldService.list();
selectItems = new ArrayList<>();
for (Field field : fields) {
selectItems.add(new SelectItem(field.getValue(), field.getLabel()));
}
}
<f:selectItems value="#{bean.selectItems}" />
See also:
Migrating from JSF 1.2 to JSF 2.0
Why Facelets is preferred over JSP as the view definition language from JSF2.0 onwards?
Our selectOneMenu wiki page

Missing parameter values in invoked method with composite components using ui:repeat

So after several days of debugging, we were eventually able to reproduce some strange interactions between composite components, ui:repeat, p:remoteCommand and partial state saving in JSF that we do not understand.
Scenario
A composite component iterates over a list of objects using ui:repeat. During each iteration, another composite component is included and arguments are passed.
<ui:composition (...)>
<ui:repeat var="myVar" value="#{cc.attrs.controller.someList}">
<namespace:myRemoteCommand someParam="SomeParam"/>
In the included composite component, there is an auto-run p:remoteCommand calling a method using parameters defined in the component's interface.
<ui:component (...)>
<p:remoteCommand actionListener="#{someBean.someMethod(cc.attrs.someParam)}"
autoRun="true"
async="true"
global="false">
However, when setting a breakpoint in someMethod(...), an empty string is passed. This only happens if partial state saving is set to false.
Solutions
We tried several solutions and the following ones appear to work (however we do not understand why and cannot foresee any further problems that could occur):
We can set partial state saving to true.
We can change the composite component pattern to ui:include.
We can remove one or both of the composite components and directly include the content instead.
Question
Why does JSF behave this way? What is this interaction between composite component, ui:repeat and argument passing that changes depending on whether we use ui:include / partial state saving or not?
We're using Primefaces 5.3, Glassfish 4.1, Mojarra 2.2.12, Java 8.
Your code is all fine. It's just that Mojarra's <ui:repeat> is broken. You're not the first one facing a state management related problem with <ui:repeat>.
Checkbox inside ui:repeat not refreshed by Ajax
Dynamically added input field in ui:repeat is not processed during form submit
Components are with the same id inside ui:repeat
<h:form> within <ui:repeat> not entirely working, only the last <h:form> is processed
Composite component with custom backing component breaks strangely when nested inside ui:repeat
ui:repeat in o:tree not working as expected
Root cause of your problem is that #{cc} is nowhere available at the moment the <ui:repeat> needs to visit the tree. Effectively, the <ui:repeat value> is null. A quick work around is to explicitly push the #{cc} in UIRepeat#visitTree() method. Given Mojarra 2.2.12, add below lines right before line 734 with pushComponentToEL(facesContext, null).
UIComponent compositeParent = getCompositeComponentParent(this);
if (compositeParent != null) {
compositeParent.pushComponentToEL(facesContext, null);
}
And add below lines right after line 767 with popComponentFromEL(facesContext).
if (compositeParent != null) {
compositeParent.popComponentFromEL(facesContext);
}
If you don't build Mojarra from source, copy the entire source code of UIRepeat into your project, maintaining its package structure and apply above changes on it. Classes in /WEB-INF/classes have higher classloading precedence than those in /WEB-INF/lib and server's /lib. I have at least created issue 4162 to address this.
An alternative is to replace Mojarra by MyFaces, or to replace the <ui:repeat> by an UIData based component which got state management right such as <h:dataTable> or <p:dataList>.
<p:dataList type="none" var="myVar" value="#{cc.attrs.controller.someList}">
<namespace:myRemoteCommand someParam="SomeParam" />
</p:dataList>
You might only want to apply some CSS to get rid of widget style (border and such), but that's trivial.
See also:
Should PARTIAL_STATE_SAVING be set to false?

How to programmatically set <f:selectItems var> in UISelectItems instance

I've successfully replaced <s:selectItems> (Seam) with <f:selectItems> (Faces) in all of my JSPs. But I also have some Java classes using the object model and there I seem to have one unsolved problem: UISelectItems#setVar(String) doesn't seem to exist.
How can I fix my following code snippet?
ValueExpression labelExpr = expFactory.createValueExpression(elContext, "#{item.findLabel()}", String.class);
UISelectItems selectItems = new UISelectItems();
selectItems.setVar("item"); // FIXME: missing method
selectItems.setValue(new ArrayList<Element>(elements));
selectItems.setValueExpression("itemLabel", labelExpr);
return selectItems;
The JSF tag <f:selectItems value="#{items}" var="item" itemLabel="#{item.findLabel()}" /> works an supports var.
If an attribute appears to have no explicit getter/setter for it, then just put it directly in the attribute map as available by UIComponent#getAttributes().
selectItems.getAttributes().put("var", "item");
Or, if it represents a value expression, then use UIComponent#setValueExpression() instead (which is inapplicable for the var attribute, by the way).
Unrelated to the concrete problem, you'd like to migrate the view-specific code from Java (the model) to XHTML (the view) as well. There's in JSF2 nothing which is impossible in XHTML and only possible in Java. Declaring/defining/creating the view in XHTML is so much more maintenance-friendly than doing so in Java.

JSF component binding - some confusion

From web pages like this one,
http://www.jsftutorials.net/components/step5.html
I understand that the binding attribute in JSF tag/view component is to bind the view component to a Java instance of the UI component in the backing bean.
E.g., that's what is done in the following code:
<h:inputText value="#{ myBean.someProperty}" binding="#{ myBean.somePropertyInputText}"/>
But sometimes I see code like this:
<h:commandButton id="t1" binding="#{foo}" value="Hello, World!" onclick="alert('I am #{id:cid(foo)}'); return false;" />
where id:cid is a taglib function which is defined as follow:
public static String cid(UIComponent component) {
FacesContext context = FacesContext.getCurrentInstance();
return component.getClientId(context);
}
In the above code, binding="#{foo}" does not bind to "a Java instance of the UI component in the backing bean".
So what is the meaning of expressions such as binding="#{foo}" ?
It just binds the component to the current Facelet scope. This is particularly useful if you don't need it in the backing bean at all. This saves your backing bean code from useless properties which aren't been used in any of the other methods at all. Note that it also works that way in JSF 1.2. Not sure about JSF 1.0/1.1 though as it uses a different and JSF-proprietary EL API.
See also:
JSF component binding without bean property

How can I set a bean property when the page is loading in jsf?

In my project, I am using myfaces 1.2, rich faces 3.3.3 and spring 2.5 for backing beans. I searched over Internet for this simple need all weekend but I couldn't managed to set the bean property. h:inputhidden trick is good for calling a bean function. But I couldn't use this to set the property. For instance I want to set a bean property named as "number" to "1" when a page is loading.
Over internet I saw these was being used to set the bean property. I am new in web programming and I don't know how these working.. But maybe these make you remember something.
HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
request.getParameter()
<h:inputhidden>
Updated:
I want to set the value from the page not in bean. so I must get the value from the page. I will set the bean property differently in different pages. and I am using one bean for multiple page.
You can use:
<f:view before="#{bean.beforePhaseMethod}"> (if using facelets it's called beforePhase)
a #PostConstruct method for request-scoped beans
if there is no logic, simply give an initial value of the field private int foo = 1

Resources