I am trying to make a simple admin page for managing users list. The jsf code looks like this:
<h:selectOneMenu id="selectUser" value="#{adminBean.user_id}" valueChangeListener="#{adminBean.userSelected}" >
<f:selectItems value="#{adminBean.myModelUsersValues}" />
<a4j:ajax event="valueChange" render="login password privilege_list" execute="#this"/>
</h:selectOneMenu >
<table>
<tr>
<td><h:outputLabel styleClass="LabelStyle" value="login: "/></td>
</tr>
<tr>
<td>
<h:inputText id="login" value="#{adminBean.login}"/>
</td>
<td>
<h:message for="login" style="color:red"/>
</td>
</tr>
<tr>
<td><h:outputLabel styleClass="LabelStyle" value="password: "/></td>
</tr>
<tr>
<td>
<h:inputText id="password" value="#{adminBean.password}"/>
</td>
<td>
<h:message for="password" style="color:red"/>
</td>
</tr>
<tr>
<td><h:outputLabel styleClass="LabelStyle" value="privilege list: "/></td>
</tr>
<tr>
<td>
<h:selectManyListbox id="privilege_list" value="#{adminBean.privilegeList}">
<f:selectItems value="#{adminBean.privilegeValues}" />
</h:selectManyListbox >
</td>
<td>
<h:message for="privilege_list" style="color:red"/>
</td>
</tr>
</table>
<br/>
<h:commandButton id="addButton" value="Add" action="#{adminBean.addUser}" styleClass="ButtonStyle"/>
<h:commandButton id="deleteButton" value="Delete" action="#{adminBean.deleteUser}" styleClass="ButtonStyle"/>
<h:commandButton id="clearButton" value="Clear" action="#{adminBean.clear}" styleClass="ButtonStyle"/>
The problem is that when the page loads, all the items are empty. Now When I click on 'add' button I have discovered that the valueChangeListener="#{adminBean.userSelected}" runs, which replaces my privilege list with the ones from the first user. The same is when I use the clear button - all fields are empty, but when I click on the add button again, the list is the one from the first user (and only the list - no other input texts). I tried adding immediate="true" to the add button and that solves this problem, but off course then all the values I put into input text are not passed through to the adminBean.addUser action method. My bean is viewscoped (I needed to use it because of the validation error on selectManyListBox). Here is the Java code (the addUser method so far only sends a logger method and checks i login exists, and if sth was selected on the priv. list):
#ManagedBean(name="adminBean")
#ViewScoped
public class AdminBean {
private String user_id ="";
private String login ="";
private String password ="";
private ArrayList<String> privilegeList = new ArrayList<String>();
private User model = new User();
private TreeMap<String, User> usersValuesBackendMap = new TreeMap<String, User>();
private TreeMap<String, String> privilegesValues = new TreeMap<String, String>();
private TreeMap<String, String> myModelUsersValues = new TreeMap<String, String>();
...
#javax.annotation.PostConstruct
public void init()
{
usersValuesBackendMap = queryDAO.getAllUsers();
for (Map.Entry<String, User> usr : usersValuesBackendMap.entrySet()) {
myModelUsersValues.put(usr.getValue().getLogin(), usr.getKey() );
}
privilegesValues = queryDAO.getFullPrivilegeList();
user_id = "";
}
public void userSelected(ValueChangeEvent event){
String newValue = event.getNewValue().toString();
User user = usersValuesBackendMap.get(newValue);
login = user.getLogin();
password = user.getPassword();
privilegesValues.clear();
for (String privilege: user.getPrivilegeValues() ){
privilegesValues.put(privilege, privilege);
}
}
public String clear(){
user_id ="";
login ="";
password ="";
privilegesValues = queryDAO.getFullPrivilegeList();
return "";
}
Interestingly I added immediate="true" to the clearing method and then sth. opposite happens - the list is OK but the inputTexts are filled.
Some facts:
The valueChangeListener runs when !oldValue.equals(newValue).
The valueChangeListener is not a client side event listener. It's entirely server side.
The valueChangeListener is completely independent on whether it's an ajax or normal request. It's invoked on all types of requests, including the normal form submit.
You need <a4j:ajax listener> instead.
<h:selectOneMenu id="selectUser" value="#{adminBean.user_id}">
<f:selectItems value="#{adminBean.myModelUsersValues}" />
<a4j:ajax event="valueChange" listener="#{adminBean.userSelected}" render="login password privilege_list" execute="#this"/>
</h:selectOneMenu >
with
public void userSelected(AjaxBehaviorEvent event){
User user = usersValuesBackendMap.get(user_id);
// ...
}
Related
So here's my problem. I've been trying to solve this for weeks and nothing, so I'm biting the bullet and asking for help.
Basically, I'm trying to write a computer encyclopedia-cum-inventory management-cum-auditing program. Seeing that MVC is all the rage these days I decided to step out of my comfort zone of classic .NET and try MVC.
I have a model with partially the following fields:
public class SoundCard
{
public Guid Id { get; set; }
...
public virtual List<SoundChipset>? SoundChipsets { get; set; }
...
public virtual List<MidiSynthChipset>? MidiSynthChipsets { get; set; }
...
}
The model is scaffolded into creating a controller and then a set of view pages. The add view works brilliantly and I could add sound and midi chipsets as needed. The edit view is where my problem lies: I could add new sound chipsets and midi chipsets, but could not remove the added ones.
The partial code for the controller for edit is as follows:
public async Task<IActionResult> Edit(Guid id, [Bind("Id,ModelSeries,ModelName,ModelNumber,ReleaseDate,HasCDROMInterface,HasSCSIPort,HasIDEPort,HasNECCDPort,HasMatsushitaCDPort,HasWaveBlasterPort,HasRAMForWaveTable,RAMSizeKB,HasGamePort,HasMPU401Port,numAudioOutPorts,numAudioInPorts,numAudioBiDirectionalPorts,numCoaxInPorts,numCoaxOutPorts,numOpticalInPorts,numOpticalOutPorts,numMidiInPorts,numMidiOutPorts")] SoundCard soundCard)
{
...
string[] selectedSndChipsets = Request.Form["lbSoundChipsetsSelected"].ToArray();
List<SoundChipset> sndChipset = new List<SoundChipset>();
foreach (string uuid in selectedSndChipsets)
{
sndChipset.Add(Factories.SoundChipsetDDLFactory.getSoundChipsetByUUID(_context, uuid));
}
soundCard.SoundChipsets = sndChipset;
string[] selectedMidChipsets = Request.Form["lbMidiChipsetsSelected"].ToArray();
List<MidiSynthChipset> MidChipsets = new List<MidiSynthChipset>();
foreach (string uuid in selectedMidChipsets)
{
MidChipsets.Add(Factories.MidiSynthChipsetDDLFactory.getMidiSynthChipsetByUUID(_context, uuid));
}
soundCard.MidiSynthChipsets = MidChipsets;
_context.Update(soundCard);
await _context.SaveChangesAsync();
...
So, practically recreating the Sound Chipsets and Midi Chipsets lists from scratch every single time. Problem is, the program treats the list as new objects to add to the existing list, it does not erase the current list despite the list being a new one!
I've tried to apply a Clear() command to the list but instead the program tossed an NullReferenceException which is puzzling because the list is supposed to be populated.
For completeness sake, here's part of the code for the edit frontend. It's partially JS to handle moving items between two boxes:
<label asp-for="SoundChipsets" class="control-label"></label>
<table>
<tr>
<th>Available</th>
<th>↔</th>
<th>Selected</th>
</tr>
<tr>
<td>
#Html.ListBox("lbAllSoundChipsets",(IEnumerable<SelectListItem>)ViewBag.SoundChipsets, new {#id="lbAllSoundChipsets", #style="min-width: 250px;"})
</td>
<td>
<input onclick="Javascript:SwitchListBoxItems('lbAllSoundChipsets', 'lbSoundChipsetsSelected');" type="button" value="→" /><br />
<input onclick="Javascript:SwitchListBoxItems('lbSoundChipsetsSelected', 'lbAllSoundChipsets');" type="button" value="←" />
</td>
<td>
#Html.ListBox("lbSoundChipsetsSelected",(IEnumerable<SelectListItem>)ViewBag.SelectedSoundChipsets, new {#id="lbSoundChipsetsSelected", #style="min-width: 250px;"})
</td>
</tr>
</table>
</div>
<div class="form-group">
<label asp-for="MidiSynthChipsets" class="control-label"></label>
<table>
<tr>
<th>Available</th>
<th>↔</th>
<th>Selected</th>
</tr>
<tr>
<td>
#Html.ListBox("lbAllMidiChipsets",(IEnumerable<SelectListItem>)ViewBag.MidiSynthChipsets, new {#id="lbAllMidiChipsets", #style="min-width: 250px;"})
</td>
<td>
<input onclick="Javascript:SwitchListBoxItems('lbAllMidiChipsets', 'lbMidiChipsetsSelected');" type="button" value="→" /><br />
<input onclick="Javascript:SwitchListBoxItems('lbMidiChipsetsSelected', 'lbAllMidiChipsets');" type="button" value="←" />
</td>
<td>
#Html.ListBox("lbMidiChipsetsSelected",(IEnumerable<SelectListItem>)ViewBag.SelectedMidiSynthChipsets, new {#id="lbMidiChipsetsSelected", #style="min-width: 250px;"})
</td>
</tr>
</table>
And the JS code:
function SwitchListBoxItems(sourceListItem, targetListItem) {
var src = document.getElementById(sourceListItem);
var dest = document.getElementById(targetListItem);
if (dest != null && src != null) {
while (src.options.selectedIndex >= 0) {
var lstItemNew = new Option(); // Create a new instance of ListItem
lstItemNew.text = src.options[src.options.selectedIndex].text;
lstItemNew.value = src.options[src.options.selectedIndex].value;
dest.options[dest.length] = lstItemNew;
src.remove(src.options.selectedIndex);
}
}
So yeah, if someone can point me in the right direction to get the system to delete items.
Thanks in advance.
My xhtml is split in to Menu area (defaultMenu.xhtml) and Content area (defaultContent.xhtml).
The code for defaultMenu.xhtml is:
<h:form id="defaultmenuform">
<p:outputPanel id="menupanel" class="contain auto-fixed-center">
<p:panel id="pmenu" visible="#{phController.user.menuVisible}">
<table id="stutable">
<tr>
<td width="15%">
<p:outputLabel id="stuname" value="#{phController.phBean.studentName}" />
</td>
<td>
<p:tabMenu activeIndex="#{param.selectedtab}">
<p:menuitem value="Home" outcome="phome" icon="ui-icon-star">
<f:param name="selectedtab" value="0" />
</p:menuitem>
<p:menuitem value="Bank" outcome="bhome" icon="ui-icon-person">
<f:param name="selectedtab" value="1" />
</p:menuitem>
</p:tabMenu>
</td>
</tr>
</table>
</p:panel>
</p:outputPanel>
</h:form>
The defaultContent.xhtml actually displays the ph.xhtml content (as part of functional navigation) and the code is:
<ui:define name="content">
<f:event listener="#{phController.readPeople}" type="preRenderView">
</f:event>
<h:form id="form">
<p:selectOneRadio id="selstud" value="#{phController.phBean.ssSeq}" layout="custom">
<p:ajax update=":defaultmenuform:parentmenupanel :defaultmenuform:stuname" listener="#{phController.onChangePerson}"/>
<f:selectItems value="#{phController.selectStudents}" />
</p:selectOneRadio>
<div style="width: 300px; float:left;">
<p:dataGrid var="studentlist" value="#{phController.listStudent}" columns="1" rowIndexVar="stuindex">
<p:panel header="" style="text-align:left">
<h:panelGrid columns="1" style="width:100%">
<h:outputText value="#{studentlist.studentName}" />
<p:radioButton for=":form:selstud" itemIndex="#{stuindex}"/> Select
</h:panelGrid>
</p:panel>
</p:dataGrid>
</div>
</h:form>
</ui:define>
The code for backing bean is:
Map<String, Object> studentparam = new HashMap<>();
studentparam.put("studentSeq", phBean.getSsSeq());
lS = getBaseDAOService().readStudent("readStudent", studentparam);
phBean.setStudentName(lS.get(0).getStudentFirstName() + " " + lS.get(0).getStudentLastName());
As you can see, I am calling the onChangeStu method to display the selected Student Name in defaultMenu.xhtml. I am using Custom Layout p:selectOneRadio in ph.xhtml and onClick trying to update a p:outputLabel in defaultMenu.xhtml.
The backing bean method gets invoked successfully and the value is also set in variable phController.phBean.studentName, but the update is not working. I also checked using view source and the id is “:defaultmenuform:stuname”, I also tried updating the menu panel ":defaultmenuform:menupanel”, but none of this works.
Not sure how to resolve this. Please suggest.
Including the structure of all .xhtmls
<h:body id="entirePageBody">
<div id="page">
<ui:insert name="header" >
<ui:include src="/template/defaultHeader.xhtml" />
</ui:insert>
<ui:insert name="menu" >
<ui:include src="/template/defaultMenu.xhtml" />
</ui:insert>
<div id="content_div" class="auto-fixed-center">
<div id="content_div_padding" class="content-block">
<ui:insert name="content" >
<ui:include src="/template/defaultContent.xhtml" />
<ui:debug hotkey="z" />
</ui:insert>
</div>
</div>
<ui:insert name="footer" >
<ui:include src="/template/defaultFooter.xhtml" />
</ui:insert>
</div>
</h:body>
PhController.java:
public class PhController extends BaseController implements Serializable {
private List<Stud> listStudent;
private List selectStudents;
SelectItem option;
private PhBean phBean;
private Boolean menuVisible;
int counter = 0;
public PhController() {
phBean = new PhBean();
}
public void readPeople() {
listStudent = new ArrayList<Stud>();
listStudent.add(new Stud(1, "John Miller"));
listStudent.add(new Stud(2, "Scott Jackson"));
selectStudents = new ArrayList();
option = new SelectItem(listStudent.get(0).getStudentSeq(), "Select");
selectStudents.add(option);
option = new SelectItem(listStudent.get(1).getStudentSeq(), "Select");
selectStudents.add(option);
phBean.setSsSeq(String.valueOf(1));
phBean.setSelectedName(listStudent.get(0).getStudentName());
menuVisible = true;
}
public void onChangePerson() {
phBean.setSelectedName(listStudent.get(1).getStudentName());
}
// Getters and Setters
}
PhBean.java:
public class PhBean implements Serializable {
private String ssSeq;
private String studName; // Used to display the name in the Menu bar.
private String selectedName;
public PhBean() {
}
// Getters and Setters
}
I'd say that in the p:ajax in defaultContent.xhtml the list of components to be updated should be separated with spaces only, no commas - so try changing this:
update=":defaultmenuform:menupanel, :defaultmenuform:stuname"
to this:
update=":defaultmenuform:menupanel :defaultmenuform:stuname"
UPDATE
I played with this a bit more and may have found a clue - please add the following code to defaultmenuform:
<p:messages autoUpdate="true" showDetail="true" />
This should help us tracking the reason for failed validation (in case failing validation is the root cause for you - as I said, I have rather limited possibility to reproduce this issue).
Anyway, when I selected some item in p:selectOneRadio, an error message like this appeared:
Conversion Error setting value 'test001.Student#4110c95c' for 'null Converter'.
And the root cause was on this row:
<p:selectOneRadio id="selstud" value="#{phController.phBean.ssSeq}" layout="custom">
p:selectOneRadio expects only String to be passed as a value - and ssSeq is very likely of a different type. Try to change the way value is populated to ensure it is always String - maybe a different attribute of the phBean or simply a brand new String one.
NOTE: if this doesn't help, maybe you could update your question with very simplified example of how phController and phBean could look like if we are to test it.
UPDATE #2
So you have explained there is a problem that you want to call phController.readPeople every time the page is loaded/refreshed, but instead it gets loaded with each and every Ajax request, thus overwriting the values.
In your PhController (it is a bean, right? session scoped?) you could add something like this (omitted null checks for the sake of readability):
public void readPeopleOnGet() {
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ec = fc.getExternalContext();
HttpServletRequest req = (HttpServletRequest) ec.getRequest();
String reqMethod = req.getMethod();
if ("GET".equals(reqMethod)) {
readPeople();
}
}
With the above method you could keep this part of your defaultContext.xhtml in place, provided it is actually called (I assume so), just with the listener method changed:
<f:event listener="#{phController.readPeopleOnGet}" type="preRenderView">
</f:event>
The method readPeopleOnGet will still be called with every request to the page, but since Ajax requests are POST, it will only call readPeople when the page is loaded or refreshed as whole.
This may not be a "perfectly clean" solution, but seemed to work properly when I tested it.
UPDATE #3
Well, since you use PrimeFaces, it would be possible to identify Ajax call also this way:
public void readPeopleOnGet() {
RequestContext rc = RequestContext.getCurrentInstance();
if (!rc.isAjaxRequest()) {
readPeople();
}
}
But if I got your point from latest comments, you want to only run readPeople when the page is loaded for the very first time - so the following part could be even better for that.
You didn't answer if PhController is actually a bean, but I assume it is (there were no annotations visible from the code you posted). You may try making it #SessionScoped:
#ManagedBean
#SessionScoped
public class PhController extends BaseController implements Serializable {
// the rest of the PhController goes here...
Then you could add something like this to the PhController:
#PostConstruct
private void init() {
readPeople();
}
The annotation #PostConstruct ensures the method init is called exactly once after the bean was created. You can continue calling the method readPeople from other places as necessary, while removing both the <f:event ... /> and readPeopleOnGet as these will no longer be needed.
I have two selectOneMenu. I choose item from selectOneMenu1 and I add item value to where condition for query. Query result is successful. But I didn't put query result to selectOneMenu2. selectOneMenu2 is empty everytime. I add managedBean and xhtml page code about this issue.
// BirimManagedBean about above issue
#Override
public void processAjaxBehavior(AjaxBehaviorEvent event) throws AbortProcessingException {
String birimRequested = deger;
byte birimId = Byte.parseByte(
bolumManagedBean.bolumBilgileriniGetir(birimId);
}
// BolumManagedBean about above issue
public void bolumBilgileriniGetir(byte id) {
bolumler = new ArrayList<Bolum>();
Session session = HibernateUtil.getSessionFactory().openSession();
Query query = session.createQuery("from Bolum b where b.birim.birim_id = :id");
query.setParameter("id", id);
bolumler = query.list();
}
// yeni_kayit.xhtml about above issue
<p:selectOneMenu id="birimi" value="#{birimMBean.deger}" style="float: left;" >
<f:selectItems value="#{birimMBean.birimler}" var="birim" itemLabel="#{birim.birim_adi}" itemValue="#{birim.birim_id}" />
<p:ajax event="change" listener="#{birimMBean.processAjaxBehavior}" />
</p:selectOneMenu>
<br/><br/>
<p:selectOneMenu id="bolumu" value="#{bolumMBean.secilenBolum}" style="float: left;">
<f:selectItems value="#{bolumMBean.bolumler}" var="bolum" itemLabel="#{bolum.bolum_adi}" itemValue="#{bolum.bolum_id}" />
</p:selectOneMenu>
add update="bolumu" to the p:ajax tag.
I have a userCreateDate Field, When I Add the data for the first time, it gets inserted into resp. table,but when I try to update the same record by onClick of Javascript and setting the value to the respective textfields, it gives Invalid field value for field "userTypeEntity.userCreatedDate". and the date field value is coming null in validate() method.
Can you help me get out of this error.
My JSP is:
<table width="100%" style="border-collapse: collapse;" border="0" bordercolor="#006699" align="center">
<tr>
<s:hidden id="id" name="userGroupEntity.userGroupId"/>
<s:textfield id="name" name="userGroupEntity.userGroupName" key="label.groupName" maxlength="15" size="30" required="true" labelSeparator=""/>
<s:textfield id="desc" name="userGroupEntity.userGroupDesc" key="label.groupDesc" maxlength="20" size="30" required="true" labelSeparator=""/>
<s:textfield id="cdate" name="userGroupEntity.userCreatedDate" key="label.CreatedDt" size="30" readonly="true" cssStyle="background-color:#E7EBDD;" labelSeparator=""/>
<s:textfield id="mdate" name="userGroupEntity.userModifiedDate" key="label.ModifiedDt" size="30" readonly="true" cssStyle="background-color:#E7EBDD;" labelSeparator=""/>
<s:radio id="status" name="userGroupEntity.userActive" key="label.status" list="userGroupStatus" required="true" labelSeparator=""/>
</tr>
<tr>
<td colspan="100%" align="center"><hr></td>
</tr>
<tr>
<td colspan="100%" align="center">
<s:submit id="save" theme="simple" name="save" key="label.save"/>
<s:reset id="reset" theme="simple" name="reset" key="label.reset"/>
<s:a href="userGroupView">
<img border="0" src='<s:url value="/images/b_cancel.gif"></s:url>' width="50" height="20" alt="Go to User Group Master">
</s:a>
</td>
</tr>
<tr>
<td colspan="100%">
<s:if test="hasActionMessages()"><div style="color:red;"><s:actionmessage/></div></s:if>
</td>
</tr>
</table>
<s:if test="{groupList.size() != 0}">
<s:iterator value="groupList" var="group">
<tr>
<td><s:property value="userGroupName"/></td>
<td><s:property value="userGroupDesc"/></td>
<td><s:property value="userCreatedDate"/></td>
<td><s:property value="userModifiedDate"/></td>
<td align="center"><b>Edit</b>
| <b>Delete</b>
</td>
</tr>
</s:iterator>
</s:if>
And My Action Class is:
public class UserGroupAction extends ActionSupport implements Preparable{
private static Log log = LogFactory.getLog(UserGroupAction.class);
/**
*
*/
private static final long serialVersionUID = 1L;
private String result;
private UserGroupEntity userGroupEntity;
private Map<String, String> userGroupStatus;
private List<UserGroupEntity> groupList;
private int userSaveStatus;
private int groupId;
public String userGroupSave() throws Exception{
log.info("...In UserGroupAction.userGroupSave...");
// int userSaveStatus = 1;
try {
int groupId = userGroupEntity.getUserGroupId();
System.out.println("Group Id: "+groupId);
System.out.println("Group Name: "+userGroupEntity.getUserGroupName());
if(groupId == 0){
userSaveStatus = UserGroupController.userGroupSave(userGroupEntity);
System.out.println("userSaveStatus: "+userSaveStatus);
if(userSaveStatus == 1 ){
addActionMessage("Group Added Successfully.");
result = SUCCESS;
}else{
addActionMessage("Group Already Exists.");
result = SUCCESS;
}
}else{
userSaveStatus = UserGroupController.userGroupSave(userGroupEntity,groupId);
System.out.println("userSaveStatus: "+userSaveStatus);
if(userSaveStatus == 1 ){
addActionMessage("Group Updated Successfully.");
result = SUCCESS;
}else{
addActionMessage("Group Already Exists.");
result = SUCCESS;
}
}
} catch (Exception e) {
System.out.println("...In Catch of UserGroupAction.execute ...");
result = ERROR;
e.printStackTrace();
}
return result;
}
#Override
public void validate(){
System.out.println("...In UserGroupAction.validate ...");
if(isEmpty(userGroupEntity.getUserGroupName())){
addFieldError("userGroupEntity.userGroupName", "Group name can't be empty.");
}
if(isEmpty(userGroupEntity.getUserGroupDesc())){
addFieldError("userGroupEntity.userGroupDesc", "Group description can't be empty.");
}
if(isEmpty(userGroupEntity.getUserActive())){
addFieldError("userGroupEntity.userActive", "Please select status.");
}
//System.out.println("Create Date:: "+userGroupEntity.getUserCreatedDate());
}
}
Here all other testField values are coming in action, but date field values are coming null... Please Help. Am Stucked here...
My Javascript is:
function userGroupEdit(flag,id,name,desc,cdate,mdate){
alert(flag+id+name+desc+cdate+mdate);
if(flag=='edit'){
document.getElementById("id").style.display="block";
document.getElementById("id").value=parseInt(id);
document.getElementById("name").value=name;
document.getElementById("desc").value=desc;
document.getElementById("cdate").value=cdate;
document.getElementById("mdate").value=mdate;
document.getElementById("statusY").checked=true;
}else{
//document.getElementById("save").style.display="block";
}
//window.location = url;//"${pageContext.request.contextPath}/"+url;
return true;
}
I got the solution: In Javascript I formatted the date as
function setDateFormat(strDate){
//strDate '2014-06-18'
//alert(strDate.length);
if(strDate!=''){
var y,m,d;
y = strDate.substring(0,4);//alert(y);
m = strDate.substring(5,7);//alert(m);
d = strDate.substring(8,10);//alert(d);
var newDate = m+"/"+d+"/"+y;
//alert(newDate);
return newDate;
}else{
if(typeof(strDate) == 'string'){
newDate = '';
return newDate;
}
}
}
These date fields are now getting persisted in DB and there is no Struts2 field message like Invalid field value for field from validate method.
Please I am missing a point here. How can I handle a request with multiple outcomes in JSF 2.0 using the implicit navigation
For example using the explicit navigation I can write the following:
<navigation-rule>
<from-view-id>/products/DeleteItem.xhtml</from-view-id>
<navigation-case>
<from-outcome>itemDeleted</from-outcome>
<to-view-id>/products/Success.xhtml</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>itemNotExist</from-outcome>
<to-view-id>/products/Failure.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
However How can I achieve the same thing with the implicit navigation, I have tried the following :
<h:link value="Delete a Product" outcome="Success" />
only one case is mentioned but I want to forward either to Success.xhtml or Failure.xhtml depending on the outcome.
Here a some additional information for the implicit navigation.
Managed bean:
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
#ManagedBean
#RequestScoped
public class DeletionBean {
#EJB
private app.session.ProductFacade ejbProductFacade;
private int id=0;
public DeletionBean() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String deleteProduct(){
String result;
result=ejbProductFacade.deleteItem(id);
if(result.equals("success"))
{
status="Successfully performed"; //msg to be shown
return "product/DeleteItem";
}
else return "product/Failure";
}
}
Form:
<h:form>
<table border="1" width="5" cellspacing="1" cellpadding="1">
<tr>
<td> Product ID </td>
<td>
<h:inputText id="pid" size="10" value="#{deletionBean.id}" title="Product ID" required="true" requiredMessage="Product ID required"/>
</td>
</tr>
<tr>
<td colspan="2">
<h:commandLink value="Delete" action="#{deletionBean.deleteProduct}" />
</td>
</tr>
</table>
</h:form>
Current error message:
Unable to find matching navigation case with from-view-id '/product/DeleteItem.xhtml' for action '#{deletionBean.deleteProduct}' with outcome 'product/Failure'
Supposing you'll perform an action method to delete your product, you should make your method return the desired outcome. Using JSF 2, there's no need of using outcome ids anymore, even you can declare them in your faces-config.xml file, JSF ties a provided outcome with a specific page:
<h:commandLink action="#{bean.deleteProduct(product)}"
value="Delete product" />
public String deleteProduct(Product p){
//Try to delete the product and return "products/xxx",
//which will be converted to "/contextname/products/xxx.xhtml"
try{
daoService.delete(p);
return "/products/Success";
catch(Exception e){
return "/products/Failure";
}
}
That will POST the server and behaves like an HTML form submit button (that's why it's called commandLink). Nevertheless if you want just to perform a GET to an specific view, you can use its view id in the outcome:
<h:link value="Go to success doing nothing"
outcome="/products/Success" />
See also:
Implicit navigation in JSF 2