I am developing a JSF application
I have 2 selectOnemenu controls and submit button.
I need to disable the button if the values of 2 fields are equal
<h:selectOneMenu id="from" value="#{currencyBean.history.fromCurrency}" >
<f:selectItems value="#{currencyBean.currency}" var="c" itemValue="#{c}" itemLabel="#{c.name}"/>
</h:selectOneMenu>
<p:outputLabel for="to" value="to:" />
<h:selectOneMenu id="to" value="#{currencyBean.history.toCurrency}" >
<f:selectItems value="#{currencyBean.currency}" var="c" itemValue="#{c}" itemLabel="#{c.name}"/>
</h:selectOneMenu>
<p:commandButton id="calculateButton" value="Convert" update=":convert :historyPanel" onstart="PF('statusDialog').show()" onsuccess="PF('statusDialog').hide()" validateClient="true" ajax="true" action="#{currencyBean.Calculate()}" />
I tried to use onchange with ajax but everytime I change one dropdown the value of the second drowpdown became null in the backbean so I cannot read it.
Here is my backbean
#Named(value = "currencyBean")
#RequestScoped
public class CurrencyBean implements Serializable {
/**
* Creates a new instance of CurrencyBean
*/
private History history;
private Currency currency;
private Date currentDate;
#Inject
private Loginbean loginBean;
private List<History> historyList;
public List<History> getHistoryList() {
int userId = loginBean.getUserId();
if (userId != -1) {
return new HistoryHelper().GetHistoryListByUser(userId);
}
return null;
}
public CurrencyBean() {
history = new History();
}
public History getHistory() {
return history;
}
public void setHistory(History history) {
this.history = history;
}
public Currency[] getCurrency() {
return Currency.values();
}
public Date getCurrentDate() {
return new Date();
}
public void Calculate() {
int userId = loginBean.getUserId();
if (userId != -1) {
new CurrencyClient().Convert(userId, this.history);
}
}
}
any clue ?
My assumption is that all of your problems come from your managed bean scope. You have #Request scope so every request your managed bean will be removed from container, thus when you define onchange="submit()" (this is only my assumption because you haven't define how you implement onchange attribute) and you select value from one selectBox component values for this component is updated but the first one is still null. When you select second selectBox value updated from first selectBox doesn't exists anymore as managed bean has been removed after first request. You should try with wider scope for instance #ViewScope. If it doesn't help then further informations like implementation onchange attribute will be needed
Related
i am looking for a plain-jsf-solution to handle bookmarkable, parameterbased datatable-pagination.
actually i am using an ajax-approach and a cookie to store the active page. when the user hits F5 or clicks a link in a datatable-row and then returns with "browser-back", i check the cookie to show the last active page.
<h:commandLink value="Next Page">
<f:ajax listener="#{bean.nextPage}" render="dataTable"/>
</h:commandLink>
#ViewScoped
public class PagerBean {
public void nextPage() {
this.resultList = Products.getNextProducts(getFirstResult(),getMaxResult());
addCookie("activePage", getActivePage());
}
}
#ViewScoped
public class ProductBean {
#ManagedProperty(value="#{pager}")
protected PagerBean pager;
#Postconstruct
public void init() {
if (isCookie("activePage"){
pager.setActivePage(getCookie("activePage"));
}
}
}
however, i am looking for a bookmarkable solution, so that we can produce links with specific url-parameters, which are also trackable by browser back/forward-button.
http://foo.com/products?page=1
http://foo.com/products?page=2
http://foo.com/products?page=3
<h:link outcome="/pages/markets/products">
<f:param name="page" value="#{bean.pager.activePage}"/>
</h:link>
#ViewScoped
public class ProductBean {
#ManagedProperty(value="#{pager}")
protected PagerBean pager;
#Postconstruct
public void init() {
final String page = Faces.getRequestParameter("page");
if (null != page){
//load next entries
}
}
}
my only problem is, that with this version, the ViewScoped ProductBean gets newly created on every pagination-action. i think, as the view is not changing, the bean should not be re-created. what is the right approach to get lucky?
found a non-primefaces-solution using h:commandLink and HTML5 History API.
on every pagination-action the current pagenumber is stored in the history. when user navigates, the pagenumber will be restored from history and ajax-submitted again.
<h:commandLink value="Next Page">
<f:ajax listener="#{bean.nextPage}" render="dataTable" onevent="pushState"/>
</h:commandLink>
<h:inputText id="current" value="#{bean.pager.activePage}"/>
<h:commandLink value="Previous Page">
<f:ajax listener="#{bean.prevPage}" render="dataTable" onevent="pushState"/>
</h:commandLink>
<!--hidden action fired when user navigates in history-->
<h:commandLink styleClass="hidden" id="hiddenLink">
<f:ajax execute="current" listener="#{bean.jumpToPage}" render="dataTable" />
</h:commandLink>
JS:
$(window).on('popstate', function(event) {
var pageInHistory = event.originalEvent.state;
if (null == pageInHistory){
pageInHistory = 1;
}
//set page number from history
$('#current').val(pageInHistory);
//trigger ajax-submit
$('#hiddenLink').trigger('click');
});
pushState = function (data){
switch (data.status) {
case "success": {
var currentPage = $('#current').val();
history.pushState(currentPage, null, "?page=" + currentPage);
}
}
Bean
#ViewScoped
public class PagerBean {
private int activePage;
public void jumpToPage() {
//load data for activePage
}
//...
}
I'm having a hard time doing basic AJAX updates of a timeline.
Let me start with a basic example where I want to update the start and end times of a timeline based on the selection of a dropdown list:
<h:form id="form">
<h:outputLabel for="period" value="#{str.schedule_period}"/>
<h:selectOneMenu id="period" value="#{timelineController.period}" label="#{str.schedule_period}">
<f:selectItems value="#{timelineController.periodWeeks}" />
<p:ajax event="change" update="timeline" />
</h:selectOneMenu>
<pe:timeline id="timeline" value="#{timelineController.model}"
editable="true"
eventMargin="0"
minHeight="120"
stackEvents="false"
start="#{timelineController.timelineStart}"
min="#{timelineControllertimelineStart}"
end="#{timelineController.timelineEnd}"
max="#{timelineController.timelineEnd}"
showNavigation="false" showButtonNew="false"
showCurrentTime="false"
axisOnTop="true"
timeZone="#{timelineController.timezone}"
zoomMin="28800000"
dropActiveStyleClass="ui-state-highlight" dropHoverStyleClass="ui-state-hover">
<p:ajax event="drop" listener="#{timelineController.onDrop}"
global="false" process="timeline"/>
</pe:timeline>
</h:form>
When I select an item in the dropdown list, an AJAX event fires and sets the period property in the backing bean, but the new value is not reflected in the timeline component. As a workaround, I wrapped the timeline in a p:outputPanel and updated the wrapper instead and it works:
...
<h:selectOneMenu id="period" value="#{timelineController.period}" label="#{str.schedule_period}">
<f:selectItems value="#{timelineController.periodWeeks}" />
<p:ajax event="change" update="wrapper" />
</h:selectOneMenu>
...
<p:outputPanel id="wrapper">
<pe:timeline id="timeline" value="#{timelineController.model}"
editable="true"
eventMargin="0"
minHeight="120"
stackEvents="false"
start="#{timelineController.timelineStart}"
min="#{timelineControllertimelineStart}"
end="#{timelineController.timelineEnd}"
max="#{timelineController.timelineEnd}"
showNavigation="false" showButtonNew="false"
showCurrentTime="false"
axisOnTop="true"
timeZone="#{timelineController.timezone}"
zoomMin="28800000"
dropActiveStyleClass="ui-state-highlight" dropHoverStyleClass="ui-state-hover">
<p:ajax event="drop" listener="#{timelineController.onDrop}"
global="false" process="wrapper"/>
</pe:timeline>
</p:outputPanel>
Note that I also had to change the process attribute of p:ajax to wrapper.
So my first question is: why doesn't the update work without wrapping the timeline component?
My second question is about drag and drop. As you can you see from my code above, I have attached a drop listener to the timeline. And I'm also able to drag and drop events from a p:dataList BEFORE I make a selection in the dropdown list. Once I select a new period in the dropdown list, the timeline gets updated appropriately, but I'm not able to drag and drop events to the timeline any more (the onDrop listener doesn't get fired). Here's my p:dataList:
<p:dataList id="eventsList" value="#{timelineController.users}"
var="user" itemType="none">
<h:panelGroup id="eventBox" layout="box" style="z-index:9999; cursor:move;">
#{user.toString()}
</h:panelGroup>
<p:draggable for="eventBox" revert="true" helper="clone" cursor="move"/>
</p:dataList>
Any ideas what's wrong here?
I'm also including the TimelineController class for reference:
#ManagedBean
#ViewScoped
public class TimelineController {
#EJB UserService userDao;
private TimelineModel model;
private String name;
private ZoneId timezone;
private Period period;
private Duration defaultShiftDuration;
private LocalDateTime timelineStart;
private LocalDateTime timelineEnd;
#PostConstruct
protected void initialize() {
timezone = ZoneId.of("Europe/Berlin);
period = Period.ofWeeks(2);
defaultShiftDuration = Duration.ofHours(8);
timelineStart = LocalDateTime.now().with(DayOfWeek.MONDAY).withHour(0).withMinute(0).truncatedTo(ChronoUnit.MINUTES);
// create timeline model
model = new TimelineModel();
}
public TimelineModel getModel() {
return model;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTimezone() {
return timezone.getId();
}
public void setTimezone(String timezone) {
this.timezone = ZoneId.of(timezone);
}
public List<SelectItem> getPeriodWeeks() {
List<SelectItem> weeks = Lists.newArrayList();
weeks.add(new SelectItem(1, "1 " + JsfUtil.getStringResource("schedule_week")));
weeks.add(new SelectItem(2, "2 " + JsfUtil.getStringResource("schedule_weeks")));
weeks.add(new SelectItem(3, "3 " + JsfUtil.getStringResource("schedule_weeks")));
return weeks;
}
public int getPeriod() {
return period.getDays() / 7;
}
public void setPeriod(int nWeeks) {
this.period = Period.ofWeeks(nWeeks);
timelineEnd = null;
}
public Date getTimelineStart() {
return Date.from(timelineStart.atZone(timezone).toInstant());
}
public Date getTimelineEnd() {
if (timelineEnd == null) {
timelineEnd = timelineStart.plus(period);
}
return Date.from(timelineEnd.atZone(timezone).toInstant());
}
public void setStartsOn(String startsOn) {
timelineStart = LocalDateTime.parse(startsOn + "T00:00");
timelineEnd = null;
}
public List<User> getUsers(){
return userDao.findAll();
}
public void onDrop(TimelineDragDropEvent e) {
// get dragged model object (event class) if draggable item is within a data iteration component,
// update event's start and end dates.
User user = (User) e.getData();
Date endDate = Date.from(e.getStartDate().toInstant().plus(defaultShiftDuration));
// create a timeline event (not editable)
TimelineEvent event = new TimelineEvent(user, e.getStartDate(), endDate, true, e.getGroup());
// add a new event
TimelineUpdater timelineUpdater = TimelineUpdater.getCurrentInstance(":form:timeline");
model.add(event, timelineUpdater);
}
}
The problem was a missing widgetVar attribute in the timeline component. This looks like a bug to me, since I'm not using the client side API of the component. I will file a bug in PF Extensions project.
I have a country class:
public class Country{
private Long id;
private String name;
}
and a person class that has two Country fields
public class Person{
private Country nationality;
private Country nationality2;
}
Now in JSF I use <f:selectItems> to return list of countries to select nationalities as following:
<h:form id="form1">
<h:selectOneMenu value="#{mybean.person.nationality.id}">
<f:selectItems value="#{mybean.countryList}" var="var" itemValue="#{var.id}"/>
</h:selectOneMenu>
<h:selectOneMenu value="#{mybean.person.nationality2.id}">
<f:selectItems value="#{mybean.countryList}" var="var" itemValue="#{var.id}"/>
</h:selectOneMenu>
<p:commandButton actionListener="#{mybean.save}" update="sometable #form"/>
</h:form>
Now the weird problem is that when I submit the form the value assigned to the second field (nationality2) is assigned to both nationality and nationality2 regardless of what has been selected for the first field. For example if the selected value for nationality is 1 and the selected value for nationality2 is 2, when I submit the form both fields have the value 2. Why is this occuring?
PS: JSF implementation is Mojarra 2.1.3
Your concrete problem is caused because you're setting copies of the same Country reference as selected value and then manipulating only the id property. All changes made in one reference get reflected in all other references as well.
E.g.
Country country = new Country();
person.setNationality1(country);
person.setNationality2(country);
country.setId(1); // Gets reflected in both nationalities!
You'd better set the whole Country entity as value instead of manipulating its properties. Create a Converter which converts between Country and id:
#FacesConverter(forClass=Country.class)
public class CountryConverter implements Converter {
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return (value instanceof Country) ? ((Country) value).getId() : null;
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.isEmpty()) {
return null;
}
if (!value.matches("\\d+")) {
throw new ConverterException(new FacesMessage("Invalid country ID: " + value));
}
Long countryId = Long.valueOf(value);
MyBean myBean = context.getApplication().evaluateExpressionGet(context, "#{myBean}", MyBean.class);
for (Country country : myBean.getCountries()) {
if (countryId.equals(country.getId())) {
return country;
}
}
throw new ConverterException(new FacesMessage("Unknown country ID: " + value));
}
}
and use it as follows:
<h:selectOneMenu value="#{mybean.person.nationality1}">
<f:selectItems value="#{mybean.countries}" var="country" itemValue="#{country}" itemLabel="#{country.name}" />
</h:selectOneMenu>
<h:selectOneMenu value="#{mybean.person.nationality2}">
<f:selectItems value="#{mybean.countries}" var="country" itemValue="#{country}" itemLabel="#{country.name}" />
</h:selectOneMenu>
Try to give your Second selectOneMenu another name for var. For Example:
<h:selectOneMenu value="#{mybean.person.nationality2.id}">
<f:selectItems value="#{mybean.countryList}" var="var2" itemValue="#{var2.code}"/>
</h:selectOneMenu>
Otherwise you overwrite your changes in the (first) variable var by changing the second selectOneMenu.
EDIT:
If it does not solve the problem, try for tests to create a second countryList (countryList2) and attach it to the second selectOneMenu.
If the value still stays the same, the failure must be in the getters or setters.
I'm trying to implement a list of users names which can be rearranged by clicking on UP or DOWN links.
<ul>
<ui:repeat var="user" value="#{cc.attrs.value}">
<li>
#{user.name}
<h:link outcome = "user" value = "left" onclick="#{accountController.moveDown}">
<f:param name="id" value = "${user.id}" />
</h:link>
</li>
</ui:repeat>
</ul>
The problem here is that it seems that I'm not using the onclick attribute correctly. What is the proper way for doing this?
Edit: Following your advices I placed all the links in a form:
<h:form>
<ui:repeat value="#{cc.attrs.value}" var = "user">
<div class = "user">
<h:commandLink id = "Link1" value = "Up" binding = "#{accountController.ommandLink}" action = "#{accountController.moveUserUp}">
<f:attribute name = "userId" value = "#{user.id}" />
</h:commandLink>
<h:commandLink id = "Link2" value = "Down" binding = "#{accountController.commandLink}" action = "#{accountController.moveUserDown}">
<f:attribute name = "userId" value = "#{user.id}" />
</h:commandLink>
<h:commandLink id = "Link3" value = "Delete" binding = "#{accountController.commandLink}" action = "#{accountController.deleteUser}">
<f:attribute name = "userId" value = "#{user.id}" />
</h:commandLink>
</div>
</h:form>
the Managed Bean:
private UIComponent commandLink;
public void moveUserUp(){
Integer userId = (Integer)commandLink.getAttributes().get("userId");
System.out.println("MOVE TAB LEFT :" + userId);
}
public void moveUserDown(){
Integer userId = (Integer)commandLink.getAttributes().get("userId");
System.out.println("MOVE TAB RIGHT: " + userId);
}
public void deleteUser(){
Integer userId = (Integer)commandLink.getAttributes().get("userId");
System.out.println("DELETE TAB: " + userId);
}
public UIComponent getCommandLink() {
return commandLink;
}
public void setCommandLink(UIComponent commandLink) {
this.commandLink = commandLink;
}
The communication between the command Link and the managed bean is working but in the UI only the last commandLink (close action) is displayed.
In order to invoke a bean action method on click of a link, you need <h:commandLink>. This must be enclosed in a <h:form>.
<h:form>
<h:commandLink ... action="#{bean.action}" />
</h:form>
public String action() {
// ...
return "/other.xhtml";
}
In JSF, only the attributes which interpret the EL expression as a MethodExpression can be used to declare action methods. All other attributes are interpreted as ValueExpression and they are immediately executed when the HTML output is generated by JSF. This covers the onclick attribute, whose value should actually represent a JavaScript function.
In case you actually want to use a GET link, then move the action method to a <f:viewAction> in the target page. This will be invoked on page load of the target page.
<h:link ... outcome="/other.xhtml" />
<f:metadata>
<f:viewAction action="#{bean.onload}" />
</f:metadata>
public void onload() {
// ...
}
See also:
When should I use h:outputLink instead of h:commandLink?
How to send form input values and invoke a method in JSF bean
How do I process GET query string URL parameters in backing bean on page load?
How to navigate in JSF? How to make URL reflect current page (and not previous one)
Following your advices I placed all the links in a form
The communication between the command Link and the managed bean is working but in the UI only the last commandLink (close action) is displayed.
You should not bind multiple physically different components to one and same bean property. Also the <f:attribute> to pass arguments is hacky and not necessary anymore in JSF2. Assuming that you're using a Servlet 3.0 / EL 2.2 container (your question history confirms that you're using Glassfish 3), rather just pass the argument as method argument directly:
<h:commandLink id="Link1" value="Up" action="#{accountController.moveUserUp(user)}" />
<h:commandLink id="Link2" value="Down" action="#{accountController.moveUserDown(user)}" />
<h:commandLink id="Link3" value="Delete" action="#{accountController.deleteUser(user)}" />
with
public void moveUserUp(User user) {
// ...
}
public void moveUserDown(User user) {
// ...
}
public void deleteUser(User user) {
// ...
}
See also:
How does the 'binding' attribute work in JSF? When and how should it be used?
Invoke direct methods or methods with arguments / variables / parameters in EL
The onclick attribute is used to invoke JavaScript function (client-side). It is be used when you want to attach a JavaScript click event hanlder.
"#{accountController.moveDown}" is a method-expression. And as the name suggests looks like accountController is a managed bean.
As the h:link doc says:
javax.el.ValueExpression (must evaluate to java.lang.String)
Can be a value expression that must ultimately evaluate to a string.
Javascript code executed when a pointer button is clicked over this element.
Update:
May be what you are looking for is h:commandLink. You can use the action attribute to invoke the backing bean method.
I have modified your code, let me know if this is what you are looking at achive
<h:form>
<a4j:outputPanel id="userList" ajaxRendered="false">
<ui:repeat value="#{manageUser.userList}" var="user">
<div class="user">
<h:panelGrid columns="3">
<h:outputText value="#{user.userId} ---- #{user.userName} ---- " />
<a4j:commandLink id="LinkUp" value="Up" execute="#this"
action="#{manageUser.moveUserUp}" limitRender="true" render="userList" >
<f:setPropertyActionListener value="#{user}" target="#{manageUser.user}" />
</a4j:commandLink>
<a4j:commandLink id="LinkDown" value="down"
action="#{manageUser.moveUserDown}" execute="#this" limitRender="true" render="userList" >
<f:setPropertyActionListener value="#{user}" target="#{manageUser.user}" />
</a4j:commandLink>
</h:panelGrid>
</div>
</ui:repeat>
</a4j:outputPanel>
</h:form>
Managed Beans (ManageUser)
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean(name="manageUser")
#ViewScoped
public class ManageUser implements Serializable {
/**
*
*/
private static final long serialVersionUID = -5338764155023244249L;
private List<UserBean> userList;
private UserBean user;
/**
* #return the user
*/
public UserBean getUser() {
return user;
}
/**
* #param user the user to set
*/
public void setUser(UserBean user) {
this.user = user;
}
/**
* #return the userList
*/
public List<UserBean> getUserList() {
return userList;
}
/**
* #param userList the userList to set
*/
public void setUserList(List<UserBean> userList) {
this.userList = userList;
}
public ManageUser() {
UserBean user1= new UserBean();
user1.setUserId("1");
user1.setUserName("userName1");
UserBean user2= new UserBean();
user2.setUserId("2");
user2.setUserName("userName2");
UserBean user3= new UserBean();
user3.setUserId("3");
user3.setUserName("userName3");
userList = new ArrayList<UserBean>();
userList.add(user1);
userList.add(user2);
userList.add(user3);
}
public void moveUserDown(){
if(user !=null){
int indexObj= userList.indexOf(user);
if(indexObj < userList.size()-1){
UserBean tempUser=userList.get(indexObj+1);
userList.set(indexObj+1, user);
userList.set(indexObj, tempUser);
}
}
}
public void moveUserUp(){
if(user !=null){
int indexObj= userList.indexOf(user);
if(indexObj > 0){
UserBean tempUser=userList.get(indexObj-1);
userList.set(indexObj-1, user);
userList.set(indexObj, tempUser);
}
}
}
}
UserBean
import java.io.Serializable;
public class UserBean implements Serializable {
/**
*
*/
private static final long serialVersionUID = 3820279264217591645L;
private String userName;
private String userId;
/**
* #return the userName
*/
public String getUserName() {
return userName;
}
/**
* #param userName the userName to set
*/
public void setUserName(String userName) {
this.userName = userName;
}
/**
* #return the userId
*/
public String getUserId() {
return userId;
}
/**
* #param userId the userId to set
*/
public void setUserId(String userId) {
this.userId = userId;
}
}
I have a JSF page which renders a text field depending on the value of a drop down using primefaces ajax listner. The dynamic rendering is done fine. but the problem is once I submit the form the the bound value of that textfield doesn't get bound instead it is shown as null.
this is the part of my JSF only the necessary fields are included here
<h:panelGroup id="textPanel" >
<h:form id="main" prependId="false">
<h:outputText value="WorkFlow ID:" />
<h:selectOneMenu id="workFlows" value="#{workFlowSelectionController.selectedWorkFlowId}" >
<p:ajax event="change" listener="#{workFlowSelectionController.dropDownChange}" update="textPanel"/>
<f:selectItems value="#{workFlowSelectionController.allActiveworkFlows}"/>
</h:selectOneMenu>
<p:inputText value="#{workFlowSelectionController.texField}" rendered="#{workFlowSelectionController.textfieldVisibility}"/>
<p:commandButton ajax="false" value="Next" action="#{workFlowSelectionController.addWorkFlowselectionDetails}"/>
</h:form>
</h:panelGroup>
this is my managed bean
#ManagedBean
#RequestScoped
public class WorkFlowSelectionController {
private boolean textfieldVisibility = false;
private String texField;
public void dropDownChange() {
logger.info("WorkFlowSelectionController.dropDownChange() entered");
if (selectedWorkFlowId != null) {
if (selectedWorkFlowId.equals("-1")) {
textfieldVisibility = true;
operationListStatus = false;
} else {
textfieldVisibility = false;
operationListStatus = true;
}
} else {
textfieldVisibility = false;
operationListStatus = true;
}
public void addWorkFlowselectionDetails() throws CloneNotSupportedException {
System.out.println("Selected Value of Text Field is" + texField);
}
public String getTexField() {
return texField;
}
public void setTexField(String texField) {
this.texField = texField;
}
}
i haven't included the dropdown code of the backing bean. i just need an idea of what i am doing wrong here if i remove the rendered attribute of the textfield it works fine.
thank you
Put the bean in the view scope instead of request scope. A request scoped is recreated on every single HTTP request. The boolean property will default to false again whenever you submit the form, so the submitted value won't be processed then.
#ManagedBean
#ViewScoped
public class WorkFlowSelectionController {
//
}
A view scoped bean will live as long as you're (ajax-) interacting with the same view by returning null or void from action(listener) methods.