Primefaces: OrderList fills backend list with String instead of Object - jsf

In my backend view, I have a list of some POJO:
BackingView.java
#Getter #Setter private List<SomePOJO> pojoList = ...
SomePojo.java
#EqualsAndHashCode(callSuper = false, of = {"id"})
public class SomePojo implements Serializable {
private static final long serialVersionUID = 1L;
#Getter #Setter private Long id;
#Getter #Setter private Long date;
#Getter #Setter private Date name;
....
#Override
public String toString() {
return String.format("%s[id=%s]", getClass().getSimpleName(), id);
}
//making a readable string-representation of the object to display on the orderList
public String toStringOrderlistDisplay() {
return "Pojo with id " + id + "and so on... "
}
This list acts as you would expect in all regards. Recently, I wanted to add a feature to my frontend which allows the user to manually order this list. For that purpose I use Primefaces OrderList, like this:
Frontend.xhtml
<p:orderList id="ordList" widgetVar="ordList"
value="#{backingview.pojoList }" var="rfg"
controlsLocation="left" responsive="true" itemValue="#{rfg}"
itemLabel="#{rfg.toStringOrderlistDisplay()}">
</p:orderList>
As soon as I add the above p:orderList to my .xhtml file, I can see when inspecting in debug mode that pojoList no longer contains instances of PojoClass but rather plain strings (to be more precise, the string representation of the PojoClass-Object (What the toString() method would return)).
If I simply remove the orderList this problem does not occur. What is going on here? How is such a thing even possible in Java?

I tried using a converter as shown here CountryConverter.java, and it worked.
Maybe when you don't specify a convertor, it uses a default .toString() implementation and can't serialize back to a PojoClass.
You can submit an issue here to see if it's a bug.

Related

How to save an entity for operations such as voting

let's take a youtube video page for example. after the page is rendered. you can upvote or downvote the video, and comment.
I'm having a similar case here. where an article is displayed with it's title and body(text). and I want to add the option for user so that they can vote. Two Entity looks like this:
public class Article implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private BigInteger id;
private User from;
private String title;
private String body;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "article")
private List<VoteArticle> votes = new ArrayList<>();
public class VoteArticle implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private BigInteger id;
#ManyToOne
#JoinColumn(name = "Article_ID", referencedColumnName = "id")
private Article article;
and I have jsf view that have a view param as the article's id article.xhtml?t=4
before rendering the view I look up and setup the article in the RequestScoped Bean (ArticleBean), then The view is rendered.
now as we all know since the bean is #RequestScoped all fields are re-initiliazed for every next request. So My question is, Do I need to get the article entity from the database every time I need to make an operation (voting, commenting) or is there a better way?
Right now my solution is to take the parameter view ".xhtml?t" and make a new Article entity, set its Id and use it.
Is something wrong with my solution? how do you implement such case? Thanks.
It sounds like setting your controller code to #ViewScoped instead of #RequestScoped would solve your problem.
You could make a method init() and give it a #PostConstruct annotation. Within init() you can pull your Article object from the database. Because the controller is view scoped these objects will stay until the user leaves the page.
You can call methods like voteUp() voteDown() on your ViewScoped controller via ajax calls and modify your Article object without having to pull it fresh from the DB each time.

primefaces5 jsf2 multiple upload e scan the elements

Hi to all i have a problem with an upload of more files in PrimeFaces/Jsf2
I see http://www.primefaces.org/showcase/ui/file/upload/multiple.xhtml
When the method intercept the event associated i set
UploadedFile fileUpload = event.getFile();
and i want to scan every file uploaded with the implementation of List
InputStream input;
input = event.getFile().getInputstream();
pojo.setFileInputStream(input);
input.close();
fileTableList.add(pojo);
But the great problem is that this list contain only one file uploaded.
How can i take every file uploaded from UploadedFile event?
What's wrong?
Thank you for the answers
But the great problem is that this list contain only one file
uploaded. How can I take every file uploaded from UploadedFile
event?
This cannot be reproduced with a minimal example with least possible dependencies / resources unless you explicitly state with a minimum reproducible example.
Create a utility class like the following (the class is fully dependent upon the requirement).
public class FileUtil implements Serializable {
private InputStream inputStream; // One can also use byte[] or something else.
private String fileName;
private static final long serialVersionUID = 1L;
public FileUtil() {}
// Overloaded constructor(s) + getters + setters + hashcode() + equals() + toString().
}
The managed bean receiving multiple files :
#Named
#ViewScoped
public class TestBean implements Serializable {
private List<FileUtil> fileList;
private static final long serialVersionUID = 1L;
public TestBean() {}
#PostConstruct
public void init() {
fileList = new ArrayList<>();
}
public void fileUploadListener(FileUploadEvent event) throws IOException {
UploadedFile file = event.getFile();
FileUtil fileUtil = new FileUtil();
fileUtil.setInputStream(file.getInputstream());
fileUtil.setFileName(file.getFileName());
fileList.add(fileUtil);
}
// Bound to a <p:commandButton>.
public void action() {
for (FileUtil fileUtil : fileList) {
System.out.println(fileUtil.getFileName());
}
// Use the list of files here and clear the list afterwards, if needed.
fileList.clear();
}
}
The XHTML file holding only a <p:fileUpload> and a <p:commandButton> just for the sake of demonstration.
<h:form id="form">
<p:fileUpload id="fileUpload"
mode="advanced"
fileLimit="5"
multiple="true"
allowTypes="/(\.|\/)(gif|jpe?g|png)$/"
sequential="true"
process="#this"
fileUploadListener="#{testBean.fileUploadListener}">
</p:fileUpload>
<p:commandButton value="Submit"
process="#this"
update="fileUpload"
actionListener="#{testBean.action}"/>
</h:form>
If you need byte[] in place of InputStream, then just change private InputStream inputStream; in the FileUtil class to byte[] and then use
byte[] bytes = IOUtils.toByteArray(uploadedFile.getInputstream());
to extract a byte array from InputStream (where IOUtils is from org.apache.commons.io. You can also do it manually just by writing a few lines of code).
You can also construct a List<UploadedFile> without creating an additional class like FileUtil as in this example but doing so would mandate a PrimeFaces dependency on the service layer (which should not happen), if you happened to use that layer in your application, since UploadedFile is a PrimeFaces artifact. After all it fully depends upon the requirement.

Access static property in JSF

I have a static List of Select Items in one of my backing beans:
private static List<SelectItem> countries = new ArrayList<SelectItem>();
with the following getters and setters:
public static List<SelectItem> getCountries() {
return countries;
}
public static void setCountries(List<SelectItem> countries) {
LoadSelectItemsBean.countries = countries;
}
I am having trouble with accessing the static List through my XHTML page. The code I have tried is as follows:
<ace:simpleSelectOneMenu id="countryField"
value="#{generalCarrierDataViewBean.carrierBean.countryId}">
<f:selectItems value="#{loadSelectItemsBean.countries}" />
<ace:ajax />
</ace:simpleSelectOneMenu>
The problem line is:
<f:selectItems value="#{loadSelectItemsBean.countries}" />
The exception which results is:
javax.el.PropertyNotFoundException: /pages/GeneralCarrierData.xhtml #394,64 value="#{loadSelectItemsBean.states}": Property 'states' not found on type com.oag.reference.util.LoadSelectItemsBean
Can anbody advise on how to correctly reference a static property from a backing bean?
Thanks
Properties are per definition not static. So getters and setters can simply not be static, although they can in turn reference a static variable. But the outside world does not see that.
You've 3 options:
Remove the static modifier from the getter. The whole setter is unnecessary, you can just remove it.
public List<SelectItem> getCountries() {
return countries;
}
Create an EL function if you really insist in accessing static "properties" (functions). Detail can be found in this answer: How to create a custom EL function to invoke a static method?
Turn the whole List<SelectItem> thing into an enum and make use of OmniFaces <o:importConstants>. Detail can be found in this answer: How to create and use a generic bean for enums in f:selectItems?
Just create a non-static method that returns the static property:
// here you have a static String
private static String static_str;
public static String getStatic_str() {
return static_str;
}
// in jsf page: #{myClass.str}
public String getStr() {
return static_str;
}

Retrieving selectOneMenu complex object as selected item

I'm beginning with JSF (Mojarra 2.2 and Glassfish 4) and currently practicing with a web application which job is to store Clients and their Orders in DB.
When creating a new Order, one feature is to allow choosing an existing client from a JSF <h:selectOneMenu>. An Order entity stores a Client entity among other attributes...
I've followed BalusC's great answer about prepopulating a <h:selectOneMenu> from a DB (here), and have successfully populated mine from data stored in an eager ApplicationScoped ManagedBean, but I can't manage to retrieve the selected item in the backing bean as complex object. It is always null.
This is driving me mad and your help will be truly appreciated!
Here are the relevant code snippets:
#ManagedBean(eager = true)
#ApplicationScoped
public class Data implements Serializable {
private static final long serialVersionUID = 1L;
#EJB
private ClientDao clientDao;
private List<Client> clients;
#PostConstruct
private void init() {
clients = clientDao.lister();
}
public List<Client> getClients() {
return clients;
}
}
Order creation bean (note: 'commande' means order ;)
#ManagedBean
#RequestScoped
public class CreerCommandeBean implements Serializable {
private static final long serialVersionUID = 1L;
private Commande commande;
private String choixNouveauClient = "nouveauClient";
#EJB
private CommandeDao commandeDao;
public CreerCommandeBean() {
commande = new Commande();
}
public void inscrire() {
System.out.println("client : " + commande.getClient()); // prints **NULL**
// ... orderService to store in DB
}
... getters and setters
Client converter:
#FacesConverter(value = "clientConverter", forClass = Client.class)
public class ClientConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null) {
return null;
}
Data data = context.getApplication().evaluateExpressionGet(context, "#{data}", Data.class);
for (Client c : data.getClients()) {
if (c.getId().toString().equals(value)) {
return c;
}
}
throw new ConverterException(new FacesMessage(String.format("Cannot convert %s to Client", value)));
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return (value instanceof Client) ? String.valueOf(((Client) value).getId()) : null;
}
}
Facelet excerpt:
<p:outputPanel id="gridContainerAncienClient">
<p:selectOneMenu value="#{creerCommandeBean.commande.client}"
rendered="#{creerCommandeBean.choixNouveauClient == 'ancienClient'}">
<f:converter converterId="clientConverter" />
<f:selectItems value="#{data.clients}" var="cli"
itemValue="#{cli}" itemLabel="#{cli.prenom} #{cli.nom}" />
</p:selectOneMenu>
</p:outputPanel>
CreerCommandeBean is #RequestScoped. That means it will live only for one request.
When you select a client to be assigned to #{creerCommandeBean.commande.client} you do this by a request. #{creerCommandeBean.commande.client} is now the selected client. Then the request is over, the bean gets destroyed and your "changes" are lost.
When you try to retrieve that data, you do that by a request again: A new instance of CreerCommandeBean is created and the constructor assigns the property commande with a new instance of Commande whose property client again is probably null.
Solution:
Use a broader scope. e.g. #ViewScoped which makes the bean "live" as long as you stay in the same view - no matter how many requests you make.
Tip:
Read BalusC's Post on Communication is JSF 2.0. Parts might be slightly different in JSF 2.2 but it's still a good and comprehensive introduction.
I got stuck with similar problem, only to realize that I forgot to implement equals() and hashCode() method in my Object. Client Class in this case.
I should blame myself for skipping the instructions in BalusC's blog.
"...Please note the Object#equals() implementation. This is very important for JSF. After conversion, it will compare the selected item against the items in the list. As the Object#equals() also require Object#hashCode(), this is implemented as well...."

JSF converter Validation Error: value is not valid for SelectOneMenu UIComponent [duplicate]

This question already has answers here:
Validation Error: Value is not valid
(3 answers)
Closed 6 years ago.
I am using the managedBean userHome in requestScope, in which the entity 'user' is going to be persist.
The user has the leader column which is mapped in ManyToOne relation.My Code looks like this
#ManagedBean
#RequestScoped
public class UserHome {
private User user = new User();
// Getters and Setters
private List<SelectItem> selectItems = new ArrayList<SelectItem>();
public UserHome() {
for(User user: availableLeaders) {
selectItems.add(new SelectItem(user.getName(), user));
}
}
public void persis();
}
User.java
public class User {
#Id
#Column
private Integer id;
#Column
privat String name;
#ManyToOne
private User leader;
}
I am trying to get the value of this leader through h:selectOneMenu like this
<h:selectOneMenu value="#{userHome.user.leader}" converter="userConverter">
<f:selectItems value="#{userHome.selectItems}"/>
</h:selectOneMenu>
My converter looks like this
#FacesConverter(forClass = User.class, value="userConverter")
public class UserConverter implements Converter {
private Map<String, User> userValues = new HashMap<String, User>();
public UserConverter() {
init();
}
#Override
public Object getAsObject(FacesContext context, UIComponent component,
String value) {
return userValues.get(value);
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
System.out.println("RAJASEKARAN "+value);
return ((User)value).getName();
}
public void init() {
UserHome userHome = new UserHome();
for(User user:userHome.availableLeaders()) {
userValues.put(user.getName(), user);
}
}
}
While try to save the user I am getting the error UserEdit:j_idt18: Validation Error: Value is not valid
Adding to BalusC's answer: after the postback, you need to make sure that the User instances are either exactly the same ones as you used for rendering the select items, or that you implement equals for your User class.
The code doesn't show where availableLeaders comes from, but if this is fetched from a DB on-demand, then the converter will not convert to the exact same object instance that's in the list that JSF resolves via #{userHome.selectItems}.
After the conversion, JSF will check whether the converted instance can be found in that list using the equals() method.
You've constructed the SelectItem the wrong way. As per the class' documentation, the 1st argument should represent the item value (which is to be converted and submitted) and the 2nd argument should represent the item label (which is to be displayed in list). But you specified them the other way round.
Fix it accordingly:
selectItems.add(new SelectItem(user, user.getName()));
If that still doesn't fix the problem, then it means that the equals() method of User class is not (properly) implemented. JSF will use it to validate the selected User against any of the item values of the list after conversion.
Unrelated to the concrete problem, it may be useful to know that <f:selectItems> in JSF2 offers you the possibility to build the list without the need to build a list of SelectItem manually. Here's an example which achieves exactly the same:
<f:selectItems value="#{userHome.availableLeaders}" var="user"
itemValue="#{user}" itemLabel="#{user.name}" />
This allows you to get rid of the additional selectItems property and the loop in the bean constructor.

Resources