how to pass object from richface Datatable to actionlistener - jsf

I am passing an object from richface datatable like:
<rich:column>
<a4j:commandLink
value="Transfer inside Group"
actionListener="#{adminBean.init_machineTransferInsideGroup}"
reRender="transferInsideGroupMachinePanel"
oncomplete="#{rich:component('transferInsideGroupMachinePanel')}.show()">
<f:setPropertyActionListener
target="#{adminBean.machineToChange}"
value="#{Machineassg3}" />
</a4j:commandLink>
<rich:column>
What i am expecting from above code, when command link is clicked:
assign object (Machineassg3) to bean's variable (adminBean.machineToChange)
then invoking actionlistener (init_machineTransferInsideGroup)
after that richmodal panel
But steps that are happening is :
Invoking actionlistener (init_machineTransferInsideGroup)
assign object (Machineassg3) to bean's variable (adminBean.machineToChange)
after that richmodal panel
How to do expected steps (means assigning first and then invoking actionlistener)

You should do the business job in action instead of actionListener. The action listener is intented to hold self-contained logic to prepare/preprocess the real business action and/or to log something, not to do the business job.
So, replace
actionListener="#{adminBean.init_machineTransferInsideGroup}"
by
action="#{adminBean.init_machineTransferInsideGroup}"
and remove the ActionEvent argument from the init_machineTransferInsideGroup() method. The action will be invoked after all action listeners (also the <f:setPropertyActionListener> one) have done its job.
See also:
Differences between action and actionListener

You could get the rows variable with a ValueExpression.
Lets say you have in the dataTable declaration the attribute var="machine", then in the managed bean's action method you could get like so
FacesContext fCtx = FacesContext.getCurrentInstance();
ELContext elCtx = fCtx.getELContext();
ExpressionFactory ef = fCtx.getApplication().getExpressionFactory();
ValueExpression ve = ef.createValueExpression(elCtx, "#{machine}", Machineassg3.class);
machineassg3 = (Machineassg3)ve.getValue(elCtx);
I don't know the actual class of the machineassg3 variable and so I just had it like Machineassg3.
I hope it helps.

Related

JSF input processing order

Is there a way to specify the order in which the inputs should be set after a submit?
Here is my case:
<h:inputText id="fieldA" value=#{myBean.myObject.fieldA}" />
<h:inputText id="fieldB" value=#{myBean.myObject.fieldB}" />
<p:autoComplete id="myObject" value=#{myBean.myObject" converter="myObjectConverter" />
<h:inputText id="fieldC" value=#{myBean.myObject.fieldD}" />
<h:inputText id="fieldD" value=#{myBean.myObject.fieldC}" />
The issue I am encountering is that, as the inputs are processed in the ordered they are declared, fieldA and fieldB are set in the initial instance of myObject, then myObject is set (with a new instance thus filedA and fieldB values are lost), and finally fieldC and fieldD are set with no problem.
If I could manage to start by setting myObject first, that would solve my problem.
I will temporarily set the fields and myObject into two different attributes of my bean, and populate myObject after clicking a save button. But it looks more like a hack than a real solution.
Needless to say that declaring the autocomplete before the inputtexts is not an option.
Thanks in advance.
In shortcut:
You can use <p:inputText> tag from primefaces. Then, you can disable all inputs. Add ajax to your autoComplete, and update other inputs after processing autoComplete. Inputs disable attribute can be set to depend on whether the autoComplete is not null. This way you will make the user to enter the autoComplet first.
you can try to set immediate="true" to p:autocomplete, so that it will be processed in the APPLY_REQUEST_VALUES phase, before all other components.
The simple solution is to update h:inputTexts when p:autocomplete item is selected to reflect its values:
<p:autoComplete id="myObject" value="#{myBean.myObject}" ...>
<p:ajax event="itemSelect" process="#this" update="fieldA fieldB fieldC fieldD" />
</p:autoComplete>
but this reverts user inputs on h:inputTexts. And since you can't move p:autocomplete on top, probably this is not acceptable too.
In case you can't/don't want to use ajax, you can force an early model update:
<p:autoComplete id="myObject" value="#{myBean.myObject}" immediate="true"
valueChangeListener="#{component.updateModel(facesContext)}" ... />
but, in my opinion, this is not very user friendly...
P.S. this time it's tested :)
There's no pretty way to get around this; your situation is already less than ideal and is hacky (re: not being able to simply reorder the fields). One workaround is for you to set fieldA and fieldB as attributes of myObject. In the converter, you could then pull the values off the components. Observe
Set attributes thus
<h:inputText id="fieldA" binding=#{fieldA}" />
<h:inputText id="fieldB" binding=#{fieldB}" />
<p:autoComplete id="myObject" value=#{myBean.myObject" converter="myObjectConverter">
<f:attribute name="fieldA" value="#{fieldA}"/>
<f:attribute name="fieldB" value="#{fieldB}"/>
</p:autoComplete>
The binding attribute effectively turns those components into page-scoped variables, allowing you to then pass them as attributes on your p:autocomplete
Get the values of those variables in your converter
//Retrieve the fields and cast to UIInput, necessary
//to retrieve the submitted values
UIInput fieldA = (UIInput) component.getAttributes().get("fieldA");
UIInput fieldB = (UIInput) component.getAttributes().get("fieldB");
//Retrieve the submitted values and do whatever you need to do
String valueA = fieldA.getSubmittedValue().toString();
String valueB = fieldB.getSubmittedValue().toString();
More importantly, why can't you just reorder the fields/logical flow of your form? You can avoid all this nasty business if you did

How do I set a selectiOneRadioButton-value inside an ui:repeat depending on the iterated entity?

in my usecase I have a (Primefaces) selectOneRadio. This is within an ui:repeat. Values of selectOneRadio are displayed fine. I want each selectOneRadio to have a useful default-value, depending on the iterated entity. This works fine, too, default is selected. But with my approach, I am not able to set the value of the selectOneRadio, as an exception rises:
javax.el.PropertyNotWritableException: [...] value="#{orderBean.getProductPriceId(product)}": Illegal Syntax for Set Operation
How do I set a selectiOneRadioButton-value inside an ui:repeat depending on the iterated entity?
OrderBean:
public class OrderBean {
private String productPriceId; // + getter and setter
public String getProductPriceId(final Product product) {
return product == null ? "" : product.getPricesAsList().get(0).getId().toString();
}
}
xhtml:
<ui:repeat var="product" value="...">
<p:selectOneRadio value="#{orderBean.getProductPriceId(product)}">
...
</p:selectOneRadio>
</ui:repeat>
This makes indeed no sense. You need to make sure that the model matches the view without any need for additional business logic.
Just use
<p:selectOneRadio value="#{product.priceId}">
and give the default item a value of null instead of "" so that it matches.

JSF PrimeFaces inputText inside dataTable

JSF-2.0, Mojarra 2.1.19, PrimeFaces 3.4.1
Summary of the problem: Have a p:inputText inside p:dataTable and inputText action fired by p:remoteCommand which passes the dataTable row index as a parameter with f:setPropertyActionListener. But it always passes the last row of the dataTable, not the index of the row which includes currently clicked p:inputText.
As it can be seen from my previous questions, I am trying to use p:inputText as a comment taker for a status like in Facebook or etc. Implementation includes a p:dataTable. It's rows represents each status. Seems like:
<p:dataTable id="dataTable" value="#{statusBean.statusList}" var="status"
rowIndexVar="indexStatusList">
<p:column>
<p:panel id="statusRepeatPanel">
<p:remoteCommand name="test" action="#{statusBean.insertComment}"
update="statusRepeatPanel">
<f:setPropertyActionListener
target="#{statusBean.indexStatusList}"
value="#{indexStatusList}">
</f:setPropertyActionListener>
</p:remoteCommand>
<p:inputText id="commentInput" value="#{statusBean.newComment}"
onkeypress="if (event.keyCode == 13) { test(); return false; }">
</p:inputText>
</p:panel>
</p:column>
</p:dataTable>
Upper code says when the press enter key, fire p:remoteCommand which calls the insert method of the managed bean.
#ManagedBean
#ViewScoped
public class StatusBean {
List<Status> statusList = new ArrayList<Status>();
public int indexStatusList;
public String newComment
//getters and setters
public void insertComment() {
long statusID = findStatusID(statusList.get(indexStatusList));
statusDao.insert(this.newComment,statusID)
}
Let's debug together; assuming there are three statuses shown in the p:dataTable, click in the p:inputText which in the second status(index of 1), type "relax" and press the enter key.
In the debug console, it correctly shows "relax", but it finds the wrong status because indexStatusList has the value of 2 which belongs the last status in the p:statusList. It must be 1 which is the index of p:inputText that clicked on the dataTable row.
I think problem is about p:remoteCommand which takes the last index on the screen.
How it works?
Let's imagine there is a p:commandLink instead of p:remoteCommand and p:inputText:
<p:commandLink action=#{statusBean.insertComment>
<f:setPropertyActionListener target="#{statusBean.indexStatusList}"
value="#{indexStatusList}"></f:setPropertyActionListener>
This component successfully passes the indexStatusList as currently clicked one.
Conceptual problem in this solution lies in way how p:remoteCommand works. It creates JavaScript function whose name is defined in name attribute of p:remoteCommand. As you putted this in dataTable it will iterate and create JavaScript function called test as many times as there is rows in this table, and at the end last one will be only one. So, solution can be in appending index at the name of the remoteCommand but that is bad, because you will have many unnecessary JavaScript functions. Better approach would be to create one function an pass argument to it. So define remoteCommand outside of datatable:
<p:remoteCommand name="test" action="#{statusBean.insertComment}" update="statusRepeatPanel">
and call test function like this in your onkeypress event:
test([{ name: 'rowNumber', value: #{indexStatusList} }])
This will pass rowNumber parameter in your AJAX request. In backing bean's insertComment() method you can read this parameter and do with it anything you want:
FacesContext context = FacesContext.getCurrentInstance();
Map map = context.getExternalContext().getRequestParameterMap();
Integer rowNumber = Integer.parseInt(map.get("rowNumber").toString());
NOTE: as you are updating panel in each row, maybe you can change update attribute of remoteCommand to #parent so this will work for all rows.
EDIT: You can update the specific panel in specific row with following code in Java method:
RequestContext.getCurrentinstance().update("form:dataTable:" + rowNumber + ":statusRepeatPanel")

How to save h:inputText values of a h:dataTable? My attempt only saves the value of last row

I'm having trouble making a dataTable where each row has a inputText and a commandLink. When the link is clicked, only it's row's inputText's data is submitted.
Something like this?
<h:dataTable value="#{bean.items}" var="item">
<h:column>
<h:inputText value="#{bean.value}"/>
</h:column>
<h:column>
<h:commandLink action="#{bean.save}" value="save">
<f:setPropertyActionListener target="#{bean.item}" value="#{item}" />
</h:commandLink>
</h:column>
</h:dataTable>
Bean:
#RequestScoped
public class Bean {
private Item item;
private String value;
Right now, as it is, it's using the last row's inputText to fill the value. I wrapped another h:form, but it broke other things and I've learned that nested h:form is not the right way to do it hehe
What's the correct way to do this?
Thanks.
You're binding the value of all HTML input elements to one and same bean property. This is of course not going to work if all those HTML input elements are inside the same form. All values are subsequently set on the very same property in the order as the inputs appeared in the form. That's why you end up with the last value. You'd like to move that form to inside the <h:column> (move; thus don't add/nest another one).
The usual approach, however, would be to just bind the input field to the iterated object.
<h:inputText value="#{item.value}"/>
An alternative, if you really need to have your form around the table, is to have a Map<K, V> as bean property where K represents the type of the unique identifier of the object behind #{item} and V represents the type of value. Let's assume that it's Long and String:
private Map<Long, String> transferredValues = new HashMap<Long, String>();
// +getter (no setter necessary)
with
<h:inputText ... value="#{bean.values[item.id]}" />
This way you can get it in the action method as follows:
String value = values.get(item.getId());
By the way, if you happen to target Servlet 3.0 containers which supports EL 2.2 (Tomcat 7, Glassfish 3, etc), then you can also just pass the #{req} as a method argument without the need for a <f:setPropertyActionListener>.
<h:commandLink ... action="#{bean.save(item)}" />
See also:
How and when should I load the model from database for h:dataTable
How can I pass selected row to commandLink inside dataTable?
How to dynamically add JSF components

Dynamic Action in JSF page

I have a JSF page. My CommandButton action method value is dependent on the bean variable value.
Example:
Bean headerBean has varaible actionValue with value "someBean.doAction1()"
When I use , It says headerBean.actionValue is not a method which is right.
How can I get the action value as "someBean.doAction1" instead of headerBean.actionValue.
Thanks,
You can use the brace notation for that.
<h:commandButton value="submit" action="#{someBean[headerBean.actionValue]}" />
When the #{headerBean.actionValue} returns a String of for example doAction1, then this will effectively invoke #{someBean.doAction1}.
If the bean name to be called is currently actually in the actionvalue (headerBean.actionValue returning someBean.doAction1), you need to split it into a field that returns the bean name and one that returns the method name and then use
<h:commandButton value="submit" action="#{requestScope[headerBean.beanName][headerBean.actionValue]}" />
If headerBean.beanName returns 'someBean' and headerBean.actionValue returns doAction1 the above will call #{somebean.doAction1}.

Resources