tags:

views:

35

answers:

1

I'm trying to generate a form with a variable - on the server side - number of text fields. The tapestry page looks similar to:

<form t:id="form">
    <t:loop source="typesOfIncome" value="typeOfIncome">
        <input t:type="TextField" t:id="typeOfIncome-${typeOfIncome.propertyIndex}" value="100"/>
     </t:loop>
</form>

That is not accepted by Tapestry, as it bails out with

Component id 'typeOfIncome-${typeOfIncome.propertyIndex}' is not valid; component ids must be valid Java identifiers: start with a letter, and consist of letters, numbers and underscores.

How can I achieve this with Tapestry? And how would the Java code look like in the component?


Update:

With a component which looks like:

public class FormSample {

    @Component
    private Form _form;

    @Inject
    private Logger _log;

    @Property
    private List<String> _typesOfIncome;

    @Property
    private String _typeOfIncome;

    @SetupRender
    void setupRender() {
        _typesOfIncome = Arrays.asList("First", "Second");
    }

    void onSuccess() {
        _log.info("Got values " + _typesOfIncome + " .");
    }

}

and a page containing

<form t:id="form">
  <t:loop source="typesOfIncome" value="typeOfIncome">
    <input t:type="TextField" t:id="typeOfIncome" t:value="typeOfIncome"/> <br/>
  </t:loop>
    <input type="submit" value="Save"/>
</form>

in onSuccess the values list is null. The values are POSTed as:

typeOfIncome    First
typeOfIncome_0  Second
+2  A: 

I did a little testing and updated this. It's possible you can do it with a pure List, but I could only get it to work using a class to hold the income types.

In Java:

@Property
@Persist
private List<Info> _infoList;

@Property
private Info _info;

void onPrepare() {
  // populate _typesOfIncome with existing ones
  if (_infoList == null) {
    _infoList = new ArrayList<Info>();
    _infoList.add(new Info("type1"));
    _infoList.add(new Info("type2"));
  }
}

void onSuccess() {
  for (Info i : _infoList) {
    System.out.println(i.getTypeOfIncome());
  }
}

public class Info {
  private String typeOfIncome;  
  public Info() { }
  public Info(String typeOfIncome) {
    this.typeOfIncome = typeOfIncome;
  }
  public String getTypeOfIncome() {
    return typeOfIncome;
  }
  public void setTypeOfIncome(String typeOfIncome) {
    this.typeOfIncome = typeOfIncome;
  }
}

In the template:

<t:form t:id="form">
  <t:loop t:source="infoList" t:value="info" formState="ITERATION">
    <input t:type="TextField" t:id="typeOfIncome" t:value="info.typeOfIncome"/><br/>
  </t:loop>
  <input t:type="submit" name="Submit"/>
</t:form>

If you want to be able to add new types on the fly, here's a good example.

Brian Deterling
Brian, thanks for your answer. I'm getting closer, but the code I posted in my answer leaves me with a null list on submission. Using Tapestry 5.1.0.5 .
Robert Munteanu

related questions