I am using Strust2 for the presentation layer.I have struts form with drop down which binds with the java object(Application) list.
Drop down displays application object list, user can select one Application and submit.When retrieve the user input value in the Action class, receiving value type is "String", Can't we retrieve objects directly from the struts drop down,In my case "Application" objects
private List<Application> applicaionList = new ArrayList<Application>();
#Autowired
private ApplicationService applicationService;
private Application application;
public void loadTheForm(){
applicationList = applicationService.findAll();
}
public void submitForm(){
Document doc = new Document();
doc.setApplication(application);
}
//Getter Setters...
}
application.jsp
<s:form action ="submitForm">
<s:select list ="applicationList" headerValue="---Select---" headerKey="-1" name="application"/>
</s:form>
struts.xml
<action name="submitForm" class="com.ActionSupport" method="submitForm">
<result name="success" type="tiles">/newAdminDocumentRequired.tiles</result>
</action>
When user select the value from the drop down and submit, the submitted value is string,
Can't we take the object directly in Struts, if we can't how we can get the object of the selected value?
Thank you,
Udeshika
What I got from your question is
1. You have to show list of applications as drop-down.
2. User selects one application and submit the form.
3. You have to retrieve the selected application and perform some action with it.
I assume your Application class has a property 'id' which is unique to all applications. And a applicationName, which you have to show to the users.
So now, I would have solved this issue as follows
<s:form action ="submitForm">
<s:select list ="applicationList" headerValue="---Select---" headerKey="-1" key="application" listKey="id" listValue="applicationName"/>
</s:form>
Now, this tag will create a drop-down like follows
<select name="application">
<option value="-1" selected="selected">---Select---</option>
<option value="1">Demo 1 App</option>
<option value="2">Demo 2 App</option>
<option value="3">Demo 3 App</option>
<option value="4">Demo 4 App</option>
</select>
Note that values(1,2,3,4) in OPTION elements are application.id and titles(Demo 1 App, Demo 2 App,etc) are application.applicationName.
Now, user will select and submit. The id of selected application will be sent to struts action in parameter "application".
In Action, you can do this
public MyClass extends ActionSupport{
private List<Application> applicaionList = new ArrayList<Application>();
#Autowired
private ApplicationService applicationService;
private **String** application;
public void loadTheForm(){
applicationList = applicationService.findAll();
}
public void submitForm(){
Application varApp = applicationService.findApplicationById(getApplication());
Document doc = new Document();
doc.setApplication(varApp);
}
//Getter Setters...
}
Please note that I have changed type of application to String. And yes, I don't think you can pass directly object from drop-down.
Hope it helps.
Dropdown with object attributes
Here Example is shown for store object
//Store Bean class
calss Store{
private int storeId;
private String storeName;
//getter setter
}
//Action Class
//Set the list in struts2 action class (Model Driven)
List<Store> storeList=new ArrayList<>();
//JSP page
<s:select id="store" name="store" headerKey="-1"
headerValue="Select Store " list="storeList"
listKey="storeId" listValue="storeId"
value="%{IteratorList[#status.index].itemBase.{storeId}}"
/></td>
Related
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.
I am not able get value from form to bean in action class.
Here small piece of code :
in jsp,i have text box like show below:
<input type="text" name="objectID.Norder" id="objectID.Norder" />
In action class ,i have added one name and getter and setter method as shown below:
private String Norder;
public void setNOrder(String norder){
this.Norder=norder;}
public String getNOrder(){
return Norder;
}
This is small piece of code in action class.Value of Norder is null when i submit form with valid inputs and early it was working fine .When i am new jars it is not working .
Getters and setters should ideally be in your ActionForm(form bean) class rather than your Action class
public class LoginForm extends ActionForm {
private String nOrder;
public void setNOrder(String norder){
this.Norder=norder;}
public String getNOrder(){
return Norder;
}
}
Use struts tags for your jsps:
<html:text property="nOrder" />
Are you trying to pre-populate your form with any values are when do you want your jsp to show some values in the text box?
I am trying to do some "custom lightweight" JSF component binding. In my bean (controlling some JSF page) i'm declaring a HahsMap where keys ranging over my h:inputText ids (appearing in the page) map these ids to custom HInputText<T> objects (T in the example given below is Long). Then i am using HInputText<T> objects to hold a subset of corresponding h:inputText attributes : value supposed to be of type T, rendered, required, etc. In this direction fields from the HInputText<T> objects give values to the h:inputText attributes.
My problem is that when using such an h:inputText inside a h:form, JSF validation does not take place : i can type alphanumeric chars in the h:inputText (supposed to hold a Long value) and my form submits without showing any errors. Note that required and rendered attributes are managed correctly (since required is set to true, i have an error when i leave the h:inputText field empty) and that when i try to display back the value of the h:inputText in the page using some h:outputText the alphanumeric chars are displayed.
Is there some trick to make JSF validation work without having to explicitly define a custom Validator for each h:inputText?
HInputText.class:
public class HInputText<T>
{
private String id;
private boolean rendered;
private boolean required;
private T value;
private Class<T> myClass;
// getters and setters for all fields
public HInputText(Class<T> myClass, String id)
{
this.myClass = myClass;
this.id = id;
this.rendered = true;
this.required = true;
}
}
Code snippet from my Managed Bean :
#ManagedBean(name="saisieController")
#SessionScoped
public class SaisieController
{
...
private HashMap<String,HInputText<Long>> htagLongInputTexts;
public HashMap<String, HInputText<Long>> getHtagLongInputTexts()
{
return htagLongInputTexts;
}
public void setHtagLongInputTexts(HashMap<String, HInputText<Long>> hLongInputTexts)
{
this.htagLongInputTexts = hLongInputTexts;
}
public void addHtagLongInputText(HInputText<Long> hLongInputText)
{
getHtagLongInputTexts().put(hLongInputText.getId(), hLongInputText);
}
public HInputText<Long> getHtagLongInputText(String hLongInputTextId)
{
return(getHtagLongInputTexts().get(hLongInputTextId));
}
#PostConstruct
public void init()
{
setHtagLongInputTexts(new HashMap<String, HInputText<Long>>());
addHtagLongInputText(new HInputText<Long>(Long.class, "HIT_LongTestHIT"));
}
public String doNothing()
{
return null;
}
}
and finally a snippet from my jsf page:
<h:form>
<h:inputText
id = "HIT_LongTestHIT"
value = "#{saisieController.htagLongInputTexts['HIT_LongTestHIT'].value}"
rendered = "#{saisieController.htagLongInputTexts['HIT_LongTestHIT'].rendered}"
required = "#{saisieController.htagLongInputTexts['HIT_LongTestHIT'].required}"
/>
<h:message for = "HIT_LongTestHIT" styleClass = "error-text" />
<h:commandButton value = "submit" action = "#{saisieController.doNothing()}" />
<h:outputText value = "#{saisieController.htagLongInputTexts['HIT_LongTestHIT'].value}" />
</h:form>
Is there some trick to make JSF validation work without having to explicitly define a custom Validator for each h:inputText?
No, that's not possible. Due to type erasure the generic type information is not available during runtime. JSF/EL has no idea that T is actually a Long, let alone that there's actually some T. You really need to explicitly specify a converter which is in this particular example the LongConverter with a converter ID of javax.faces.Long.
<h:inputText ... converter="javax.faces.Long">
or, dynamically so you want
<h:inputText ...>
<f:converter converterId="#{saisieController.htagLongInputTexts['HIT_LongTestHIT'].converterId}" />
</h:inputText>
You can find an overview of all available standard JSF converters in the javax.faces.convert package summary. You can find out their converter IDs by navigating to the Constant field values link on the public constant CONVERTER_ID.
Hi I have a problem about dynamically displaying the value of a HtmlSelectOneMenu. Below is a small application that describes my problem.
I have a list of cars List<Car> carList = new ArrayList<Car>() in my backing bean.
Car is a abstract class and Toyota and Ford extends Car.
Now I need to display different message in the selectonemenu based on the class type. If it is Toyota then I would display something else. Maybe its clearer for the codes to tell the story.
Backing Bean:
#ManagedBean(name="myBean")
#SessionScoped
public class MyCarBackingBean implements PhaseListener {
private List<Car> carList = new ArrayList<Car>();
private HtmlSelectOneMenu hsom;
Car myCar;
#PostConstruct
public void init() {
carList.add(new Ford());
carList.add(new Toyota());
}
#Override
public void beforePhase(PhaseEvent event) {
//hsom becomes null here. Im pretty sure the setHsom was called before and the variable was set.
if(hsom != null) {
switch((Integer)hsom.getValue()){
case 1: hsom.setValue("This is a Ford car"); break;
case 2: hsom.setValue("This is a Toyota car");
}
}
//The rest of the world...
}
And i bind the selectonemenu to the component in my page:
<h:form>
<h:selectOneMenu binding="#{myBean.hsom}">
<f:selectItems value="#{myBean.carList}" var="car" itemValue="#{car.id}" itemLabel="#{car.id}" />
</h:selectOneMenu>
<h:commandButton value="Submit" action="#{myBean.mySubmit()}"/>
</h:form>
And finally the model classes:
public abstract class Car {
protected int id;
//Getters and Setters
}
public class Toyota extends Car {
public Toyota(){
this.id = 2; //in case of ford car, id is 1.
}
}
And I'm thinking using a phase listener to change the display, cos I read some posts saying that it is bad to change the getters and setters and put business logic in them. Nor do I want to wrap those cars in other objects and make use of itemLabel and itemValue.
But when I was debugging it I found that hsom is null when the execution reaches beforePhase but it is not null in the rest part of the code.
So my questions are : is it a good way to use a phase listener for this? And why is the component object null in beforePhase?
Add a different attribute (say description) to your class. Implement it as you like, and refer to it in the selectItems tag. V.g.
<f:selectItems value="#{myBean.carList}" var="car" itemValue="#{car.id}" itemLabel="#{car.description}" />
Alternatively, replace myBean.carList with a method that returns a List<SelectItem>, and create the selectItems as you wish.
As a rule of thumb, try to keep the .xhtml as "logic-free" as possible.
How to populate ddropdown list in a web page in struts2 from database. The items of the drop down will be taken from database.
For creating list in struts2, we can use implement Preparable.
public class CountryListAction extends ActionSupport implements Preparable {
private List<String> country;
getters and setters
public String prepareCountryList() throws Exception{
country=new ArrayList<String>();
country=fill country from database;
}
public String countryList() throws Exception{
return SUCCESS;
}
}
For jsp, use:
<s:select label="Select Country"
name="country"
headerValue="Select Country"
list="%{country}"
/>
Preparable interface will call prepareCountryList method before calling countryList and jsp will be prepopulated. You can populate as many drop down list using single method in struts2.
You need to create a List in you action class with getters and setters and all you have to do is to populate the list inside your Action's execute method.
Action Class
public class MyListAction extends ActionSupport{
private List<String> country;
getters and setters
public String execute() throws Exception{
country=new ArrayList<String>();
countr=fill country from database;
return SUCCESS;
}
}
Now all you need to have a S2 select tag with following entry
<s:select label="Select Country"
name="country"
headerKey="-1" headerValue="Select Country"
list="%{country}"
/>
here list is an Iterable source to populate from. If the list is a Map (key, value), the Map key will become the option 'value' parameter and the Map value will become the option body.
So list=%{country} will be evaluated by OGNL as getCountry() method in your action class and will fetch the required list from value-stack to populate the drop down
You can use the below code:
<s:select name="someName" id="someId"
list="someMap_or_List" onchange="someFunction();"/>
here someMap_or_List is the List or Map that you can use to populate your Drop Down List.