I have a simple JSF application that iterates over a list.
I know I can use ui:repeat, but just for learning and comparison purposes,
since c:forEach from JSTL is also part of JSF, I'd like to see how it works.
What I do not understand, why does c:forEach evaluate the items for every
iteration. I have a CDI bean that produces the list, and its getter is called
when rendering every row of the c:forEach iteration. And even worse, it calls
the getter even when c:forEach uses a variable that is set before that as
shown below.
This code (part of my index.xhtml JSF file):
Primes (over c:forEach):<br/><br/>
<c:set var="order" value="${0}" />
<c:set var="ps" value="${primeCalculator.primes}" />
<c:forEach items="${ps}" var="p">
<c:set var="order" value="${order + 1}" />
<h:panelGrid columns="2">
<h:outputLabel value="${order}." />
<h:outputLabel value="${p}" />
</h:panelGrid>
</c:forEach>
renders the list of primes properly (n=10):
Primes (over c:forEach):
1. 2
2. 3
3. 5
4. 7
but when I do some logging from the CDI bean (#Named, #RequestScoped):
public List<Integer> getPrimes()
{
System.out.println("getPrimes() n=" + n + ", cnt=" + (cnt++));
ArrayList<Integer> result = new ArrayList<>();
boolean compound[] = new boolean[n + 1];
for (int i = 2; i <= n; i++)
if (!compound[i])
{
result.add(i);
for (int j = 2 * i; j <= n; j += i) compound[j] = true;
}
return result;
}
I see the following in the log:
[2022-03-28T23:06:20.066+0200] (skip...) [[getPrimes() n=0, cnt=0]]
[2022-03-28T23:06:20.067+0200] (skip...) [[getPrimes() n=10, cnt=1]]
[2022-03-28T23:06:20.069+0200] (skip...) [[getPrimes() n=10, cnt=2]]
[2022-03-28T23:06:20.069+0200] (skip...) [[getPrimes() n=10, cnt=3]]
[2022-03-28T23:06:20.069+0200] (skip...) [[getPrimes() n=10, cnt=4]]
[2022-03-28T23:06:20.069+0200] (skip...) [[getPrimes() n=10, cnt=5]]
[2022-03-28T23:06:20.069+0200] (skip...) [[getPrimes() n=10, cnt=6]]
Why is the getter called for every prime that is printed out?
And how could that be avoided? Yes, I have noticed when using
ui:repeat, that it gets called only twice, which is still more
than would make me happy, but it is better than once for each
output item. I am using: Jakarta EE 9, GlassFish 6.2.3, and IntelliJ IDEA 2021.3.2 (that is JSF 3.0.1, inject api 2.0.0, cdi api 3.0, e-api 4.0, implementations weld se 4.0.2) Thanks.
Related
The inputNumber declaration below will not accept any input, however when I removed the minValue attribute, it accept input.
What am not doing wrong here?
code xhtml:
<a:column>
<p:inputNumber id="year"
value="#{consomationControleur.myYear}"
maxValue="#{now.year + 1900}"
minValue="2000"
>
<p:ajax event="blur" process="#this"/>
</p:inputNumber>
</a:column>
java CDI bean
#Getter
#Setter
#Named("consomationControleur")
#ViewScoped
public class ConsomationControleur implements Serializable {
private int myYear;
//...
}
jsf version 2.2 primefaces version 11
I tried to remove the maximum value.
I tried to set minValue to a negative value, it works, but I need the years 1990-2022. So I only need positive values.
I would like to make a note
minValue="1980" maxValue="#{now.year + 1900}" it works but because of JS
but because of JS When you write a value, you selected the input and you type a number (like 2), the JS triter this value, and as the minimum condition is 1980, automate re update the input and put the field to minimum value 1980.
to work you have to select only the 3rd number (8) and change it, change a number by one, it's bad, but it works like this .
or you using <f:validateLongRange minimum = "" maximum = "" />
<p:inputNumber value="#{consomationControleur.anneeActuel}" >
<f:validateLongRange minimum = "1900" maximum = "#{now.year + 1900}" />
<p:ajax event="blur" update="display2" process="#this"/> </p:inputNumber>
I have a checkBox inside dataTable in my JSF page with value, which it gets from map which is in bean.
<h:selectBooleanCheckbox id="rowChecked"
value="#{messagesListBean.selectedRowIndexesMap[rowIndex]}">
<a4j:ajax event="click" execute="#this" render="#none"
limitRender="true"
listener="#{messagesListBean.rowChecked(rowIndex, rec)}">
</a4j:ajax>
</h:selectBooleanCheckbox>
<h:outputLabel for="rowChecked" class="checkbox-style" />
Map is initialized when page loads and it initializes just fine - has 4 values, all of them are false. Map itself looks like this:
public Map<Integer, Boolean> selectedRowIndexesMap = new HashMap<Integer, Boolean>();
The problem is when I press one of the checkBoxes. It should edit value inside the map according to rowNumber. What it does is it add completely new element to the map, it does not edit it.
For example, if before clicking checkbox map looked like this: 0-false; 1-false; 2-false; 3-false;
After clicking the checkbox, it looks like this: 0-true; 0-false; 1-false; 2-false; 3-false;
Why is this happening?
Because numbers in EL are by default evaluated as Long. I.e. #{rowIndex} is actually Long.
A Long value of 0L does not equal an Integer value of 0. Evidence is below:
Long zeroL = 0L;
Integer zero = 0;
System.out.println(zeroL.equals(zero)); // false
So it simply inserts a new map entry with 0L as key instead of replacing the value associated with key of 0. Change it to a Map<Long, Boolean> and it'll work. This problem is unrelated to JSF checkbox.
I have a bit complex structure. First, I start problems table. Each problem has a subcategory and maincategory and also each subcategory has a main category. Besides, a main category have sub categories and a subcategory have problems.
Then, I tried to list them like below.
I have created a dynamic web form with JSF SelectManyCheckbox. It is the following:
<ui:repeat var="mainCatvar" value="#{dprEdit.mainCategories}">
<h:outputText value="#{mainCatvar.mainCatName}" styleClass="mainTitle"/>
<ui:repeat var="subCategory" value="#{mainCatvar.subCategories}">
<h:outputText value="#{subCategory.subCatName}" styleClass="title" />
<p:selectManyCheckbox value="#{dprEdit.selectedProblems[subCategory]}">
<f:selectItems value="#{subCategory.problems}" var="problem"
itemValue="#{problem.problemId}"
itemLabel="#{problem.problemName}" />
</p:selectManyCheckbox>
</ui:repeat>
</ui:repeat>
In the java code,
problems = new ProblemDAO(ConnectionManager.getConnection())
.getProblems(formId);
for (int i = 0; i < problems.size(); i++) {
Problem problem = problems.get(i);
if (subcat == problem.getSubCat())
problemIdList = addElement(problemIdList,problem.getProblemId().toString());
else
Arrays.fill(problemIdList, null);
problemIdList = addElement(problemIdList,problem.getProblemId().toString());
subcat = problem.getSubCat();
selectedProblems.put(problem.getSubCat(), problemIdList);
}
PS: addElement is a function that is written to add an element to the array whose length is already defined.
I suppose, I send the data like below to the xhtml.
(subcat1, [1,2]),(subcat3,[5]) etc...
And the value of dprEdit.selectedProblems[subCategory] is a list.
And I send a list as a value.
So I expect that the problems whose ids are 1,2,5 must be checked but they don't.
Where am I wrong?
JSF 2.2, Mojarra, PrimeFaces and Tomcat 7.x.
This is called 3 times, for each row once. (example table has 3 rows)
....
<ui:param name="rowIndex" value="#{cc.attrs.rowIndex}" />
<ui:param name="rowActive" value="#{cc.attrs.activeRow}" />
<c:set var="index" value="#{rowIndex}" type="java.lang.Long"/>
<c:set var="activeRowIndex" value="#{rowActive}" type="java.lang.Long"/>
<c:choose>
<c:when test="${index == 2}">
ACTIVE
</c:when>
<c:when test="${index != activeRowIndex}">
${index} - ${activeRowIndex} - INACTIVE
</c:when>
<c:otherwise>
NONE
</c:otherwise>
</c:choose>
....
Result:
0 - 1 - INACTIVE
1 - 1 - INACTIVE
2 - 1 - INACTIVE
I would have expected:
0 - 1 - INACTIVE
NONE
ACTIVE
I'm quite clueless why the result is so different from what i expected.
So i hope you can help me :-)
The variable names used suggests that you're using the composite inside a repeating component, such as <h:dataTable> or <ui:repeat>.
JSTL tags are executed during view build time, that moment when the JSF component tree is built based on XHTML source code. However, the var attribute of a repeating component is only available during view render time, that moment when the HTML output is produced based on JSF component tree.
In effects, at least the #{cc.attrs.rowIndex} is always null when JSTL runs.
When you're dependent on conditions which are only available during view render time, then you should be using the rendered attribute of a JSF component instead of JSTL <c:choose>/<c:if>.
E.g.
<c:set var="active" value="#{cc.attrs.rowIndex == 2}" />
<c:set var="inactive" value="#{not active and cc.attrs.rowIndex != cc.attrs.activeRow}" />
<c:set var="none" value="#{not active and not inactive}" />
<h:outputText value="ACTIVE" rendered="#{active}" />
<h:outputText value="#{index} - #{activeRowIndex} - INACTIVE" rendered="#{inactive}" />
<h:outputText value="NONE" rendered="#{none}" />
Note that this problem doesn't affect the <c:set>. It merely creates a EL variable mapping (an "alias"), it doesn't immediately evaluate the EL expression and store its result somewhere (as long as scope isn't definied). Also note that ${} and #{} behave exactly the same when Facelets is used instead of JSP. As the ${} is basically a heritage of legacy JSP, you should prefer exclusively using #{} to avoid confusion by yourself and your future maintainers.
I'm using the repeat tag of JSF 2.0 to loop through a list of objects and display some of their properties. I want to use the varStatus attribute of repeat so that I can access the loop index, the number of the last list item, and to tell whether the end of the list has been reached (so the spacer won't be displayed). I thought this would work:
<ui:repeat var="anObject" varStatus="repeatStatus" value="#{objectList}">
<h:panelGroup>
<h:outputText value="Item #{repeatStatus.index + 1} of #{repeatStatus.end}" />
<h:outputText value="#{anObject.text}" />
</h:panelGroup>
<h:outputText value=" " rendered="#{false == repeatStatus.last}" />
</ui:repeat>
However, it never displays anything for repeatStatus.end. The index and last properties work well.
Instead of repeatStatus.end, I tried using objectList.size(), but that worked for only the first item in the list.
How can I display the number of items in the list as part of the "Item x of y" text?
The end is only used when you set the size attribute.
<ui:repeat ... size="#{fn:length(objectList)}">
Alternatively, you can also just use it directly.
Item #{repeatStatus.index + 1} of #{fn:length(objectList)}
By the way, the boolean comparison in #{false == repeatStatus.last} is ugly. It returns a boolean already; if you want to negate it, rather use #{not repeatStatus.last}.