JSF Pass OutputText and InputText value to a property - java

This is my second day working with JSF. Have no previous background in Java , Have Been working with Flex and C++ for quite some time. Some history so that everybody knows where im coming from . For a "rush" project i am running into an issue
<h:panelGroup id="txeTab" layout="block" class="txeTab">
<h1>TXE</h1>
<h:form id="txeForm">
<h:panelGrid columns="3">
<c:forEach items="${txeConfBean.getListTable()}" var="property">
<h:outputLabel id="key" value="${property.key}"/>
<h:inputText id="value" value="${property.value}" />
<h:commandButton value="Change" action='${txeConfBean.setProperty('key','value')}'/>
</c:forEach>
</h:panelGrid>
</h:form>
</h:panelGroup>
and The Bean is as follows
public HashMap <String,String> getListTable ()
{
String[] keys = new String[super.keyData.size()];
HashMap <String,String> retKeys = new HashMap <String, String>();
super.keyData.toArray(keys);
for (int i=0;i<keys.length;i++)
{
if(!keys[i].isEmpty())
{
retKeys.put(keys[i],getProperty(keys[i]));
}
}
return retKeys;
}
im able to display the Key,value pairs recursively. But i want to update a specific key with new value once someone updated the h:inputText id="value" value="${property.value}" /> and press the command button the new value is written to. Need help in this regard . Googling it make me feel there are too many ways to do it. Need help. I am just unable to figure out what to pass to ${txeConfBean.setProperty('key','value')} How can i pass the value of both InputText and OutPutText to setProperty ?

The ${}, which is inherited from legacy JSP, can't do a "set" operation on a property. It can only do a "get" operation on the property.
If you want to support both "get" and "set" on a property, you need the #{}. Even more, in general in JSF, you should not use ${} anymore. Further, in order to get/set a map value by an input component, you have to reference the map value by its key using the brace notation as #{bean.map[key]}.
<h:form id="txeForm">
<h:panelGrid columns="3">
<c:forEach items="#{txeConfBean.listTable}" var="property">
<h:outputLabel id="key" value="#{property.key}"/>
<h:inputText id="value" value="#{txeConfBean.listTable[property.key]}" />
<h:commandButton value="Change" />
</c:forEach>
</h:panelGrid>
</h:form>
Note that the command button action is omitted. JSF/EL will "automagically" already call the map's put() method when the model value is about to be updated. It's unnecessary in this particular construct. Also note that in this construct, the entire form is submitted. You might want either to put the command button outside the table, or to use <f:ajax> to submit the current "row" only.
See also:
Difference between JSP EL, JSF EL and Unified EL
Unrelated to the concrete problem: you're doing the business job in a getter method. This is extremely inefficient in case the property is referenced in iterating components. Do the business job in (post)constructor instead. It'll be invoked only once. This way you can make the getter really a fullworthy getter.
public HashMap<String,String> getListTable() {
return listTable;
}
See also:
JSF calling setter & getter multiple times.

Related

JSF 2.2 dynamic h:selectOneMenu in p:dataTable submit old value

I'm stuck on this current Problem, maybe somebody can help me out on this:
I try to build a dynamic Ruleseditor based on JSF 2.2 and the latest primefaces version (5.0).
Therefor I construct a p:dataTable which display rule values in seperate columns. To edit the values the Editor generates a pickable Valuelist from the used database table.
Everything works fine but if I try to select another value in the pickable value list and click the save Button the old Value is submitted to the bean and the h:selectOneMenu display the last used value.
I tried many solutions but this is the current on JSF Side:
<h:form id="ruleTableForm">
<p:dataTable value="#{table.rules}" var="rule">
<p:columns var="column" value="#{table.columns}" headerText="#{column.header}" width="250">
<h:selectOneMenu id="columnSelectMenu" value="#{rule.ruleColumnToValueMap[column].value}">
<f:selectItems value="#{column.pickList.entrySet()}"
var="pickItem" itemLabel="#{pickItem.value}"
itemValue="#{pickItem.key}" />
</h:selectOneMenu>
</p:columns>
</p:dataTable>
<div class="button_group">
<h:commandLink styleClass="add_button" action="#{regelEditorViewController.saveRuleset()}">Save</h:commandLink>
</div>
</h:form>
The parts used in the xhtml-File from my ViewController look like this:
#ManagedBean
#ViewScoped
public class RegelEditorViewController implements Serializable {
private RegelEditorViewData viewData;
#Produces
public RegelEditorViewData getViewData() {
if (this.viewData == null) {
this.viewData = this.viewDataService.produceViewData(0L,20L);
}
return this.viewData;
}
public void saveRuleset() {
RulesetViewData rulesetViewData = this.viewData.getCurrentRuleset();
this.viewDataService.saveRuleset(rulesetViewData);
this.refreshViewData();
}
}
The ruleColumnToValueMap is a Map:
private Map<RuleTableColumnViewData, Value> ruleColumnToValueMap = new LinkedHashMap<RuleTableColumnViewData, Value>();
...and the "pickList" in the "ruleTableColumnViewData"-object is also a Map with Strings:
private Map<String, String> pickList = new LinkedHashMap<String, String>();
The solutions I tried and but do not worked:
work with an ajax setter, which select the value (but the value is null in the setter method)
<f:ajax listener="#{regelEditorViewController.selectedRuleColumnValueChanged(table, rule, column, rule.ruleColumnToValueMap[column].value)}" render="#form" />
work with an immediate ajax setter, which sends always the last setted value to the bean:
<f:ajax listener="#{regelEditorViewController.selectedRuleColumnValueChanged(table, rule, column, rule.ruleColumnToValueMap[column].value)}" immediate="true" render="#form" />
tried to work with p:selectOneMenu but it seems to be very buggy: it is not possible to change the selected item, the component do not show a menu
tried to work with "SelectItem" in a "pickList" List and a "SelectItem" for the column in the "ruleColumnToValueMap" Map
Maybe someone have a possible solution that can help me?
The example above this answer is working correctly, the failure was that the controller do not save the data to the database!

JSF 1.2 Life Cycle understanding: Executing the ValueChangeListener method in InvokeApplication phase

I am using a <h:selectBooleanCheckbox> in the facet header of a DataTable. All the rows content for that <h:column> DataTable are <h:selectBooleanCheckbox>. The rows are getting selected perfectly the way I wanted. Below is the code I used:
<h:form>
<h:dataTable
value="#{employeeService.employeeList }"
var="empl"
binding="#{employeeService.dataTablebinding }">
......
......
......
<h:column>
<f:facet name="header">
<h:selectBooleanCheckbox id="chkBoxAll" value="#{employeeService.checkedHdr }" valueChangeListener="#{employeeService.checkAll }" onclick="submit()"></h:selectBooleanCheckbox>
</f:facet>
<h:selectBooleanCheckbox id="tableChkBox" value="#{empl.checked }" valueChangeListener="#{employeeService.getCheckChanged }" onclick="submit()"></h:selectBooleanCheckbox>
</h:column>
</h:dataTable>
This is the code for the ValueChangeListener:
public void checkAll(ValueChangeEvent event){
if(isInvokeApplicationPhase(event)){
Iterator<Employee> empl = employeeList.iterator();
while(empl.hasNext()){
Employee emp = empl.next();
emp.setChecked(checkedHdr);
}
}
}
This is the isInvokeApplicationPhase utility I added up for making this code work (referred the solution suggested by BalusC in this link: JSF 1.2: valueChangeListener event not returning newly selected value ):
public boolean isInvokeApplicationPhase(FacesEvent event){
if(event.getPhaseId() != PhaseId.INVOKE_APPLICATION){
event.setPhaseId(PhaseId.INVOKE_APPLICATION);
event.queue();
return false;
}
return true;
}
Now my Question:
What is the use of that isInvokeApplicationPhase check in the ValueChangeListener method? If I comment out this check then it does not work - WHY? I thought I have understood the JSF life cycle properly but this behavior proves that I have not :(
Kindly let me know the explanation based on the JSF life cycle phases.
The value change listener runs during validations phase and is intented to hook on the value change event. I.e. the newly submitted value is different from the original model value.
The validations phase runs before update model values phase, wherein JSF set all submitted, converted and validated values in the model (the backing bean's properties). So when you attempt to change the model value of a different field in the value change listener, then it would be overridden during the update model values phase.
This particular trick re-queues the value change event to the invoke application phase, which runs after update model values phase. So when you attempt to change the model value of a different field, then it won't be overriden.
In a nutshell: the validations phase is simply the wrong moment to manually manipulate the model values and the invoke application phase is the right moment to manually manipulate the model values.
Actually, the value change event is being abused here. You should rather be using an action event here, but this was not available for inputs in JSF 1.x. This was only available in JSF 2.x in flavor of <f:ajax listener>.
See also:
When to use valueChangeListener or f:ajax listener?
Setter not called for input text

Make JSF 2.0 execute methods as they are assigned to params, not store reference to method?

Before I begin, my apologies if the wording of this question's title is confusing. I hope my explanation here will make it much clearer.
In a JSF template of my application, I want to include another template and pass a parameter to it that holds the results of an application method. In this parent template, I have:
<ui:repeat var="loopObject" value="#{ApplicationBean.objectList}">
<ui:include src="anotherTemplate.xhtml">
<ui:param name="firstParam"
value="#{ApplicationBean.initForOtherTemplate(loopObject)}" />
</ui:include>
</ui:repeat>
It turns out, though, that initForOtherTemplate is not executed at this point and firstParam contains a reference to that method, rather than its return value, as I expected.
Actually, while initForOtherTemplate does have a return value, anotherTemplate.xhtml doesn't need it. However,the method does set up some other objects in ApplicationBean that this new template will use. For example, it sets values for importantInfo and importantInfoToo, which the other template needs.
anotherTemplate.xhtml contains:
<ui:remove>
<!--
When a parameter contains a method call, the method isn't executed until
the parameter is referenced. So we reference the parameter here and ignore
the results. There must be a better way.
-->
</ui:remove>
<h:outputText value="#{firstParam}" style="display: none;" />
<h:outputText value="#{ApplicationBean.importantInfo}" />
<h:outputText value="#{ApplicationBean.importantInfoToo}" />
If this template didn't reference firstParam, then importantInfo and importantInfoToo wouldn't be set or have unpredictable values. This is very disappointing, because I expected initForOtherTemplate to be executed in the parent template, rather than here, which feels messy.
How can I get the assignment of the parameter to actually execute the method immediately rather than store a reference to it?
The <ui:repeat> is an UIComponent which runs during view render time. The <ui:include> is a TagHandler (like JSTL) which runs during view build time. So at the moment <ui:include> runs, the <ui:repeat> isn't running and thus the #{loopObject} isn't available in the EL scope at all.
Replacing <ui:repeat> by <c:forEach> should solve this particular problem.
<c:forEach var="loopObject" items="#{ApplicationBean.objectList}">
<ui:include src="anotherTemplate.xhtml">
<ui:param name="firstParam"
value="#{ApplicationBean.initForOtherTemplate(loopObject)}" />
</ui:include>
</c:forEach>
See also:
JSTL in JSF2 Facelets... makes sense? - Substitute "JSTL" with "ui:include".

How to prepopulate repeating h:selectOneMenu?

Modifying working form with one spot per order to multiple spots per order I met problems with prepopulating h:selectOneMenu. Exception is java.lang.IllegalArgumentException: Value binding '#{spot.deliveryTypes}'of UISelectItems with component-path {Component-Path : [Class: javax.faces.component.UIViewRoot,ViewId: /order.jsp][Class: javax.faces.component.html.HtmlForm,Id: pf][Class: javax.faces.component.html.HtmlSelectOneMenu,Id: _idJsp11][Class: javax.faces.component.UISelectItems,Id: _idJsp12]} does not reference an Object of type SelectItem, SelectItem[], Collection or Map but of type : null
old working JSP code:
<h:selectOneMenu value="#{order.deliveryType}" immediate="true">
<f:selectItems value="#{order.deliveryTypes}" />
</h:selectOneMenu>
new not working JSP code:
<c:forEach var="spot" items="${order.spots}">
<h:selectOneMenu value="#{spot.deliveryType}" immediate="true">
<f:selectItems value="#{spot.deliveryTypes}" /> <%-- Works as empty list if this line removed --%>
</h:selectOneMenu> <c:out value="${spot.name}"/><br/>
</c:forEach>
New field was introduced List<Spot> spots as well as getter and setter. List<SelectItem> getDeliveryTypes() has been moved from managed bean class Order to class Spot.
How to access to spot.deliveryTypes? Changing # to $ didn't help because value= doesn't accept EL.
MyFaces 1.1.8
Thanks.
JSTL and JSF doesn't go nicely hand in hand. The JSP won't be processed from top to bottom as you'd expect from the coding. It's more so that JSTL processes the JSP from top to bottom first and then hands the generated result over to JSF for its own processing from top to bottom. This makes especially the c:forEach unusable for this kind of requirements. In this particular case, the ${spot} won't be there anymore when it's JSF's turn to process the JSP page.
You'd like to use a JSF UIData based component instead of c:forEach. A fullworthy JSF alternative to the c:forEach is Tomahawk's t:dataList. Use it and your problem will be solved.
If it happens that you're using Facelets instead of JSP, you can also use its ui:repeat instead.

Passing data from request ManagedBeans in JSF

I'm somewhat confused about the lifecycle of ManagedBeans of type "request".
In this example i'm using one request bean "userBean" to fill this page and one request bean "userEditBean" to fill the following edit page.
<h:form>
<h:panelGrid border="1" columns="2">
<h:outputText value="Name" />
<h:outputText value="#{userBean.user.name}" />
...
</h:panelGrid>
<h:commandButton value="Edit" action="edit" actionListener="#{userEditBean.init}">
<f:attribute name="user" value="#{userBean.user}"/>
</h:commandButton>
</h:form>
When i press the Edit button a userEditBean is created but the attribute map resolves "user" to null.
Does this mean that the attribute EL is resolved after the userBean has already been destroyed? How can i pass values from incoming beans to outgoing beans?
You're setting the attribute value with an expression, not a static value. Whenever you request the value, the expression will be re-evaluated again. The userBean.user apparently isn't present in the subsequent request. You need to ensure that it is there (in other words, the constructor of the userBean should ensure that the user is been created and set.
There are however alternatives. One of the best is to use Tomahawk's <t:saveState> for that. Add it somewhere in the page:
<t:saveState value="#{userBean.user}" />
That said, I agree with Bozho that the whole approach is a bit strange, but that's another story. You may however get lot of useful ideas out either of the following articles: Communication in JSF and/or Using Datatables. Good luck.
request scope means the bean lives during one request. And you fill your edit page (1st request), and send the edited user (2nd request).
In addition to that, <f:attribute> sets tha attributes in the parent component, not in the request. So in your code the attributes will be found in the button.getAttributes() (if you have bound your button).
Furthermore, it is strange to have an actionListener method named init. Since you don't need the event, you can set the action to be the method which will do the editing operation, and make that method return the navigation-rule you want.

Categories

Resources