问题
I am using @ViewScoped Bean for small CRUD application I have a edit and view page but when I click buttons (edit) it will render edit form. After edit form appears the save button or cancel button does not call the function but renders the whole page. The actionListener's function is not called at all and everthing is initialized. Is something wrong with my bean and page?? I am using JSF 2 with richfaces and facelet.
//ViewScoped Bean
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.legendMgr.Legend;
import java.io.Serializable;
import java.sql.SQLException;
import java.util.List;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
/**
*
* @author kitex
*/
@ManagedBean(name = "legendbean")
@ViewScoped
public class LegendController implements Serializable {
LegendDTO legendDTO;
String selectedLegend;
List<LegendDTO> legendDTOs;
boolean edit;
public List<LegendDTO> getLegendDTOs() {
return legendDTOs;
}
public void setLegendDTOs(List<LegendDTO> legendDTOs) {
this.legendDTOs = legendDTOs;
}
@PostConstruct
void initialiseSession() {
FacesContext.getCurrentInstance().getExternalContext().getSession(true);
}
public LegendController() {
if (!edit) {
legendDTO = new LegendDTO();
legendDTO.getList().add(new Legend());
legendDTOs = getLegends();
}
}
public String getSelectedLegend() {
return selectedLegend;
}
public void setSelectedLegend(String selectedLegend) {
this.selectedLegend = selectedLegend;
}
public boolean isEdit() {
return edit;
}
public void setEdit(boolean edit) {
this.edit = edit;
}
public LegendDTO getLegendDTO() {
return legendDTO;
}
public void setLegendDTO(LegendDTO legendDTO) {
this.legendDTO = legendDTO;
}
public void addLegendRange() {
Logger.getLogger(LegendController.class.getName()).warning("List Size " + legendDTO.getList().size());
legendDTO.getList().add(new Legend());
Logger.getLogger(LegendController.class.getName()).warning("List Size " + legendDTO.getList().size());
}
public void removeLegendRange(Legend legend) {
if (legendDTO.getList().size() != 1) {
legendDTO.getList().remove(legend);
}
}
public String saveLegend() {
Logger.getLogger(LegendController.class.getName()).warning("Save Legend Edit" + edit);
LegendDAO dao = new LegendDAO();
if (dao.addLegend(legendDTO, edit)) {
edit = false;
Logger.getLogger(LegendController.class.getName()).warning("Save Legend Edit" + edit);
} else {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Could Not Save Confim if you have already defined Legend " + legendDTO.getLegendName() + "!"));
}
return "";
}
public String cancel() {
edit = false;
legendDTO = new LegendDTO();
legendDTO.getList().add(new Legend());
return "";
}
public List<LegendDTO> getLegends() {
LegendDAO dao = new LegendDAO();
return dao.getLegendDTO();
}
//All function from here are for legend delete
public void deleteLegendType(LegendDTO dto) {
LegendDAO dao = new LegendDAO();
if (dao.deleteLegendType(dto.getLegendName())) {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Deleted !"));
} else {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Deleted Error !"));
}
}
//All function from here is to legend edit
public void editLegendType(LegendDTO dto) {
edit = true;
Logger.getLogger(LegendController.class.getName()).warning("DTO : " + dto.legendName);
legendDTO = dto;
LegendDAO dao = new LegendDAO();
Logger.getLogger(LegendController.class.getName()).warning("Edit dto set");
try {
List<Legend> legends = dao.getDetailForEditLegend(dto.getLegendName());
if (legends == null || legends.isEmpty()) {
dto.getList().add(new Legend());
} else {
dto.setList(legends);
}
} catch (SQLException ex) {
Logger.getLogger(LegendController.class.getName()).warning("SQL EXception has occoured");
}
Logger.getLogger(LegendController.class.getName()).warning("In Edit Legend Function The size of list" + dto.getList().size());
}
}
//xhtml code
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:rich="http://richfaces.org/rich"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<ui:composition template="/legendTemplate.xhtml">
<ui:define name="windowTitle">Change Legend</ui:define>
<ui:define name="content">
<h:messages globalOnly="true"/>
<rich:panel id="firstPanel">
<h:form id="nis_viewLegend">
<rich:dataTable id="data_tbl" value="#{legendbean.legendDTOs}" var="legendDTOvar" style="width:100%" rendered="#{!legendbean.edit and not empty legendbean.legendDTOs}">
<rich:column>
<f:facet name="header">
<h:outputText value="Description"/>
</f:facet>
<h:outputText value="#{legendDTOvar.desc}"/>
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="Legend Type"/>
</f:facet>
<h:outputText value="#{legendDTOvar.legendName}"/>
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="Legend Type"/>
</f:facet>
<h:outputText value="#{legendDTOvar.legendFor}"/>
</rich:column>
<rich:column>
<a4j:commandLink value="Delete" actionListener="#{legendbean.deleteLegendType(legendDTOvar)}" render=":firstPanel"/>
<h:outputText value="/"/>
<a4j:commandLink value="Edit" actionListener="#{legendbean.editLegendType(legendDTOvar)}" render=":secondPanel :editLegendForm :nis_viewLegend"/>
</rich:column>
</rich:dataTable>
</h:form>
</rich:panel>
<rich:panel id="secondPanel">
<h:form id="editLegendForm" rendered="#{legendbean.edit}">
<h:outputText value="Legend Name"/><br/>
<h:inputText value="#{legendbean.legendDTO.legendName}" readonly="true"/><br/>
<h:outputText value="Description"/><br/>
<h:inputText value="#{legendbean.legendDTO.desc}"/><br/>
<h:outputText value="Legend For"/><br/>
<h:inputText value="#{legendbean.legendDTO.legendFor}"/><br/>
<br/>
<h:outputText value="Range" />
<rich:dataTable id="editDataPnl" value="#{legendbean.legendDTO.list}" var="legend" style="width:100%">
<rich:column>
<f:facet name="header">
<h:outputText value="SN"/>
</f:facet>
<h:inputText value="#{legend.sn}"/>
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="Description"/>
</f:facet>
<h:inputText value="#{legend.desc}"/>
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="Lower Range"/>
</f:facet>
<h:inputText value="#{legend.lowerRange}"/>
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="Upper Range"/>
</f:facet>
<h:inputText value="#{legend.upperRange}"/>
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="Color"/>
</f:facet>
<h:inputText value="#{legend.color}"/>
</rich:column>
<rich:column>
<a4j:commandLink value="Add" actionListener="#{legendbean.addLegendRange}" render=":secondPanel"/>
<h:outputText value=" / "/>
<a4j:commandLink value="Remove" actionListener="#{legendbean.removeLegendRange(legend)}" render=":secondPanel"/>
</rich:column>
</rich:dataTable>
<br/>
<center>
<a4j:commandButton value="SAVE" action="#{legendbean.saveLegend()}" render=":firstPanel :secondPanel"/>
<a4j:commandButton value="CANCEL" action="#{legendbean.cancel()}" render=":firstPanel :secondPanel"/>
</center>
</h:form>
</rich:panel>
</ui:define>
</ui:composition>
</h:body>
</html>
回答1:
In ViewScope, once the view is built, for example form.xhtml
, its data will last as long you do not go away from this view. To stay in the same view you should call methods that has return type void
(which are usually used in actionListener
property) or return null, in case of returning an outcome for navigation.
- Method expression
In your case your methods are void but instead of passing it to the action listener you're calling it in the view.
Try changing similar code like this:
<a4j:commandButton value="SAVE" actionListener="#{legendbean.saveLegend()}" render="mainPnl"/>
To this:
<a4j:commandButton value="SAVE" actionListener="#{legendbean.saveLegend}" render="mainPnl"/>
As actionListener
property already expects a method expression.
- Form inside dataTable
Also I noticed you have a form inside your dataTable. That could lead to strange behavior because your form has an id
it will be repeated in the resulting page. For that you should try placing the form outside the dataTable.
Even better you could have only one form enclosing the entire code as nested forms are invalid HTML code.
I would suggest you check your legendTemplate.xhtml
against nested forms too.
- Bean construction
In order to initialize your bean state it is recommended to use a @PostContruct
method instead of the bean constructor.
Try changing from this:
public LegendController() {
legendDTO = new LegendDTO();
legendDTO.getList().add(new Legend());
}
To this:
@PostConstruct
public void reset() {
legendDTO = new LegendDTO();
legendDTO.getList().add(new Legend());
}
And delete your constructor.
Your bean data should be kept as long as you're in the same view (aka .xhtml page).
I hope it helps.
回答2:
I am having the same problem. I don't think the answers above really address the question. He is not having a problem as a result of submitting any forms - pressing those buttons themselves results in the whole page re-rendering, which means that the reference to the view state is already gone before the button is pressed. This is what I am observing as well. For me, it only happens when there are a lot of large search results, and simply re-submitting the same search I just did re-renders the page (without executing the search). I believe the problem has to do with a limit on the amount of data that can be passed to the server in a form: in view scope, all of the data is serialized and passed around in one long hidden value as a value in the form. If that value is too long the server won't accept it and, therefore, will not remember the previous state.
I know this is not definitive, but this is only thread out there on this problem I can find so I hope it helps shed light for others or inspires better information. If you have something more definitive please let us know.
Edit: I am convinced now that this was the problem. My model bean had a reference to a file blob. Once I replaced that reference with a boolean (only needed to know if it existed) the problem went away. Try passing around references to your DTOs/DAOs instead of the objects themselves, or mark them as "transient" where you don't need them to persist. Or, if possible, lighten the objects as I did.
回答3:
Did you try to return null for functions saveLegend() and cancel() instead of returning empty string?
public String saveLegend() {
---------
return null;
}
public String cancel() {
----------
return null;
}
Return can also be void for ajax request. But if I remember it correctly for richfaces returning null is the only solution. Give it a try. :) The cancel() works as it reinitialize the bean.
As answered by Bento you cannot pass values using actionListener. Use action instead.
Further Reading:
- JSF 2 ViewScope questions
- JSF2 Action parameter
- Differences between action and actionListener
来源:https://stackoverflow.com/questions/12796348/jsf-viewscoped-bean-state-is-lost