PrimeFaces AutoComplete error - jsf-2

I am facing a strange problem with p:autoComplete, I get following error
java.lang.NumberFormatException: For input string: "player"
My code is as below
xhtml
<p:autoComplete id="schedChemAC" value="#{testMB.selectedPlayer}" completeMethod="#{testMB.completePlay}" process="#this" var="m" itemLabel="#{m.player}" itemValue="#{m}" converter="#{testConverter}">
<p:ajax event="itemSelect" listener="#{testMB.onSelectFrstL}" process="#this"/>
</p:autoComplete>
MBean
public List<Player> getSelectedPlayer() {
return selectedPlayer;
}
public void setSelectedPlayer(List<Player> selectedPlayer) {
this.selectedPlayer = selectedPlayer;
}
public void getName() {
playerName = playerSession.getAll();
}
public List<Player> completePlay(String query) {
List<Player> suggestion = new ArrayList<Player>();
if (playerName == null) {
getName();
}
for (Player c : playerName) {
if (c.getPlayer().toUpperCase().contains(query.toUpperCase())) {
suggestion.add(c);
}
}
return suggestion;
}
public void onSelectFrstL(SelectEvent event) {
}
Converter
#Named(value = "testConverter")
public class TestConverter implements Converter {
#EJB
PlayerSession playSession;
public static List<Player> playLst;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (playLst == null) {
playLst = playSession.getAll();
}
if (value.trim().equals("")) {
return null;
} else {
try {
int number = Integer.parseInt(value);
for (Player c : playLst) {
if (c.getPk() == number) {
return c;
}
}
} catch (Exception ex) {
System.out.println("error");
}
}
return null;
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null || value.equals("")) {
return "";
} else {
return String.valueOf(((Player) value).getPk());
}
}
}
I am not able to find what is wrong in the above code, if i remove the var,itemValue,itemLabel,converter part then it works fine but once i put the var,itemValue,itemLabel,converter code (as given in prime showcase) i get the above error.
Kindly guide me on what is that i am doing wrong or what is that i should check.
Note: My sample table has only two columns, pk(int) & player(string).

I figured out the problem, its basically if i Pass a List to value(AutoComplete) then the Multiple="true" has be used. Whereas to just do one selection i need to pass only Player object to value(AutoComplete).
Hope this helps somebody else who post without understanding how it works (like me).

Related

Updating adapter of AutoCompleteTextView from LiveData

I have a AutoCompleteTextView that I give it 2 different adapters depending on the amount of text that is being present at the textview - if it has 0 characters I want it to display a list of "recently searched" strings adapter, while if it has more than 1 characters I want it to display auto completion list.
My getRecentlySearchedQueries method along with the RecentSearchedViewModel-
private List<String> recentlySearchedQueries = new ArrayList<>(); // pasted from the top of the class
#Override
public void getRecentlySearchedQueries() {
recentSearchViewModel.getAllQueries().observe(getActivity(), databaseRecentlySearchList -> {
if (databaseRecentlySearchList == null) {
return;
}
for (int i = 0; i < databaseRecentlySearchList.size(); i++) {
Log.d("localDBValue", "Added value - " + databaseRecentlySearchList.get(i).toString() + "\n");
String query = databaseRecentlySearchList.get(i).getQuery();
recentlySearchedQueries.add(query);
}
//Log.d("localDBValue", "recent search list value - " + recentlySearchedQueries);
});
}
public class RecentSearchViewModel extends AndroidViewModel {
private RecentSearchRepository recentSearchRepository;
private LiveData<List<RecentSearchModel>> allRecentlySearched;
public RecentSearchViewModel(#NonNull Application application) {
super(application);
recentSearchRepository = new RecentSearchRepository(application);
allRecentlySearched = recentSearchRepository.getAllRecentSearches();
}
public void insert(RecentSearchModel model) {
recentSearchRepository.insert(model);
}
public void update(RecentSearchModel model) {
// add implementation in the future if needed
}
public void delete(RecentSearchModel model) {
// add implementation in the future if needed
}
public LiveData<List<RecentSearchModel>> getAllQueries() {
return allRecentlySearched;
}
}
public class RecentSearchRepository {
private RecentSearchDao recentSearchDao;
private LiveData<List<RecentSearchModel>> allRecentSearches;
public RecentSearchRepository(Application application) {
MarketplaceDatabase database = MarketplaceDatabase.getRecentSearchInstance(application);
recentSearchDao = database.recentSearchDao();
allRecentSearches = recentSearchDao.getRecentSearchList();
}
public void insert(RecentSearchModel model) {
new RecentSearchRepository.InsertRecentSearchAsyncTask(recentSearchDao).execute(model);
}
public void update (RecentSearchModel model) {
//TODO - implement in future if needed
}
public void delete(RecentSearchModel model) {
//TODO - implement in future if needed
}
public LiveData<List<RecentSearchModel>> getAllRecentSearches() {
return allRecentSearches;
}
private static class InsertRecentSearchAsyncTask extends AsyncTask<RecentSearchModel, Void, Void> {
private RecentSearchDao recentSearchDao;
public InsertRecentSearchAsyncTask(RecentSearchDao recentSearchDao) {
this.recentSearchDao = recentSearchDao;
}
#Override
protected Void doInBackground(RecentSearchModel... recentSearchModels) {
recentSearchDao.insert(recentSearchModels[0]);
return null;
}
}
private static class UpdateRecentSearchAsyncTask extends AsyncTask<RecentSearchModel, Void, Void> {
private RecentSearchDao recentSearchDao;
public UpdateRecentSearchAsyncTask(RecentSearchDao recentSearchDao) {
this.recentSearchDao = recentSearchDao;
}
#Override
protected Void doInBackground(RecentSearchModel... recentSearchModels) {
recentSearchDao.update(recentSearchModels[0]);
return null;
}
}
}
#Dao
public interface RecentSearchDao {
#Insert()
void insert(RecentSearchModel model);
#Update
void update(RecentSearchModel model);
#Delete
void delete(RecentSearchModel model);
#Query("select * from recent_search_table")
LiveData<List<RecentSearchModel>> getRecentSearchList();
}
#Entity(tableName = "recent_search_table")
public class RecentSearchModel {
#PrimaryKey(autoGenerate = true)
private int ID;
private String query;
public RecentSearchModel(){
}
public RecentSearchModel(String query) {
this.query = query;
}
public void setID(int ID) {
this.ID = ID;
}
public int getID() {
return ID;
}
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
#Override
public String toString() {
return "RecentSearchModel{" +
"query='" + query + '\'' +
'}';
}
#Override
public boolean equals(#Nullable Object obj) {
if (obj instanceof RecentSearchModel)
return this.query.equalsIgnoreCase(((RecentSearchModel) obj).query);
return false;
}
}
So, what I am doing here is for start getting all values inside my local DB and adding them to my String list that is part of the adapter. So far so good.
The issue I am facing is that the adapter won't show the amount of strings available in the list that populates it. In fact, it sometimes shows a view half-cut with wierd information, sometimes does not show anything and sometimes shows part of the corrent information. What am I missing?
Another thing I am facing is that the "recently searched" adapter won't work when clicking on the AutoCompleteTextView - it only works when typing and deleting values so the char length is 0. How can I make it work from start of focus?
Here is the way I am populating the information to the ViewModel -
/**
* Shows the searched products following
*/
#Override
public void getSearchedProducts(String searchedQuery) {
MarketplaceUtils.getSearchedProducts(searchedQuery, marketApiCalls, false, initialSearchTake, initialMarketplacePage, new MarketplaceUtils.OnProductsFetchCompleteListener() {
#Override
public void onSuccess(List<MiniProductModel> list) {
if (!searchedQuery.equals(currentSearchedText))
return;
if (list == null) {
//reaching here means we do not have a result to show to the UI so we empty the list.
currentProductList.clear();
productsAdapter.notifyDataSetChanged();
return;
}
if (searchedQuery.length() > 3 && searchAutoCompleteStrings.contains(searchedQuery)) {
Log.d("localDBValue", "searchedValue - " + searchedQuery);
recentSearchViewModel.insert(new RecentSearchModel(searchedQuery));
}
mPresenter.setDiscoverProductsLayoutVisibility(View.GONE);
currentProductList.clear();
currentProductList.addAll(list);
productsAdapter.notifyDataSetChanged();
}
#Override
public void onError(Throwable throwable) {
Log.d("searchedProducts", throwable.getMessage());
}
});
}
The default behaviour for #Insert method of Room is OnConflictStrategy.ABORT - so what I did is to implement equals() method to verify that the RecentSearchModels that are being compared are compared by their string value. Still does seems to effect anything.

p:selectOneMenu not calling setter on form submit

I'm kinda new to JSF and primefaces and I'm running into a very annoying problem.
I'm making a very basic application to learn a bit more about primefaces. I have a simple form that has several textual input fields, two datepickers and one dropdown (selectOneMenu).
Everything works all values are put in the backing bean when I submit the form, except for the value from the dropdown menu. The setter for that item is never called. And the application does not call the public void saveNewActivity(ActionEvent evt) method on the controller as defined on the commandbutton. When I however remove or comment out the dropdown menu in html it does call that method (but the field for the dropdown menu is obviously null).
I've been trying things for nearly two days, and still can't get this to work properly.
I have the following code (snippets):
My html/jsf code
<div id="newActivitycontent">
<h:form id="newActivityForm">
<h:messages id="messages"/>
<table>
<tr>
<td>Gebruiker:</td>
<td><p:selectOneMenu value="#{plannedActivityController.newActivity.organiser}}"
converter="#{userConverter}">
<f:selectItem itemLabel="Kies een gebruiker" itemValue=""/>
<f:selectItems value="#{plannedActivityController.users}" var="user"
itemLabel="#{user.firstname} #{user.lastname}" itemValue="#{user}"/>
</p:selectOneMenu></td>
</tr>
<tr>
<td>Titel:</td>
<td><p:inputText value="#{plannedActivityController.newActivity.name}"/></td>
</tr>
<tr>
<td>Beschrijving:</td>
<td><p:inputText value="#{plannedActivityController.newActivity.desctription}"/></td>
</tr>
<tr>
<td>Startdatum:</td>
<td><p:calendar value="#{plannedActivityController.newActivity.startDateDate}"/></td>
</tr>
<tr>
<td>Einddatum:</td>
<td><p:calendar value="#{plannedActivityController.newActivity.endDateDate}"/></td>
</tr>
</table>
<p:commandButton id="btnSaveNewActivity" value="Opslaan"
actionListener="#{plannedActivityController.saveNewActivity}"
update=":overviewForm:activityTable messages"/>
<p:commandButton id="btnCancelNewActivity" value="Annuleren"
actionListener="#{plannedActivityController.cancelNewActivity}" onclick="hideAddNewUI()"
update=":overviewForm:activityTable" type="reset" immediate="true"/>
</h:form>
</div>
The controller that is used by that code:
#Named
#SessionScoped
public class PlannedActivityController implements Serializable {
#Inject
private ApplicationModel appModel;
#Inject
private SessionModel sessionModel;
#Inject
private ActivityMapper activityMapper;
#Inject
private UserMapper userMapper;
private ActivityBean newActivity;
private ActivityBean selectedActivity;
private List<ActivityBean> activities;
private List<UserBean> users;
public PlannedActivityController() {
}
#PostConstruct
public void onCreated() {
convertActivities();
onNewActivity();
users = userMapper.mapToValueObjects(appModel.getUsers());
}
public void convertActivities() {
List<PlannedActivity> originalActivities = appModel.getActivities();
this.activities = activityMapper.mapToValueObjects(originalActivities);
}
public void onRowEditComplete(RowEditEvent event) {
System.out.println("row edited : " + event.getObject());
//TODO: save changes back to db!
}
public void onRowSelectionMade(SelectEvent event) {
System.out.println("row selected : " + event.getObject());
selectedActivity = (ActivityBean)event.getObject();
}
//Activity crud methods
public void onNewActivity() {
newActivity = new ActivityBean();
newActivity.setId(new Date().getTime());
}
public void saveNewActivity(ActionEvent evt) {
PlannedActivity newAct = activityMapper.mapToEntity(newActivity);
if(newAct != null) {
appModel.getActivities().add(newAct);
}
convertActivities();
}
public void cancelNewActivity() {
//TODO: cleanup.
}
public void deleteSelectedActivity() {
if(selectedActivity != null) {
activities.remove(selectedActivity);
appModel.setActivities(activityMapper.mapToEntities(activities));
convertActivities();
} else {
//TODO: show error or information dialog, that delete cannot be done when nothing has been selected!
}
}
//Getters & Setters
public ApplicationModel getAppModel() {
return appModel;
}
public void setAppModel(ApplicationModel appModel) {
this.appModel = appModel;
}
public SessionModel getSessionModel() {
return sessionModel;
}
public void setSessionModel(SessionModel sessionModel) {
this.sessionModel = sessionModel;
}
public ActivityMapper getActivityMapper() {
return activityMapper;
}
public void setActivityMapper(ActivityMapper activityMapper) {
this.activityMapper = activityMapper;
}
public UserMapper getUserMapper() {
return userMapper;
}
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
public ActivityBean getNewActivity() {
return newActivity;
}
public void setNewActivity(ActivityBean newActivity) {
this.newActivity = newActivity;
}
public ActivityBean getSelectedActivity() {
return selectedActivity;
}
public void setSelectedActivity(ActivityBean selectedActivity) {
this.selectedActivity = selectedActivity;
}
public List<ActivityBean> getActivities() {
return activities;
}
public void setActivities(List<ActivityBean> activities) {
this.activities = activities;
}
public List<UserBean> getUsers() {
return users;
}
public void setUsers(List<UserBean> users) {
this.users = users;
}
}
My activitybean:
public class ActivityBean implements Serializable {
private Long id = 0L;
private String name;
private String desctription;
private UserBean organiser;
private Calendar startDate;
private Calendar endDate;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesctription() {
return desctription;
}
public void setDesctription(String desctription) {
this.desctription = desctription;
}
public UserBean getOrganiser() {
return organiser;
}
public void setOrganiser(UserBean organiser) {
this.organiser = organiser;
}
public Calendar getStartDate() {
return startDate;
}
public void setStartDate(Calendar startDate) {
this.startDate = startDate;
}
public Date getStartDateDate() {
if(this.startDate == null) {
return null;
}
return this.endDate.getTime();
}
public void setStartDateDate(Date startDate) {
if(this.startDate == null) {
this.startDate = new GregorianCalendar();
}
this.startDate.setTime(startDate);
}
public String getStartDateString() {
if(this.startDate == null) {
return null;
}
return startDate.get(Calendar.DAY_OF_MONTH) + "/" + startDate.get(Calendar.MONTH) + "/" + startDate.get(Calendar.YEAR) + "";
}
public Calendar getEndDate() {
return endDate;
}
public void setEndDate(Calendar endDate) {
this.endDate = endDate;
}
public Date getEndDateDate() {
if(this.endDate == null) {
return null;
}
return endDate.getTime();
}
public void setEndDateDate(Date endDate) {
if(this.endDate == null) {
this.endDate = new GregorianCalendar();
}
this.endDate.setTime(endDate);
}
public String getEndDateString() {
if(this.endDate == null) {
return null;
}
return endDate.get(Calendar.DAY_OF_MONTH) + "/" + endDate.get(Calendar.MONTH) + "/" + endDate.get(Calendar.YEAR) + "";
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ActivityBean that = (ActivityBean) o;
if (id != null ? !id.equals(that.id) : that.id != null) return false;
return true;
}
#Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
}
}
My userbean:
public class UserBean {
private Long id;
private String username;
private String firstname;
private String lastname;
private String email;
private String phone;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserBean userBean = (UserBean) o;
if (id != null ? !id.equals(userBean.id) : userBean.id != null) return false;
return true;
}
#Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
}
}
And the converter used by the selectOneMenu:
#Named
public class userConverter implements Converter{
#Inject
private PlannedActivityController activityController;
#Override
public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String s) {
for (UserBean user : activityController.getUsers()) {
if(user.getId().toString().equals(s)) {
return user;
}
}
return null;
}
#Override
public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object o) {
if(o instanceof UserBean) {
UserBean user = (UserBean)o;
return user.getId().toString();
}
return "";
}
}
The problem is here. Look closer. This is invalid EL syntax.
value="#{plannedActivityController.newActivity.organiser}}"
This should however have thrown a PropertyNotWritableException on submit something like this:
javax.el.PropertyNotWritableException: /test.xhtml #25,39 value="#{plannedActivityController.newActivity.organiser}}": Illegal Syntax for Set Operation
at com.sun.faces.facelets.el.TagValueExpression.setValue(TagValueExpression.java:136)
at javax.faces.component.UIInput.updateModel(UIInput.java:822)
at javax.faces.component.UIInput.processUpdates(UIInput.java:739)
at javax.faces.component.UIForm.processUpdates(UIForm.java:281)
at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1244)
at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1244)
at javax.faces.component.UIViewRoot.processUpdates(UIViewRoot.java:1223)
at com.sun.faces.lifecycle.UpdateModelValuesPhase.execute(UpdateModelValuesPhase.java:78)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)
This exception should have been logged to the server log. By default this will however not end up in an error page for the enduser because JSF/PrimeFaces have by default no form of feedback to enduser in case of exceptions which are thrown during ajax requests. You should however be able to see it in actual ajax response body in webbrowser's builtin HTTP traffic monitor.
The JSF utility library OmniFaces offers a FullAjaxExceptionHandler for the very problem of total absence of feedback on exceptions during ajax requests. You may find it useful. When I tried to recreate your problem, I was been presented a clear error page so that I don't need to dig in server log or HTTP traffic monitor for clues.

Assigning one of a <p:selectOneMenu> item in java object

I have a list of governorates that I displayed in a <p:selectOneMenu>
the java code in the managed bean:
public List<SelectItem> gouvernorats() {
List<Gouvernorat> all = emetteursEJB.findAllGouvernorat();
List<SelectItem> items = new ArrayList<>();
for (Gouvernorat g : all) {
items.add(new SelectItem(g, g.getLibelleGouv()));
}
return items;
}
in the <p:selectOneMenu> I add <p:ajax>:
<p:selectOneMenu value="#{emetteurBean.selectedGouvernorat}" style="width:160px" >
<f:selectItem itemLabel="Select One" itemValue="" />
<f:selectItems value="#{emetteurBean.gouvernorats()}" />
<p:ajax event="change" listener="#{emetteursBean.handelGouvChanged()"/>
</p:selectOneMenu>
in the method handelGouvChanged() selectedGouvernorat object is always null;
Recently I added the convert I stumbled upon NullPointerException
#FacesConverter(forClass = Gouvernorat.class)
public class EmetteursConverter implements Converter {
#EJB
private ReferentielDaoLocal refEJB;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String selectedValue) {
System.out.println("Inside The Converter");
System.out.println(selectedValue.length());
if (selectedValue == null) {
return null;
} else {
return refEJB.findGouvByCode(selectedValue.trim());
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) {
return null;
} else {
return String.valueOf(((Gouvernorat) value).getIdGouvernorat());
}
}
}
I found a solution, I injected EJB with InitilContext.lookup ()
#FacesConverter(forClass = Gouvernorat.class)
public class GouvernoratConverter implements Converter {
private static final Logger LOG = Logger.getLogger(GouvernoratConverter.class.getName());
#Override
public Object getAsObject(FacesContext context, UIComponent component, String selectedValue) {
if (selectedValue == null) {
return null;
} else {
try {
ReferentielDaoLocal myService = (ReferentielDaoLocal) new
InitialContext().lookup("java:global/ErpCCF/ErpCCF-ejb/ReferentielDaoImpl");
return myService.findGouvByCode(selectedValue);
} catch (NamingException ex) {
LOG.log(Level.SEVERE, "Converter Gouvernorat Error", ex.getMessage());
return null;
}
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) {
return null;
} else {
return String.valueOf(((Gouvernorat) value).getIdGouvernorat());
}
}
}

Logging the invoked managed bean actionListener in a PhaseListener

I need to log actions fired from managed Bean.This link , Logging the invoked managed bean action in a PhaseListener helps me solve the problem related to actions. But, when I use actionListener , I have a NullPointerException
#Override
public void beforePhase(PhaseEvent event) {
FacesContext context = event.getFacesContext();
if (context.isPostback()) {
UICommand component = findInvokedCommandComponent(context);
if (component != null) {
String methodExpression = component.getActionExpression().getExpressionString();
// It'll contain #{bean.action}.
}
}
}
private UICommand findInvokedCommandComponent(FacesContext context) {
UIViewRoot view = context.getViewRoot();
Map<String, String> params = context.getExternalContext().getRequestParameterMap();
if (context.getPartialViewContext().isAjaxRequest()) {
return (UICommand) view.findComponent(params.get("javax.faces.source"));
} else {
for (String clientId : params.keySet()) {
UIComponent component = view.findComponent(clientId);
if (component instanceof UICommand) {
return (UICommand) component;
}
}
}
return null;
}
THe NullPointerException occurs with line
String methodExpression = component.getActionExpression().getExpressionString();
How can I get the name of the actionListener method ?
I tried
private UICommand findInvokedCommandComponent(FacesContext context) {
UIViewRoot view = context.getViewRoot();
Map<String, String> params = context.getExternalContext().getRequestParameterMap();
if (context.getPartialViewContext().isAjaxRequest()) {
UIComponent component = view.findComponent(params.get("javax.faces.source"));
if (component instanceof UICommand) {
// component.get
UICommand comp= (UICommand) component;
ActionListener[] actionListeners= comp.getActionListeners();
System.out.println("Taille des Listeners : "+actionListeners.length);
ActionListener method;
method = actionListeners[0];
String toString = method.toString();
System.out.println("ActionListener : "+toString);
return (UICommand) component;
}
} else {
for (String clientId : params.keySet()) {
UIComponent component = view.findComponent(clientId);
if (component instanceof UICommand) {
return (UICommand) component;
}
}
}
return null;
}
System.out.println("ActionListener : "+toString); returns ActionListener : `javax.faces.event.MethodExpressionActionListener#16a779b` . What I would like to have is `#{bean.action}` .Maybe I did it the wrong way
You were on the good way, but you need to get the MethodExpressionActionListener which implements the ActionListener. Using it, you still can't get the MethodExpression out of the box, probably the only way is to get it by reflection (not the best thing...).
That said, you can modify your code like this :
if (component != null) {
String methodExpression = "";
if(component.getActionExpression() != null)
{
methodExpression = component.getActionExpression().getExpressionString();
}
else if(component.getActionListeners().length > 0)
{
methodExpression = getActionListener((MethodExpressionActionListener)component.getActionListeners()[0]).getExpressionString();
}
System.out.println("Method Expression : " + methodExpression);
}
And you will need this method to actually get required information out of the MethodExpressionActionListener :
private MethodExpression getActionListener(MethodExpressionActionListener listener)
{
MethodExpression expression = null;
Field field;
try
{
field = listener.getClass().getDeclaredField("methodExpressionZeroArg");
field.setAccessible(true);
expression = (MethodExpression)field.get(listener);
if(expression == null)
{
field = listener.getClass().getDeclaredField("methodExpressionOneArg");
field.setAccessible(true);
expression = (MethodExpression)field.get(listener);
}
}
catch(Exception e)
{
}
return expression;
}

Howto get multiple data from a custom component to the backend bean?

I have a data object called DeliveryPeriod which is a container for a start and a end date (saved as String like dd.MM.yyy, comes from the database) an the id of another object called PlanningPeriod. This delivery period should be displayed in its own custom component in JSF like
<myc:deliveryPeriodComponent value="#{backendBean.deliveryPeriod}" />
I implement a class DeliveryPeriodComponent which extends UIInput and a DeliveryPeriodComponentRenderer which extendes javax.faces.renderer. The rendering works well, i see two calender elements and a SelectOneMenu to choose the planning period. But render the data is not all, I need to change the data as well. And here comes the problem, i have no idea to get the data inside my component to the backend bean. The decode() method did not know the new values and the other methods are never called. I didn't know the trick, how to connect the JSF page to the bean, from the tutorial (http://jsfatwork.irian.at, i bought the book) i had these methods like getValue() and getConverter().
Here is the code from the component:
public class DeliveryPeriodComponent extends UIInput {
public static final String COMPONENT_TYPE = "de.hacon.tps.integrator.web.component.deliveryperiod.DeliveryPeriodComponent";
enum PropertyKeys {
begin, end, planningPeriod
}
public DeliveryPeriodComponent() {
setRendererType("de.hacon.tps.integrator.web.component.deliveryperiod.DeliveryPeriodComponent");
}
public String getBegin() {
return (String) getStateHelper().eval(PropertyKeys.begin, "01.01.2012");
}
public void setBegin(String begin) {
getStateHelper().put(PropertyKeys.begin, begin);
}
public String getEnd() {
return (String) getStateHelper().eval(PropertyKeys.end, "31.12.2012");
}
public void setEnd(String end) {
getStateHelper().put(PropertyKeys.end, end);
}
public int getPlanningPeriod() {
return (Integer) getStateHelper().eval(PropertyKeys.planningPeriod, 0);
}
public void setPlanningPeriod(int planningPeriod) {
getStateHelper().put(PropertyKeys.planningPeriod, planningPeriod);
}}
And here is the renderer:
public class DeliveryPeriodComponentRenderer extends Renderer {
SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");
#Override
public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
DeliveryPeriodComponent comp = (DeliveryPeriodComponent) component;
String clientId = comp.getId();
try {
encodeInput(context, comp, clientId);
} catch (ParseException e) {
e.printStackTrace();
}
}
private void encodeInput(FacesContext context, DeliveryPeriodComponent comp, String clientId) throws ParseException {
comp.getChildren().clear();
DeliveryPeriod value = (DeliveryPeriod) comp.getAttributes().get("value");
List<PlanningPeriodSubset> pp = (List<PlanningPeriodSubset>) comp.getAttributes().get("periods");
HtmlPanelGrid pGrid = new HtmlPanelGrid();
pGrid.setColumns(4);
Calendar cBegin = new Calendar();
cBegin.setShowOn("button");
cBegin.setValue(sdf.parse(value.getStartDate()));
cBegin.setPattern("dd.MM.yyyy");
pGrid.getChildren().add(cBegin);
Calendar cEnd = new Calendar();
cEnd.setShowOn("button");
cEnd.setValue(sdf.parse(value.getEndDate()));
cEnd.setPattern("dd.MM.yyyy");
pGrid.getChildren().add(cEnd);
HtmlSelectOneMenu sPlanningPeriod = new HtmlSelectOneMenu();
Collection<UISelectItem> items = new ArrayList<UISelectItem>();
for (PlanningPeriodSubset op : pp) {
UISelectItem item = new UISelectItem();
item.setItemLabel(op.getName());
item.setItemValue(op.getId());
items.add(item);
}
sPlanningPeriod.getChildren().addAll(items);
sPlanningPeriod.setValue(value.getPlanningPeriodId());
pGrid.getChildren().add(sPlanningPeriod);
HtmlPanelGrid buttonPanel = new HtmlPanelGrid();
buttonPanel.setColumns(2);
Button bDelete = new Button();
bDelete.setValue(" - ");
buttonPanel.getChildren().add(bDelete);
Button bInfo = new Button();
bInfo.setValue(" i ");
buttonPanel.getChildren().add(bInfo);
pGrid.getChildren().add(buttonPanel);
comp.getChildren().add(pGrid);
}
#Override
public void decode(FacesContext context, UIComponent component) {
DeliveryPeriodComponent deliveryComponent = (DeliveryPeriodComponent) component;
DeliveryPeriod deliveryPeriod = new DeliveryPeriod();
deliveryPeriod.setStartDate(deliveryComponent.getBegin());
deliveryPeriod.setEndDate(deliveryComponent.getEnd());
deliveryPeriod.setPlanningPeriodId(deliveryComponent.getPlanningPeriod());
// Map<String, String> params = context.getExternalContext().getRequestParameterMap();
// String clientId = component.getClientId();
// String value = params.get(clientId);
// ((UIInput) component).setSubmittedValue(value);
}
#Override
public Object getConvertedValue(FacesContext context, UIComponent component, Object submittedValue)
throws ConverterException {
Converter converter = getConverter(context, (DeliveryPeriodComponent) component);
if (converter != null) {
return converter.getAsObject(context, component, (String) submittedValue);
} else {
return submittedValue;
}
}
private Object getValue(FacesContext context, DeliveryPeriodComponent comp) {
Object submittedValue = comp.getSubmittedValue();
if (submittedValue != null) {
return submittedValue;
}
Object begin = comp.getBegin();
Object end = comp.getEnd();
Object planningPeriod = comp.getPlanningPeriod();
DeliveryPeriod period = new DeliveryPeriod();
period.setStartDate((String) begin);
period.setEndDate((String) end);
period.setPlanningPeriodId((Integer) planningPeriod);
Converter converter = this.getConverter(context, comp);
if (converter != null) {
return converter.getAsString(context, comp, period);
} else if (period != null) {
return period.toString();
} else {
return "";
}
}
private Converter getConverter(FacesContext context, DeliveryPeriodComponent comp) {
Converter conv = ((UIInput) comp).getConverter();
if (conv != null) {
ValueExpression exp = comp.getValueExpression("value");
if (exp == null) {
return null;
} else {
Class valueType = exp.getType(context.getELContext());
if (valueType == null) {
return null;
} else {
return context.getApplication().createConverter(valueType);
}
}
}
return null;
}}
Maybe this is a trivial problem, I am not a pro at JSF and still learning. And it is very hard to write your own components when you almost confused about the basics :-( Still learning new things every day. Thank you for your help!
(I found a lot on custom components with examples like a custum inputfields, these components works well and transfer their data. Unfortunatly I found nothing on custom components which contains more then one input field or did something different from the existing JSF elements)
In Xhtml Page Call your listner with
<h:commandButton id="add" value="Add More" styleClass="input_right_cor" type="button">
<f:ajax execute="#this" listener="#{templateSearchHandler.AddRow}" />
</h:commandButton>

Resources