How to specify the order of evaluation in JSF? - jsf

I'm on JSF-2.1_29. As far as I can see, the order of manged bean's methodw invokation corresponds to how they are placed in the markup. In my particular case I have:
<h:outputText value="Rows count:"/>
<h:outputText value="#{bonusBean.rowsCount}"/>
<rich:dataTable id="bonusesTable"
var="bonus"
value="#{bonusBean.list}"
render="ds"
rowClasses="tr0, tr1">
<!-- Columns, etc... -->
</rich:dataTable>
ManagedBean itself:
public class BonusBean{
private Integer rowsCount = 0;
//GET, SET
public List<BonusActionDTO> getList(){
List<BonusActionDTO> lst = new ArrayList<BonusActionDTO>();
//Getting the list from a persistance storage
rowsCount = lst.size();
return lst;
}
In that case getRowsCount() is being invoked first which returns 0, so the Rows count: 0 is going to be printed when the page is loaded first, although the table may contain some rows. After invokation of getRowsCount() method, getList() is being invoked, so the actual Rows count is going to printed only after refreshing the page. How can I reorder that order of methods invokation? Is it possible in JSF?

I don't know if you can change the order of the getter invocations. But apart of this, the dependency of the result of a get method on the call of another get method is a bad design. Two getter should return the same results without regard of the order in which they are called.

Related

Primefaces: how to update a p:inputText with a parameter on the setter? [duplicate]

This question already has answers here:
How to set a Map value in h:inputText
(1 answer)
Write to a map property in an h:inputText in h:dataTable
(1 answer)
Closed 4 months ago.
I have a page where the user must input grading for a number of categories. The categories are retrieved dynamically from a database (there are different user types, and for each type you get different categories). As there are subcategories, I have implemented it using <p:treeTable>.
I have the grades stored in a map, with the category id as the key and the grade as the value:
HashMap<Integer, Double> detailedGrades
The problem is how to get and set the grade for each category using <p:inputText>, because the category id has to be a parameter in that. I have managed to do the getter like this:
<p:inputText id="cat" value="#registerCandidateGradeBean.getGradeForCat(gradingCat.pkCertGradeCategoryId)}"/>
and in the bean:
public Double getGradeForCat(int catID){
return detailedGrades.containsKey(catID) ? detailedGrades.get(catID) : null;
}
This displays the value in the input field OK, but I don't know how to set the value when the user changes it. I tried with <p:ajax listener> as mentioned here, where I set
<p:ajax listener="#{registerCandidateGradeBean.setGradeForCat1(gradingCat.pkCertGradeCategoryId)}"/>
and in the bean:
public void setGradeForCat1(int catID){
System.out.println(catID);
}
but whenever I change the input value I get a "javax.el.PropertyNotWritableException: Illegal Syntax for Set Operation" exception. I tried to use a valueChangeListener on the inputText
<p:inputText id="cat" value="#registerCandidateGradeBean.getGradeForCat(gradingCat.pkCertGradeCategoryId)}"
valueChangeListener="#{registerCandidateGradeBean.setGradeForCat}"/>
and in the bean:
public void setGradeForCat(ValueChangeEvent e){
System.out.println("called");
//System.out.println(catID);
System.out.println(e.getNewValue());
}
but it never gets called. (I have tried both setGradeForCat(ValueChangeEvent e) and setGradeForCat(ValueChangeEvent e, int catID)).
Any ideas?
Forgot to mention: we are using primefaces 5.3.
The problem is, that you use value="#registerCandidateGradeBean.getGradeForCat(gradingCat.pkCertGradeCategoryId)}" in the input component.
This makes jsf looking for a getter and setter which is called getgetGradeForCat and setgetGradeForCat.

NullPointerException while displaying the same page after some taks in jsf

First I want to tell :
I saw How to use JSF's h:selectBooleanCheckbox with h:dataTable to create one object per row?
and my problem is like same but not solved problem.
Following is my code :
*.xhtml
<rich:column>
<h:selectBooleanCheckbox id="resolveTxn" value="#{verifyTransactionBean.checked[verifyTxnList.id]}"/>
</rich:column>
.
.//Some code here
.
<h:commandButton value="Resolve" action="#{verifyTransactionBean.resolveToTxn}" styleClass="btn" />
and following is my code
private Map<Long, Boolean> checked = new HashMap<Long, Boolean>();
public void resolveToTxn() {
List<ToVerifyTxnDTO> list = new ArrayList<ToVerifyTxnDTO>();
for (ToVerifyTxnDTO dTO : toVerifyTxnDTOList) {
if(checked.get(dTO.getId())){//I got null pointer exception here
Long id=dTO.getId();
verifyTransactionSessionBeanLocal.updateResolvedflag(id);
}
}
}
What I want to do ?
I want to pass checked row id as parameter on following :
verifyTransactionSessionBeanLocal.updateResolvedflag(id);
And after clicking the button Resolve some operation should be done and refresh dataTable and display same page. Operation is done but getting null pointer exception while displaying (Reddering) the same page
Thanks
The if () test on a Boolean will throw NullPointerException if the Boolean itself is actually null instead of true or false. In other words, the checked.get(dTO.getId()) has unexpectedly returned null for some reason.
That can have several causes:
The JSF implementation hasn't properly updated the checked map with the value from <h:selectBooleanCheckbox> for some reason.
The EL implementation was using Boolean instead of boolean for some reason.
The toVerifyTxnDTOList has incompatibly changed during the process which caused that the currently iterated dTO isn't the one which was been displayed in the table at the moment checkboxes were clicked (perhaps because the bean is request scoped instead of view scoped).
The toVerifyTxnDTOList contains more items than present in the checked map because you're using pagination and displaying only a subset at once.
In any case, to fix the NullPointerException, just add an additional nullcheck:
Boolean dtoChecked = checked.get(dTO.getId());
if (dtoChecked != null && dtoChecked) {
// ...
}
This however doesn't fix the underlying cause of your problem. My best guess would be that the toVerifyTxnDTOList contains more items than actually being displayed in the table. Thus, it's one of the last mentioned two causes mentioned in the list. The first mentioned two causes are theoretically possible, but in practice I haven't seen this before.

Bind the value of an input component to a list item by index

here is an example :
<h:outputLabel for="category1" value="Cateogry"/>
<h:selectOneMenu id ="category1" value="#{articleManageBean.categoryId1}"
converter="categoryConverter">
<f:selectItems value="#{articleManageBean.categories}" var="category"
itemValue="#{category.id}" itemLabel="#{category.name}" />
</h:selectOneMenu>
and here is the managed bean that I have
#ManagedBean
#SessionScoped
public class ArticleManageBean {
private Long categoryId1;
private List<Category> categories;
//...
}
The categories list gets populated from db, and selectOneMenu gets populated with this list using a converter.
My First question:
If I want to create another selectOneMenu in my jsf page I would have to copy paste the entire thing and just change the value of selectOneMenu to say categoryId2 thus putting another attribute to managed bean called categoryId2. That is not practical. I want to map these values of selectMenu to list items, for instance to an attribute
List<Long> categoryIds;
if I use
<h:selectOneMenu id ="category1" value="#{articleManageBean.categoryIds.[0]}" >
I get an error
javax.el.PropertyNotFoundException: /createArticle.xhtml #47,68 value="#{articleManageBean.categoriesId[0]}": Target Unreachable, 'null' returned null
If I nitialize the Araylist then I get this exception
javax.el.PropertyNotFoundException: /createArticle.xhtml #47,68 value="#{articleManageBean.categoriesId[0]}": null
My second question:
Is there a way to dinamicly write selectOneMenu tags, by that I mean not to copy paste the entire tag, just somehow create a function that take the categoryId parameter and writes automaticaly the tag (somekind of custom tag maybe ?)
Hope you understood my questions
thanks in advance
Use the brace notation instead to specify the index.
<h:selectOneMenu id="category1" value="#{articleManageBean.categoryIds[0]}">
You only need to make sure that you have already prepared the values behind #{articleManageBean.categoryIds}. JSF won't do that for you. E.g.
private List<Long> categoryIds = new ArrayList<Long>();
public ArticleManageBean() {
categoryIds.add(null);
categoryIds.add(null);
categoryIds.add(null);
// So, now there are 3 items preserved.
}
an alternative is to use Long[] instead, this doesn't need to be prefilled.
private Long[] categoryIds = new Long[3]; // So, now there are 3 items preserved.

How do I bind a inputbox values to a map value in a backing bean property when using a wizard

I am using the Primefaces wizard component. On one tab I am dynamically creating input boxes based on previous tabs input(user type). The inputbox text labels are derived from a list. In my backing bean, I have a map that contains input labels as keys and inputbox inputs as values.
Clicking on next, I would like the map(values) to be updated with the user input (corresponding to the key)
<c:forEach items="#{gdsiGeodataBean.actionCommand.fields}" var="reqs">
<h:outputLabel for="#{reqs.name}" value="#{reqs.name}:* " />
<pou:inputText value="#{gdsiGeodataBean.actionCommand.values['reqs.name']}" required="true" requiredMessage="Input is required."/>
</c:forEach>
My backing bean :
private List<RequiredParam> fields; // +getter (no setter required)
private Map<String, String> values; // +getter (no setter required)
public CommandAction(String actionName, String actionParams, String context) {
this.actionName = actionName;
this.actionParams = actionParams;
this.contextName = context;
//Set up parameters
getRequiredParams();
getOptionalParams();
fields = getFields();
values = new HashMap<String, String>();
}
Essentially what I would like is for the map values to be updated with user inputs from the textinput boxes.
Your approach to bind the input value to a map is not entirely correct.
<pou:inputText value="#{gdsiGeodataBean.actionCommand.values['reqs.name']}" required="true" requiredMessage="Input is required."/>
You're specifying a fixed map key instead of a dynamic map key based on the currently iterated #{reqs}. This way all submitted values will end up in one and same fixed map key "reqs.name", whereby each one overrides each other so that you only get the value of the last field in the map.
You need to remove those singlequotes to make it a really dynamic key.
<pou:inputText value="#{gdsiGeodataBean.actionCommand.values[reqs.name]}" required="true" requiredMessage="Input is required."/>
Unrelated to the concrete question, even though this approach will work when used as-is in your question, the <c:forEach> will fail in certain circumstances. E.g. when used inside a composite component or an iterating JSF component. Rather use <ui:repeat> instead. See also JSTL in JSF2 Facelets... makes sense?

"ValueExpression Map" of a JSF component

I'm storing value expressions in a JSF component with the f:attribute tag, e.g.:
<h:inputText ...>
<f:attribute name="myId1" value="#{bean.prop1}" />
<f:attribute name="myId2" value="#{bean.prop2}" />
<f:attribute name="myId3" value="#{bean.prop3}" />
</h:inputText>
Is there a way to access all of those value expressions programmatically? (without knowlegde of the names myId1, myId2,...)
Section 9.4.2 of the JSF 2.1 specification says that those values are stored "in the component’s ValueExpression Map".
That's the only occurrence of the term "ValueExpression Map" in the complete spec.
How do I access that map?
In the UIcomponent's Method getValueExpression() of the Jboss/Mojarra implementation the map
getStateHelper().get(UIComponentBase.PropertyKeys.bindings)
is used to obtain a single value expression.
I guess that map is a super set of the "ValueExpression Map"?
Can I be sure that all implementations and all inherited (standard) components use that map to store ValueExpressions?
Thanks.
In theory you should be able to see them all by UIComponent#getAttributes():
Map<String, Object> attributes = component.getAttributes();
for (Map.Entry<String, Object> entry : attributes.entrySet()) {
System.out.printf("name=%s, value=%s%n", entry.getKey(), entry.getValue());
}
However, that doesn't work the way as you'd expect. It only returns static attributes. This does not seem to ever going to be fixed/implemented. See also JSF issue 636. I'd suggest to stick to attribtues with predefinied prefix and an incremental numerical suffix, like as you've presented in your example. That's also what I've always used to pass additional information from the component on to custom validators and converters. You can just collect them as follows:
Map<String, Object> attributes = component.getAttributes();
List<Object> values = new ArrayList<Object>();
for (int i = 1; i < Integer.MAX_VALUE; i++) {
Object value = attributes.get("myId" + i);
if (value == null) break;
values.add(value);
}
System.out.println(values);
An alternative to the answer given by BalusC might be to use nested facets or UIParameter components. Facets can be retrieved as a map using getFacets but you probably need to put an additional UIOutput inside each facet to access its value expression.
Nested UIParameters can be accessed by iterating over the components children and checking for instanceof UIParameter. UIParameters have name and value attributes and so could be easily converted to a map.
I have used parameters in a custom component, but I'm not sure how a standard UIInput like in your example reacts to these.
BalusC is right. UIComponent#getAttributes().get(name) gets values from both places - at first from attributes map and then if not found from "value expression map". To put some value you have to call UIComponent#setValueExpression(name, ValueExpression). If value is literal, it gets stored into the attribute map, otherwise into the "value expression map". Everything is ok then.

Resources