5

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

Lenz Lüers
  • 53
  • 1
  • 1
  • 5
  • Unless there's something you're not showing us - your form is submitting the form data to the action "auth/GroupAdmin/processEditDienstabschnittJahr" but your action is named "DienstabschnittViewJahrAction" – user497087 Oct 23 '12 at 11:13
  • thanks for your reply. I added the line for the action mapping from the struts configuration. The action is called properly. – Lenz Lüers Oct 23 '12 at 11:28
  • I recommend that you install the config-browser plugin http://struts.apache.org/2.x/docs/config-browser-plugin.html - that will allow you to check that Struts is configured the way that you think it is. – user497087 Oct 23 '12 at 11:32
  • where is the function `getTeilzeitgradListByTypAndJahr(getTzgTypKey(), getJahrIndex())`. you should try initialize your `model` first. do something like `List teilzeitgrads = new ArrayList<>();` – Jaiwo99 Oct 23 '12 at 13:03
  • @Jaiwo99 the `getTeilzeitgradListByTypAndJahr(getTzgTypKey(), getJahrIndex())` method is coming from an inherited action, which I did not want to post to avoid further confusion. It returns an emtpy but initialized List, if there are not objects in the db. I solved my problem now (see the above comments). thanks for your help! – Lenz Lüers Oct 23 '12 at 13:39

5 Answers5

2

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>
user497087
  • 1,561
  • 3
  • 24
  • 41
  • thanks for that great example! I outsourced my model initialization into the `prepare()` method, what works just fine! – Lenz Lüers Oct 23 '12 at 13:52
0

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".

Aleksandr M
  • 24,264
  • 12
  • 69
  • 143
  • I tried to change my object access to name="teilzeitgrads.get(#rowStatus.index).datumBis" but still the same effect... – Lenz Lüers Oct 23 '12 at 11:36
  • I've seen that example before, but they do not handle a list of objects but a single one. I already implemented an update-user-functionality successfully with the ModelDriven interface, but I can't get it running on List objects. – Lenz Lüers Oct 23 '12 at 11:45
  • How about `name="teilzeitgrads[%{#rowStatus.index}].datumBis"`? – Aleksandr M Oct 23 '12 at 11:48
  • I tried your last recommendation. Now my setter function `setTeilzeitgrads` is called, but it passes an empty ArrayList-object th my action. – Lenz Lüers Oct 23 '12 at 12:05
  • Hmm... Check and make sure you are not nulling it in `getModel` or `prepare` methods. – Aleksandr M Oct 23 '12 at 12:53
  • thanks @Aleksandr M yeah I guess my problem was located somewhere else. After creating a new Action class everything worked fine with the code snippet you provided! – Lenz Lüers Oct 23 '12 at 13:29
0

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>
rees
  • 1,566
  • 1
  • 12
  • 19
  • Hi @rees, thanks for your idea. I see, you want to use EL instead of OGNL. I just tried that, the HTML outut looks good, but the effect is the same as from the other solutions. An empty ArrayList is passed to my setter method. – Lenz Lüers Oct 23 '12 at 12:22
  • @LenzLüers The above certainly should work, as I use the approach myself without issue. An empty ArrayList? Then your problem is probably something else. Struts2 won't actually being passing a `List` to you, it will be getting a list from you and then setting values on items in the list. You are responsible for providing it with the populated list, presumably preparing it in your `prepare` method. You don't show any code that does this, so do you have any? – rees Oct 23 '12 at 12:49
  • after fixing the other problem in my code, which must have been outside of the jsp-part, I tried your code example again and it worked fine! I prefered not to use the EL-Version for my final code, but this here works well. Thanks a lot! – Lenz Lüers Oct 23 '12 at 13:26
0

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)

user497087
  • 1,561
  • 3
  • 24
  • 41
  • thx @user497087 I tried your code, which makes at least my setter being called. But the passed ArrayList is empty. The HTML-Code for the form seems to be ok, it creates the correct IDs and everything. See here: `` – Lenz Lüers Oct 23 '12 at 12:10
  • Have you used Firebug to see exactly what is being passed to your program? With my change I would expect that getModel() is called and the setDatumBis() method to be called each called multiple times (once for each index entry). I'm confused by your html now showing tgList[0].betrag - where did the variable tgList come from – user497087 Oct 23 '12 at 12:35
  • When you say your setter method is being called - can you be specific which setter method? What interceptor stack are you using for this? – user497087 Oct 23 '12 at 12:43
  • sorry for the confusion with the tzgList-object, I temporarily renamed the the variable teilzeitgrads with its getter/setters to tzgList. I changed it back now. The setter-method I was tracing is `public void setTeilzeitgrads(List teilzeitgrads)`. This one is called after submitting the form and before the execute-method. But the passed parameter is an empty ArrayList. – Lenz Lüers Oct 23 '12 at 12:50
  • like already written on the other comments my mistake was somewhere on action-level. I can access and edit my List-Data now with the code snippet posted! thanks a lot! – Lenz Lüers Oct 23 '12 at 13:31
0

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

Lenz Lüers
  • 53
  • 1
  • 1
  • 5