I want to process this form (valueChangueListener is not valid in real case).
This is the back bean:
public class TestBean extends PrivateBaseBean implements Serializable {
private List<String> strings;
#PostConstruct
public void init() {
strings = new ArrayList<String>();
strings.add("");
strings.add("");
strings.add("");
}
public void saveAction(ActionEvent event) {
StringBuilder textToShowInMessage = new StringBuilder();
for (String string : strings) {
textToShowInMessage.append(string);
textToShowInMessage.append("; ");
}
FacesMessage msg = new FacesMessage(super.getBundle().getString(
textToShowInMessage.toString()), "");
FacesContext.getCurrentInstance().addMessage(null, msg);
}
getters... setters...
An the view:
....
<h:form>
<ui:repeat var="string" value="#{testBean.strings}">
<h:inputText value="#{string}" />
<br />
</ui:repeat>
<p:commandButton value="#{msg.save}"
actionListener="#{testBean.saveAction}" icon="ui-icon-disk"
update="#form" />
</h:form>
...
When the form is processed in the back bean string list always is blank.
How to process form intput's inside iteration, without any value changue listener?
There are some screenshots:
The same problem occurs with action or actionListener on
Your problem is not connected with PrimeFaces <p:commandButton>'s behaviour, but rather with a scoping problem that is implicilty created when using the <ui:repeat> tag.
First of all, let's depart from your example. Basically, you've got
<ui:repeat value="#{bean.strings}" var="s">
<h:inputText value="#{s}"/>
</ui:repeat>
with the backing List<String> strings.
The culprit is here: value="#{s}". The exported by <ui:repeat> variable s is visible only within its loop and it is not bound to any managed bean's property, but instead only to a local variable. Put it differently, s is not bound/equal to bean.strings[index] as one would expect and has no knowledge, as we see, where it originated from. So basically, you're off with a unilateral relationship: value from the bean is printed in your input properly, but the reverse is not happening.
The workarounds
Workaround #1: wrapper classes / model objects
The situation can be overcome by using a wrapper object for your class. In case of a string it could be a 'simple mutable string', like below:
public class MString {
private String string;//getter+setter+constructor
}
In this case the iteration will be working as predicted:
<ui:repeat value="#{bean.mstrings}" var="ms">
<h:inputText value="#{ms.string}"/>
</ui:repeat>
with the backing List<MString> mstrings.
Note that if you have your model class, like User, and will change its properties within <ui:repeat> the class itself will be effectively a wrapper, so that the properties will be set appropriately.
Workaround #2: chained property access
Another workaround consists of accessing an element of your collection directly from within a <h:inputText> tag. This way, any such property will be set by accessing the bean, then collection, then setting the property at the desired index. Excessively long, but that's how it is. As to the how question, <ui:repeat> provides for an exported current iteration status variable, varStatus, that will be used to access the array/collection in the managed bean.
In this case the iteration will also be working as predicted:
<ui:repeat value="#{bean.strings}" var="s" varStatus="status">
<h:inputText value="#{bean.strings[status.index]}"/>
</ui:repeat>
with the ordinary backing List<String> strings.
My workaround solution take the value directly from the page:
<ui:repeat id="repeat" value="#{bean.strings}" var="s" varStatus="status">
<h:inputText id="x" value="#{s.field}"/>
<h:commandLink style="margin: .5em 0" styleClass="commandLink" actionListener="#{bean.save(status.index)}" value="#{bundle.Send}"/>
</ui:repeat>
The save method:
public void save(String rowid) {
String jsParam = Util.getJsParam("repeat:" + rowid + ":x");
System.out.println("jsParam: " + jsParam); //persist...
}
The getJsParam method:
public static String getJsParam(String paramName) {
javax.faces.context.FacesContext jsf = javax.faces.context.FacesContext.getCurrentInstance();
Map<String, String> requestParameterMap = jsf.getExternalContext().getRequestParameterMap();
String paramValue = requestParameterMap.get(paramName);
if (paramValue != null) {
paramValue = paramValue.trim();
if (paramValue.length() == 0) {
paramValue = null;
}
}
return paramValue;
}
Related
What am I not doing right?
#Named("utilityController")
#RequestScoped
public class UtilityController {
public DataModel<Result> getResultSample() {
Result[] resultSample = new Result[11];
//Populate the array
return new ArrayDataModel<>(resultSample);
}
}
In The JSF:
<h:dataTable id="sampleResult" value="#{utilityController.resultSample}" var="item" styleClass="table table-bordered table-striped table-hover table-condensed" >
<h:column>
<f:facet name="header">SN</f:facet>
#{utilityController.resultSample.rowIndex}
</h:column>
<h:column>
<f:facet name="header">Subject</f:facet>
#{item.subject.name}
</h:column>
....
</h:dataTable>
The rowIndex always returns 0 as can be seen above. Please can some help me pin point what I am doing wrongly
What am I not doing right?
Creating the model in a getter method. Never do that. All getter methods should look like this:
public DataModel<Result> getResultSample() {
return resultSample;
}
The getter method is invoked on every iteration round. You're basically clearing out the model from the previous iteration round and returning a brand new one, with all state (such as current row index) reset to default value.
Move that job to bean's #PostConstruct method.
private DataModel<Result> resultSample;
#PostConstruct
public void init() {
Result[] results = new Result[11];
// ...
resultSample = new ArrayDataModel<Result>(results);
}
public DataModel<Result> getResultSample() {
return resultSample;
}
As to your concrete functional requirement, you can also just reference UIData#getRowIndex() without wrapping the value in a DataModel.
public Result[] getResults() { // Consider List<Result> instead.
return results;
}
<h:dataTable binding="#{table}" value="#{bean.results}" var="result">
<h:column>#{table.rowIndex + 1}</h:column>
<h:column>#{result.subject.name}</h:column>
</h:dataTable>
Note that I incremented it with 1 as it's 0-based while humans expect an 1-based index.
See also:
How and when should I load the model from database for h:dataTable
Why JSF calls getters multiple times
How does the 'binding' attribute work in JSF? When and how should it be used?
I'm wondering what the best practices are to pass data (an object) between two ViewScoped beans.
They need to be view scoped because of the problem that's brilliantly explained here (to put it short: In both views I'm using a h:commandLink from within a h:dataTable which requires the data model to still be present when submitting).
My problem now is that clicking the link also navigates to a new view, so using the following code, my object gets passed but the DetailViewController instance gets killed right after that and a new one is created when the view changes (as you would expect).
View:
<h:dataTable value="#{searchController.dataModel}" var="item">
...
<h:column>
<f:facet name="header">Action</f:facet>
<h:commandLink id="open" value="open" action="#{searchController.showDetail(item)}" />
</h:column>
</h:dataTable>
Bean:
#ManagedBean
#ViewScoped
public class SearchController {
#ManagedProperty(value="#{detailViewController}")
private DetailViewController detailViewController;
// getters, setters, etc. ...
public String showDetail(Item i) {
detailViewController.setItem(i);
return "view_detail.xhtml";
}
}
How would you solve this? I thought about putting the object inside Flash: FacesContext.getExternalContext.getFlash()... Is there an easier or more elegant solution?
You can use view parameters. (See How do you pass view parameters when navigating from an action in JSF2?)
Typically, your method return the url with query parameters:
public String showDetail(Item i) {
return "view_detail.xhtml?id="+i.getId();
}
And in your view_detail.xhtml file, you add a f:viewParam tag evaluating to on of your bean field:
<f:metadata>
<f:viewParam name="id" value="#{myBean.id}" />
</f:metadata>
Then from your backing bean, you use that field to get your Item instance in your #postConstruct method.
If you don't use the f:viewparam tag, you can also fetch the request parameters to obtain the id.
private String id;
private Item item;
#PostConstruct
public void init() {
if (id != null) {
item = fetchItem(id);
} else {
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
Map<String, String> requestParameterMap = externalContext.getRequestParameterMap();
if (requestParameters.containsKey("id")) {
id = requestParameters.get("id");
item = fetchItem(id);
} else {
throw new WebServiceException("No item id in request parameters");
}
}
}
I tried to ask a more specific question but I think i may have been overly specific so I'll zoom out to get a better advice.
I'm trying to create a composite/custom component that will accept two attributes
A list of strings --> names of fields
a list of lists of <String, Value> --> groups of fields with <fieldName, fieldValue>
example values:
[Street, Country, State, ZipCode]
[{(Street, Greenfield), (Country, USA)}, {(Country, Canada), (ZipCode, 3333)}]
The component will render this, based on the attributes:
The reason I am having difficulties with this component is that I don't know what's the proper way to maintain placeholders for fields that were not entered originally but can be added by the user through the component.
In the above example, for the first set, these would be State and ZipCode.
My Idea was to create a dummy object with all the fields and on submittion copy the dummy object's values to the data structure passed in the attributes.
The problem I was facing was not knowing how to read the values on component creation and altering the List passed through the attribute on submission.
I will add sample code soon (Although that shouldn't be a crucial for answering this question)
Thank you just for reading this far!! :-)
My Code (Again, not required for answering the question but may be helpful to understand my challenge)
The Composite Component Code:
<cc:interface componentType="dynamicFieldList">
<cc:attribute name="styleClass" default="fieldType" />
<cc:attribute name="form" default="#form"
shortDescription="If used, this is the name of the form that will get executed" />
<cc:attribute name="list" type="java.util.List" required="true"
shortDescription="The values of the list. The type must be List of FieldGroup" />
<cc:attribute name="groupTypes" type="java.util.List" required="true"
shortDescription="The types that will be available to choose from for each field. The type of this must be List of FieldType" />
</cc:interface>
<cc:implementation>
<h:dataTable id="table" value="#{cc.model}" var="fieldGroup">
<h:column>
<ui:repeat var="field" value="#{fieldGroup.values}">
<utils:fieldType value="#{field.value}" type="#{field.type}"/>
</ui:repeat>
</h:column>
<h:column>
<h:commandButton value="delete" action="#{cc.remove}">
<f:ajax render="#{cc.attrs.form}" execute="#{cc.attrs.form}" />
</h:commandButton>
</h:column>
</h:dataTable>
<h:commandButton value="add" action="#{cc.add}">
<f:ajax render="#{cc.attrs.form}" execute="#{cc.attrs.form}" />
</h:commandButton>
</cc:implementation>
The component bean java code:
#FacesComponent(value = "dynamicFieldGroupList")
// To be specified in componentType attribute.
#SuppressWarnings({ "rawtypes", "unchecked" })
// We don't care about the actual model item type anyway.
public class DynamicFieldGroupList extends UIComponentBase implements NamingContainer
{
private transient DataModel model;
private List<FieldGroup> displayList;
public DynamicFieldGroupList()
{
super();
List<FieldGroup> list = new ArrayList<FieldGroup>();
for (FieldGroup group : getList()){
FieldGroup currGroup = new FieldGroup("Untitled");
//Assumption - Each type may exist only once in a group.
Map<FieldType, FieldDetail> hash = new HashMap<FieldType, FieldDetail>();
for (FieldDetail detail: group.getDetails()){
hash.put(detail.getType(), detail);
}
// While creating the dummy object, insert values the exist or placeholders if they don't.
for (FieldType type : getGroupTypes()){
if (hash.containsKey(type)){
currGroup.addDetail(hash.get(type));
} else {
currGroup.addDetail(new FieldDetail(type,null));
}
}
list.add(currGroup);
}
// Assign the created list to be the displayed (dummy) object
setDisplayList(list);
}
public void add()
{
// Add a new group of placeholders
FieldGroup group = new FieldGroup("Untitled");
for (FieldType type: getGroupTypes()){
group.addDetail(new FieldDetail(type, null));
}
getList().add(group);
}
public void remove()
{
getDisplayList().remove(model.getRowData());
}
#Override
public String getFamily()
{
return "javax.faces.NamingContainer"; // Important! Required for
// composite components.
}
public DataModel getModel()
{
if (model == null)
model = new ListDataModel(getDisplayList());
return model;
}
private List<FieldGroup> getList()
{ // Don't make this method public! Ends otherwise in an infinite loop
// calling itself everytime.
return (List) getAttributes().get("list");
}
private List<FieldType> getGroupTypes()
{ // Don't make this method public! Ends otherwise in an infinite loop
// calling itself everytime.
return (List) getAttributes().get("groupTypes");
}
public void setDisplayList(List<FieldGroup> displayList)
{
this.displayList = displayList;
}
public List<FieldGroup> getDisplayList()
{
return displayList;
}
}
You can use a Map to hold the values for each group.
Map<String, Object> values = new HashMap<String, Object>();
Assuming that you've all those maps in a List<Map<String, Object>> and the field names in a List<String>, then you can get/set them all basically as follows
<ui:repeat value="#{allValues}" var="values">
<ui:repeat value="#{fieldNames}" var="fieldName">
<h:outputLabel value="#{fieldName}" />
<h:inputText value="#{values[fieldName]}" />
<br/>
</ui:repeat>
</ui:repeat>
I understand I cannot use <c:choose> within a component in a jsf page. I am trying to see if there is an alternative. I looked at the Tomahawk and that isn't what I really need. I am trying to validate negative and positive numbers in a column. I want to be able to choose between the 2 validator tags that I have created. I tried using the rendered attribute but it still doesn't work. Below is kind of what I am looking for but it is not working like I want it to. Does anyone have any suggestions??
Thanks in advance.
<c:choose>
<c:when test="#{entry.dataEntry.posValue}">
<f:validator validatorId="hits.positiveNumberValidator"/>
</c:when>
<c:otherwise test="#{entry.dataEntry.negValue}">
<f:validator validatorId="hits.negativeNumberValidator"/>
</c:otherwise>
</c:choose>
Wrap in another validator and add them as attributes.
<f:validator validatorId="hits.numberValidator"/>
<f:attribute name="posValue" value="#{entry.dataEntry.posValue}" />
<f:attribute name="negValue" value="#{entry.dataEntry.negValue}" />
And then in the NumberValidator:
Boolean negValue = component.getAttributes().get("negValue");
if (posValue != null && posValue) {
new PositiveNumberValidator().validate(context, component, value);
}
Boolean posValue = component.getAttributes().get("posValue");
if (negValue != null && negValue) {
new NegativeNumberValidator().validate(context, component, value);
}
Note that this doesn't work when #{entry} is actually an iterated item like as declared in var attribute of h:dataTable or ui:repeat, because the f:attribute is tied to the JSF component, not to its output. Since the variable name #{entry} hints less or more that this is actually the case, here's how you could do it.
Wrap the collection in a DataModel:
private DataModel entries;
public Bean() {
entries = new ListDataModel(someDAO.list());
}
// ...
Use it in h:dataTable or ui:repeat as follows:
<h:dataTable value="#{bean.entries}" var="entry">
<h:column>
<h:inputText validator="#{bean.numberValidator}" />
</h:column>
</h:dataTable>
And implement the validator in the Bean as follows:
public void numberValidator(FacesContext context, UIComponent component, Object value) throws ValidatorException) {
Entry entry = (Entry) entries.getRowData();
if (entry.isPosValue()) {
new PositiveNumberValidator().validate(context, component, value);
}
if (entry.isNegValue()) {
new NegativeNumberValidator().validate(context, component, value);
}
}
(you may want to make those validators an instance variable of the bean instead (only if they are threadsafe))
can some one help me with the following JSF dataTable? here I am getting data from database table and I used dataTable binding, but I don't know why it displays the rows 3 times in the screen, but if I remove the binding then it displays only one time.
<h:dataTable binding="#{threadController.dataTable}" var="category" value="#{threadController.queryCategories}" border="1" cellpadding="2" cellspacing="0">
<h:column>
<img src="../../images/directory.jpg" alt="Forum Icon" />
</h:column>
<h:column>
<h:form>
<h:commandLink value="#{category.cname}" action="#{threadController.categoryDateItem}" />
</h:form>
</h:column>
// defined globally
private HtmlDataTable dataTable;
private HtmlInputHidden dataItemId = new HtmlInputHidden();
public String categoryDateItem() {
category = (Category) dataTable.getRowData();
System.out.println("category action by select: "+category.getCname());
dataItemId.setValue(category.getId());
return "editItem"; // Navigation case.
}
#SuppressWarnings("unchecked")
public ArrayList<Category> getQueryCategories(){
return (ArrayList<Category>)HibernateUtil.getSession().createCriteria(Category.class).list();
}
output:
myText myText myText
The binding expression to bind this component to the bean value="#{threadController.queryCategories}".So value attribute is sufficient to retrieve data using dataTable tag.
Binding = component backing bean
Value= data model backing bean
So, you have the Value and Binding set correctly (at least, as far as I can see). Your problem may result from the fact that you're not caching the list you're getting back from the database in getQueryCategories().
You really can't have any idea how often getQueryCategories() will be called in the process of rendering that dataTable, so it's a good idea to do something like this:
// Somewhere near the top of the handler class.. create a cache variable:
private ArrayList<Category> qCategories = null;
// now for getQueryCategories
public ArrayList<Category> getQueryCategories(){
if ( qCategories == null ) { // qCategories should be a member of the handler
qCategories = (ArrayList<Category>)HibernateUtil.getSession().createCriteria(Category.class).list();
}
return qCategories
}
This kind of cache-ing is very helpful in JSF apps with handlers that are session of even request scoped, as again you can't really know how often JSF will evaluate your "value" expression in the dataTable.