0

I need to perform a web service (method) call just before load of a jsf page. The call will return a list of input fields that have to be displayed on my jsf page. The user can fill in the form and on click of next I need the values entered on the form to be sent back to another web service (method). My approach was to have a request scoped bean for the jsf page(which consists of a blank form and a binding to the bean), and perform the web service call in the setter method of my form method and dynamically create UIInput fields

//call web service
//Loop
    UIInput input = new HtmlInputText();
    //set unique Id
    form.getChildren().add(input);
//End Loop

It does create the Input fields, but if I perform browser back or refresh it keeps adding Input fields. So clearly my approach is wrong.
Also I found out that when I try to get the values for these dynamically created Input fields on Submit action like

List<UIComponent> dynamicFields = form.getChildren();
 for(int i=0;i<form.getChildCount();i++){   
     if("javax.faces.Input".equals(componentFamily)){
        UIInput input = (UIInput)dynamicFields.get(i);
        System.out.println("Input Field: ID = "+input.getId() + " , Value="+ input.getValue());
      }
 }

The Id of the fields is printed properly, However value is always null. Clearly doing it all wrong.

Kindly let me know when and at what point do I create fields and how do I capture those values P.S Am using JSF 2.0, Jdeveloper, Glassfish and/or Weblogic Server

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Idea Inside
  • 197
  • 1
  • 4
  • 15
  • What is the benefit of working with dynamic fields in your case? why you didn't simply work with a loop and create your components statically in the view ? – Bardelman Apr 04 '13 at 13:43
  • Ok, the reason is that I will not always be using a single type of input. The web service returns me the type of input too. I will use some sort of a switch to build the necessary form. – Idea Inside Apr 04 '13 at 16:46
  • Well, i would like use the dynamic add of the components too (for me , i've another specific reason) but it seems there are alot problems in this issue and a poor documentation also. You can use a jstl tag to make the switch you need statically if u can't find a solution for this(i guess you kow it already :D). – Bardelman Apr 04 '13 at 17:12
  • Thanks @Bradelman I had not known these tags. Although they are partially dynamic, I would always like to have the full control on my backing bean. Since the code to create the components exists, my question was simply where to put it :) . I dint quite get what you meant by "there are alot problems in this issue and a poor documentation also". – Idea Inside Apr 05 '13 at 06:12
  • Actually i started manipulating components programmatically recently and i met alot problems with this and asked some questions in this forum which still unanswered. Most JSF's most known references books like the JSF 2.0 Core or The JSF 2.0 Complete Reference books do not say much about this topic (an exception with the JavaServer Faces JSF in Action which reserved one chapter).For example there is a problem with adding AjaxBehaviour programmatically(which every programmer will need it i think) which still not resolved even in the primefaces community. Sorry for bad english:) – Bardelman Apr 05 '13 at 08:30
  • Hi @Bradelman . I am not able to use or tag on my .jspx page (I am using JDeveloper 11g Release 1) any idea why ? I get the error "element repeat not expected". Thanks – Idea Inside Apr 08 '13 at 13:14
  • Check for which mojorra version you're using it seems ui:repeat have some bugs(see this http://stackoverflow.com/questions/8464983/uirepeat-adding-removing-elements-from-list-does-not-work-properly) or look for a working example and try to run in a simple test case first (this is an example http://www.mkyong.com/jsf2/jsf-2-repeat-tag-example/) and make sure u have declared the component library on your jsp like in facelets: xmlns:ui="http://java.sun.com/jsf/facelets".Good luck :) – Bardelman Apr 08 '13 at 13:44

2 Answers2

0

From your question I was not able to be sure about what kind of data you expect to get from your webservice, and what kind of component you want to render it in. My answer below assumes that you will always receive a list of String and that you will display them in text boxes.

One possible approach would be to call your webservice and fetch the data in a @PostConstruct method, put this data into a list, and then render the data in a data table. Code below.

Bean:

@ManagedBean(name="bean")
@ViewScoped
public class YourBean implements Serializable {


private static final long serialVersionUID = 1L;

private List<String> values = new ArrayList<String>();

   //The method below @PostConstruct is called after the bean is instantiated
   @PostConstruct
   public void init(){
          //fetch data from source webservice, save it to  this.values
   }

   public void save(){
        for(String s: this.values)
            // send s to destination webservice
   }

   public List<String> getValues(){
         return this.values;
   }  

   public void setValues(List<String> values){
         this.values = values;
   }       

}

XHTML excerpt:

<h:form>
     <h:dataTable value="#{bean.values}" var="s">
          <h:column>
                <h:inputText value="#{s}" />
          </h:column>
     </h:dataTable>
     <h:commandButton value="Save" action="#{bean.save}" />
</h:form>
bigato
  • 79
  • 4
-1

This problem because the scope of your bean you are binding on it if it is @RequestScoped this means that every time you refresh or call the page you are going to invoke the post constuctor (@PostConstuct) method again so do the job of the creation again and for the null value of the input fields you should add to every input field value expression to store the value in it.

    private String inputValue; //setter() getter()
    UIInput input = new HtmlInputText(); 

   @PostCostruct
   public void addInput()
     {
        // your previos create and add input fields to the form + setting value expression
        Application app = FacesContext.getCurrentInstance().getApplication();  
        input.setValueExpression("value",app.getExpressionFactory().createValueExpression(
                   FacesContext.getCurrentInstance().getELContext(), "#{bean.inputValue}", String.class));
     }

The correct answer if you are using binding don't use Request scope use Session Scope and it will work with you and get data not null when retrieve values.

Gomy
  • 21
  • 2
  • Bad advice. Component instances are request scoped. Binding them to a bean in the session scope would only result in "duplicate component ID" errors or at least in serious JSF state integrity problems when the same view is opened in multiple browser tabs/windows in the same session. You **must** bind component instances to a request scoped bean. A view scoped one can also, but that requires a minimum of JSF 2.2 (or in case of Mojarra, version 2.1.18) due to chicken-egg issue 1492 which causes recreation of view scoped beans during view build time. – BalusC Apr 24 '13 at 11:08