How to make a form submit when its rendered based on a value in a request scoped bean - forms

I discovered a problem in my little program, and Im wondering if anyone have any tips or advice on how to solve this problem as best as possible.
I have the bean testBean which is in request scope. It contains the following:
public class testBean {
private boolean internal = false;
private String input = "";
public String internalTrue() {
System.out.println("Set internal true");
setInternal(true);
return null;
}
public String submitForm() {
System.out.println("");
return null;
}
public boolean isInternal() {
return internal;
}
public void setInternal(boolean internal) {
this.internal = internal;
}
public String getInput() {
return input;
}
public void setInput(String input) {
this.input = input;
}
}
My file welcomeJSF.jsp contains this:
<f:view>
<h:form>
<h:commandButton value="Set internal true" action="#{testBean.internalTrue}" />
</h:form>
<h:panelGrid columns="1" rendered="#{testBean.internal}">
<h:form>
<h:outputText value="JavaServer Faces" /><h:inputText value="#{testBean.input}" />
<h:commandButton value="Go" action="#{testBean.submitForm}" />
</h:form>
</h:panelGrid>
</f:view>
When I run the application Im presented with the button "Set internal true". I click it and Im presented with the form where I have the button "Go". Clicking "Go" does not trigger the method in my bean, most likely because of the field actually not being rendered on the server anymore, and thus it wont run the method. Is there any smart solutions to this?
In advance, thanks for your time.

The children of the panel will never decode input because its rendered attribute always evaluates to false during the APPLY REQUEST VALUES phase. This is a sensible thing to do from a security point of view.
(source: ibm.com)
One thing you could do is take advantage of the fact that JSF components maintain state for the lifetime of the view.
The new bean:
public class TestBean {
private String input = null;
private UIComponent panel;
public String internalTrue() {
panel.setRendered(true);
return null;
}
public String submitForm() {
panel.setRendered(false);
System.out.println("submitForm");
return null;
}
public UIComponent getPanel() { return panel; }
public void setPanel(UIComponent panel) { this.panel = panel; }
public String getInput() { return input; }
public void setInput(String input) { this.input = input; }
}
The new view bound to the bean:
<f:view>
<h:form>
<h:commandButton value="Set internal true"
action="#{testBean.internalTrue}" />
</h:form>
<h:panelGrid binding="#{testBean.panel}" columns="1"
rendered="false">
<h:form>
<h:outputText value="JavaServer Faces" />
<h:inputText value="#{testBean.input}" />
<h:commandButton value="Go" action="#{testBean.submitForm}" />
</h:form>
</h:panelGrid>
</f:view>
Using the binding attribute on the panelGrid will cause setPanel to be called when the view is created/restored.
Note that you may have some testing to do depending on how your implementation's and/or libraries' StateManager stores views between requests (which may in turn be affected by the javax.faces.STATE_SAVING_METHOD init parameter). The view might be stored in a hidden field in the form, in the session keyed to the view ID (which may cause collisions with multiple browser windows), in the session keyed to a unique ID created by a GET or JSF navigation, or by some completely custom mechanism. The pluggable nature of the framework makes it versatile, but in this case it means you need to double-check how your implementation behaves.

Related

how can I get the value of " #{readeredit.reader.tel}" form the jsf form? [duplicate]

I have started learning JSF, but sadly most tutorials out there present only a log in or a register section.
Can you point me to some more in depth examples? One thing I'm interested in is a page presenting a list of products. I'm on page home and I press on page products so that I can see the latest products added. And every time I visit the page, the product list will be created from the latest entries in the database. How can I handle this?
One way to solve this would be to create a session scoped managed bean in which I would place different entities updated through other managed beans. I found this kind of approach in some tutorials, but it seems quite difficult and clumsy.
Which would be the best approach to solve a thing like this? What is the correct usage of session scope in two-page master-detail user interface?
What is the correct usage of session scope
Use it for session scoped data only, nothing else. For example, the logged-in user, its settings, the chosen language, etcetera.
See also:
How to choose the right bean scope?
And every time I visit the page, the product list will be created from the latest entries in the database. How can I handle this?
Typically you use the request or view scope for it. Loading of the list should happen in a #PostConstruct method. If the page doesn't contain any <h:form>, then the request scope is fine. A view scoped bean would behave like a request scoped when there's no <h:form> anyway.
All "view product" and "edit product" links/buttons which just retrieve information (i.e. idempotent) whould be just plain GET <h:link> / <h:button> wherein you pass the entity identifier as a request parameter by <f:param>.
All "delete product" and "save product" links/buttons which will manipulate information (i.e. non-idempotent) should perform POST by <h:commandLink>/<h:commandButton> (you don't want them to be bookmarkable/searchbot-indexable!). This in turn requires a <h:form>. In order to preserve the data for validations and ajax requests (so that you don't need to reload/preinitialize the entity on every request), the bean should preferably be view scoped.
Note that you should basically have a separate bean for each view and also note that those beans doesn't necessarily need to reference each other.
So, given this "product" entity:
#Entity
public class Product {
#Id
private Long id;
private String name;
private String description;
// ...
}
And this "product service" EJB:
#Stateless
public class ProductService {
#PersistenceContext
private EntityManager em;
public Product find(Long id) {
return em.find(Product.class, id);
}
public List<Product> list() {
return em.createQuery("SELECT p FROM Product p", Product.class).getResultList();
}
public void create(Product product) {
em.persist(product);
}
public void update(Product product) {
em.merge(product);
}
public void delete(Product product) {
em.remove(em.contains(product) ? product : em.merge(product));
}
// ...
}
You can have this "view products" on /products.xhtml:
<h:dataTable value="#{viewProducts.products}" var="product">
<h:column>#{product.id}</h:column>
<h:column>#{product.name}</h:column>
<h:column>#{product.description}</h:column>
<h:column>
<h:link value="Edit" outcome="/products/edit">
<f:param name="id" value="#{product.id}" />
</h:link>
</h:column>
</h:dataTable>
#Named
#RequestScoped
public class ViewProducts {
private List<Product> products; // +getter
#EJB
private ProductService productService;
#PostConstruct
public void init() {
products = productService.list();
}
// ...
}
And you can have this "edit product" on /products/edit.xhtml:
<f:metadata>
<f:viewParam name="id" value="#{editProduct.product}"
converter="#{productConverter}" converterMessage="Unknown product, please use a link from within the system."
required="true" requiredMessage="Bad request, please use a link from within the system."
/>
</f:metadata>
<h:messages />
<h:form rendered="#{not empty editProduct.product}>
<h:inputText value="#{editProduct.product.name}" />
<h:inputTextarea value="#{editProduct.product.description}" />
...
<h:commandButton value="save" action="#{editProduct.save}" />
</h:form>
#Named
#ViewScoped
public class EditProduct {
private Product product; // +getter +setter
#EJB
private ProductService productService;
public String save() {
productService.update(product);
return "/products?faces-redirect=true";
}
// ...
}
And this converter for <f:viewParam> of "edit product":
#Named
#RequestScoped
public class ProductConverter implements Converter {
#EJB
private ProductService productService;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.isEmpty()) {
return null;
}
try {
Long id = Long.valueOf(value);
return productService.find(id);
} catch (NumberFormatException e) {
throw new ConverterException("The value is not a valid Product ID: " + value, e);
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) {
return "";
}
if (value instanceof Product) {
Long id = ((Product) value).getId();
return (id != null) ? String.valueOf(id) : null;
} else {
throw new ConverterException("The value is not a valid Product instance: " + value);
}
}
}
You can even use a generic converter, this is explained in Implement converters for entities with Java Generics.
See also:
How to navigate in JSF? How to make URL reflect current page (and not previous one)
JSF Controller, Service and DAO
JSF Service Layer
How to inject #EJB, #PersistenceContext, #Inject, #Autowired, etc in #FacesConverter?
Communication in JSF 2.0 - Contains several examples/hints
As a small improvement to what BalusC recommended, sometimes you can remove the required / requiredMessage part from the <f:viewParam> of your "details" screen and instead use the conditional rendering of the editing form (as BalusC did) with a reverse condition for recommending a specific link for the "list/master" screen or, even use a viewAction that would test the param and force a redirect to that list.

Old values in input fields by GET request

I have got this JSF form in the file loginform.xhtml:
<h:form>
<h:panelGrid columns="3" styleClass="components" cellpadding="5px">
<h:outputText value="#{msg['login.username']}"/>
<h:inputText id="username" value="#{userManager.loginUser.username}" required="true"/>
<h:message styleClass="error" for="username"/>
<h:outputText value="#{msg['login.password']}"/>
<h:inputSecret id="password" value="#{userManager.loginUser.password}"
required="true"/>
<h:message styleClass="error" for="password"/>
<h:commandButton value="#{msg['login.confirm']}"
action="#{userManager.doLogin}"/>
</h:panelGrid>
</h:form>
With this ManagedBean:
public class UserManager implements Serializable {
/**
* Creates a new instance of UserManager
*/
public UserManager() {
}
private UserRecord loginUser = new UserRecord();
private UserRecord sessionUser;
#EJB
private UserRecordFacadeLocal userRecordFacade;
public UserRecord getLoginUser() {
return loginUser;
}
public void setLoginUser(UserRecord loginUser) {
this.loginUser = loginUser;
}
public UserRecord getSessionUser() {
return sessionUser;
}
public void setSessionUser(UserRecord sessionUser) {
this.sessionUser = sessionUser;
}
public String doLogout() {
setSessionUser(null);
return "logout";
}
public String doLogin() {
if (userRecordFacade.authorizedAcces(loginUser.getUsername(), loginUser.getPassword())) {
setSessionUser(loginUser);
return "success";
}
return "failure";
}
}
Here is my question: if I type a GET request to loginform.xhtml (in my case: http://localhost:8080/Impetus-web/loginform.xhtml), the form is filled by the old values! Even more correct values - this is really bad for the security of the system :-). The same happens, if I make the navigation to this page via h:link tag. It works fine only in the case, if I jump to the page via POST request (via commandButton f. e.).
How is it possible?
JSF doesn't do that (as evidence, look in generated HTML output). The webbrowser does that. This feature is called "autofill"/"autocomplete". Just tell it to not do that by adding autocomplete="off" to the individual input components.
<h:inputText ... autocomplete="off" />
<h:inputSecret ... autocomplete="off" />
Or if you're on JSF 2.2 (or are using OmniFaces Html5RenderKit), you could also set it form-wide.
<h:form ... autocomplete="off">

JSF view displaying outdated value

I have two entities and a backing bean in my application. Following, a simplified version of it:
Controller and Models:
class BackingBean {
private List<A> collectionOfA;
private A currentA;
private B currentB;
private String newDescription;
// accessors
public void prepareForUpdate(ActionEvent e) {
currentA = (A) e.getComponent().getAttributes().get("a");
currentB = (B) e.getComponent().getAttributes().get("b");
}
public void save(ActionEvent e) {
// method to save b
b.setName("changing the name");
b.setSomeNumber(2);
b.setDescription(newDescription);
entityManager.merge(b);
}
}
#Entity
class A {
private String name;
#OneToMany
private List<B> bs;
}
#Entity
class B {
private String name;
private String description;
private int someNumber;
}
View:
<div>
<!-- some popup with inputs for updating B -->
<h:inputText value="#{backingBean.currentB}" />
<h:commandLink actionListener="#{backingBean.save}" />
</div>
<ui:repeat value="#{backingBean.collectionOfA}" var="a">
<h:outputText>#{a.name}</h:outputText>
<ui:repeat value="#{a.bs}" var="b">
#{b.name}
#{b.description}
#{b.someNumber}
<h:commandLink actionListener="#{backingBean.prepareForUpdate}">
<f:attribute name="a" value="#{a}" />
<f:attribute name="b" value="#{b}" />
</h:commandLink>
</ui:repeat>
</ui:repeat>
Assuming that, when I click the commandLink for prepareForUpdate(), the popup shows, my problem is this: when I save the currentB entity, every field of the entity is updated in the view. However, an instant after, the field b.description is rendered again with the old value. When I check the database, the description is, in fact, updated, as it is if I refresh the page.
Any thoughts on why this is happening?

Struts 2: updating a list of objects from a form with model driven architecture

I already searched and found several approaches here, but I can't get them working for my project.
I want to show an edit page for a list of objects, which should all be updated at once. I use the model driven architecture approach to achieve this, but I can't get it running properly. I can always display and iterate the list and its values, but I can't modify its values.
So here is what I'm currently doing:
I have a Model 'Teilzeitgrad' in my database, which has some simple attributes with getters and setters.
public class Teilzeitgrad {
private Date datumAb;
private Date datumBis;
private double betrag;
// ... getters and setters
}
In my Action-Class I implement the ModelDriven Interface with a List of Teilzeitgrad-Objects
public class DienstabschnittViewJahrAction implements ModelDriven<List<Teilzeitgrad>>, Preparable
{
List<Teilzeitgrad> teilzeitgrads;
private String tzgTypKey;
private Integer jahrIndex;
public String execute() {
return SUCCESS;
}
public List<Teilzeitgrad> getModel()
{
if(teilzeitgrads == null) {
teilzeitgrads = getTeilzeitgradListByTypAndJahr(getTzgTypKey(), getJahrIndex());
}
return teilzeitgrads;
}
public List<Teilzeitgrad> getTeilzeitgrads()
{
return teilzeitgrads;
}
public void setTeilzeitgrads(List<Teilzeitgrad> teilzeitgrads)
{
this.teilzeitgrads = teilzeitgrads;
}
#Override
public void prepare() throws Exception
{
// TODO Auto-generated method stub
}
public String getTzgTypKey()
{
return tzgTypKey;
}
public void setTzgTypKey(String tzgTypKey)
{
this.tzgTypKey = tzgTypKey;
}
public Integer getJahrIndex()
{
return jahrIndex;
}
public void setJahrIndex(Integer jahrIndex)
{
this.jahrIndex = jahrIndex;
}
}
The action mapping in struts.xml is defined as follows:
<action name="*/auth/GroupAdmin/processEditDienstabschnittJahr" method="execute" class="org.hocon.ul.portal.action.DienstabschnittViewJahrAction">
<result name="success" type="redirect">${referer}</result>
</action>
In my JSP File I'm iterating the model object, displaying its values in textfields or lists as follows:
<ul:form action="auth/GroupAdmin/processEditDienstabschnittJahr">
<s:iterator value="model" status="rowStatus">
<tr>
<td style="text-align: center;">
<s:date name="model.get(#rowStatus.index).datumAb" var="datumAb_DE" format="dd.MM.yyyy" />
<s:textfield style="width:70px;" name="model.get(#rowStatus.index).datumAb" value="%{#datumAb_DE}" label="DatumAb"></s:textfield >
</td>
<td style="text-align:center;">
<s:date name="model.get(#rowStatus.index).datumBis" var="datumBis_DE" format="dd.MM.yyyy" />
<s:textfield style="width:70px;" name="model.get(#rowStatus.index).datumBis" value="%{#datumBis_DE}" label="DatumBis"></s:textfield >
</td>
<td class="currency">
<s:set var="tzgBetrag">
<fmt:formatNumber type="NUMBER" maxFractionDigits="0"><s:property value="%{getBetrag()*100}"></s:property></fmt:formatNumber>
</s:set>
<s:textfield style="width:30px;" maxlength="3" name="model.get(#rowStatus.index).betrag" value="%{#tzgBetrag}" label="Betrag"></s:textfield >
</td>
</tr>
</s:iterator>
<s:submit style="width:24px; height:24px;" type="image" src="../../../res/24px/floppy-disk.png" value="Speichern"></s:submit>
</ul:form>
The ul-tag is from a custom taglib, which adds a customer specific url parameter to action path.
So when I display the page it shows all my Teilzeitgrad-records with a row for each entry. But when I submit the form, the list of my models is not populated. The setter setTeilzeitgrads(List<Teilzeitgrad> teilzeitgrads) is not even called at all.
I also tried to access the list in array-syntax:
<s:textfield style="width:70px;" name="teilzeitgrads[#rowStatus.index].datumAb" value="%{#datumAb_DE}" label="DatumAb"></s:textfield >
but this did also not work.
Any help solving this case is apreciated! Thanks in advance!
Lenzo
Ok - here is a very basic working example of list indexing. The main change is to move the creation of the model from getModel() to prepare(). This is because getModel() is called for every value you need to set the list - so you end up re-creating your model each time overwriting the previous change.
package com.blackbox.x.actions;
import java.util.ArrayList;
import java.util.List;
import com.blackbox.x.actions.ListDemo.ValuePair;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.Preparable;
public class ListDemo extends ActionSupport implements ModelDriven<List<ValuePair>>, Preparable {
private List<ValuePair> values;
#Override
public List<ValuePair> getModel() {
return values;
}
public String execute() {
for (ValuePair value: values) {
System.out.println(value.getValue1() + ":" + value.getValue2());
}
return SUCCESS;
}
public void prepare() {
values = new ArrayList<ValuePair>();
values.add(new ValuePair("chalk","cheese"));
values.add(new ValuePair("orange","apple"));
}
public class ValuePair {
private String value1;
private String value2;
public ValuePair(String value1, String value2) {
this.value1 = value1;
this.value2 = value2;
}
public String getValue1() {
return value1;
}
public void setValue1(String value1) {
this.value1 = value1;
}
public String getValue2() {
return value2;
}
public void setValue2(String value2) {
this.value2 = value2;
}
}
}
and the corresponding jsp
<%# taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
</head>
<body>
<s:form action="list-demo" theme="simple">
<table>
<s:iterator value="model" status="rowStatus">
<tr>
<td><s:textfield name="model[%{#rowStatus.index}].value1" value="%{model[#rowStatus.index].value1}"/></td>
<td><s:textfield name="model[%{#rowStatus.index}].value2" value="%{model[#rowStatus.index].value2}"/></td>
</tr>
</s:iterator>
</table>
<s:submit/>
</s:form>
</body>
</html>
Have you tried an approach like this?
<s:iterator var="teilzeitgrad" value="teilzeitgrads" status="listStatus">
<s:set name="paramName">teilzeitgrads[${ listStatus.index }].datumAb</s:set>
<s:textfield name="%{#paramName}" value="%{#teilzeitgrad.datumAb}"/>
</s:iterator>
You are submitting values to model, you have to submit them to your list teilzeitgrads.
For example see http://www.dzone.com/tutorials/java/struts-2/struts-2-example/struts-2-model-driven-action-example-1.html.
Update
How about
name="teilzeitgrads[%{#rowStatus.index}].datumBis".
Assuming you've got the configuration correct - the problem is probably due to the way you're defining the indexing. Try changing the name attribute on the textfield to use
model[%{#rowStatus.index}].datumBis
and let OGNL sort out the access methods. (I'd also use Firebug in Firefox to see what is actually being sent when you submit the form)
Thanks to all of you getting along with this issue! Your hints were most useful. I finally got it up and running rewriting everything from the scratch. I can edit my models now using the following Action-Class:
public class TeilzeitgradEditAction implements ModelDriven<List<Teilzeitgrad>> {
List<Teilzeitgrad> teilzeitgrads;
private String tzgTypKey;
private Integer jahr;
public String execute() {
return SUCCESS;
}
#Override
public List<Teilzeitgrad> getModel()
{
if(teilzeitgrads == null) {
teilzeitgrads = getTeilzeitgradListByTypAndJahr(tzgTypKey, jahr);
}
return teilzeitgrads;
}
public List<Teilzeitgrad> getTeilzeitgrads()
{
return teilzeitgrads;
}
public void setTeilzeitgrads(List<Teilzeitgrad> teilzeitgrads)
{
this.teilzeitgrads = teilzeitgrads;
}
// getters and setters for local attributes
}
and this JSP-Code:
<ul:form action="auth/GroupAdmin/processEditDienstabschnittJahr">
<s:iterator var="teilzeitgrad" value="teilzeitgrads" status="listStatus">
<tr>
<td>
<s:date name="%{#teilzeitgrad.datumAb}" var="datumAb_DE" format="dd.MM.yyyy" />
<s:textfield name="teilzeitgrads[%{#listStatus.index}].datumAb" value="%{#datumAb_DE}"/>
</td>
</tr>
</s:iterator>
<s:submit style="width:24px; height:24px;" type="image" src="../../../res/24px/floppy-disk.png" value="Speichern"></s:submit>
Thanks a lot for your support!
Cheers,
Lenzo

Unable to persist object : detached entity passed to persist

I am using JSF - EJB3 - Hibernate JPA2.0 in my application. In one of the screens when I try to persist a new entry, I get the following exception:
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: info.novatec.timemgmt.entities.Customer
Following are chunks of my code that may be helpful,
View:
<h:form>
<h:panelGrid columns="2">
<h:outputLabel value="Customer:" for="customer" />
<h:selectOneMenu id="customer" value="#{projectController.selected.customer}" title="Customer" >
<f:selectItems value="#{customerController.itemsAvailableSelectOne}"/>
</h:selectOneMenu>
<h:outputLabel value="Name:" for="name" />
<h:inputText id="name" value="#{projectController.selected.name}" title="Name" />
<p:calendar id="endDate" value="#{projectController.selected.endDate}" showOn="button" pattern="MM/dd/yyyy" size="10"/>
</h:panelGrid>
Managed bean
#ManagedBean
#SessionScoped
public class CustomerController implements Serializable {
// ...
public SelectItem[] getItemsAvailableSelectOne() {
return JsfUtil.getSelectItems(ejbFacade.findAll(), true);
}
// ...
}
JSFUtil helper class:
public class JsfUtil{
public static SelectItem[] getSelectItems(List<?> entities, boolean selectOne) {
int size = selectOne ? entities.size() + 1 : entities.size();
SelectItem[] items = new SelectItem[size];
int i = 0;
if (selectOne) {
items[0] = new SelectItem("", "---");
i++;
}
for (Object x : entities) {
items[i++] = new SelectItem(x, x.toString());
}
return items;
}
}
Could you please point as to where I am going wrong?
The problem is in your Converter for the Customer class (which you omitted from the question, but it's surely there in your real code). You seem to be manually constructing a new Customer() with an ID instead of obtaining the Customer instance from entity manager by its ID. Fix the converter accordingly.