2

I'm populating an <s:select> from database. The action class is model-driven.

@Namespace("/admin_side")
@ResultPath("/WEB-INF/content")
@ParentPackage(value="struts-default")
public final class TestAction extends ActionSupport implements Serializable, ValidationAware, Preparable, ModelDriven<Transporter>
{
    @Autowired
    private final transient SharableService sharableService=null;
    private static final long serialVersionUID = 1L;

    private Transporter transporter; //Getter and setter
    private Long transporterId; //Getter and setter.
    private List<Transporter> transporters; //Getter only.

    @Action(value = "Test",
            results = {
                @Result(name=ActionSupport.SUCCESS, location="Test.jsp"),
                @Result(name = ActionSupport.INPUT, location = "Test.jsp")},
            interceptorRefs={@InterceptorRef(value="defaultStack", params={"validation.validateAnnotatedMethodOnly", "true", "validation.excludeMethods", "load"})})
    public String load() throws Exception
    {
        return ActionSupport.SUCCESS;
    }

    @Validations(
            requiredFields={@RequiredFieldValidator(fieldName="transporterId", type= ValidatorType.FIELD, key = "transporter.required")})
    @Action(value = "testInsert",
        results = {
            @Result(name=ActionSupport.SUCCESS, location="Test.jsp", params={"namespace", "/admin_side", "actionName", "Test"}),
            @Result(name = ActionSupport.INPUT, location = "Test.jsp")},
        interceptorRefs={@InterceptorRef(value="defaultStack", params={"validation.validateAnnotatedMethodOnly", "true"})})
    public String insert() {
        System.out.println("Selected item in the drop box : "+transporterId);
        return ActionSupport.SUCCESS;
    }

    @Override
    public void prepare() throws Exception {
        transporters=sharableService.getTransporterList();
    }

    @Override
    public Transporter getModel() {
        return transporter;
    }
}

and the following is <s:select> :

<s:select id="transporterId" 
          name="transporterId" 
          list="transporters" 
          value="transporterId" 
          listKey="transporterId" 
          listValue="transporterName" 
          headerKey="" headerValue="Select" 
          listTitle="transporterName"/>

This works perfectly.


I need this <s:select> in another action class which implements ModelDriven<ZoneTable>.

The table structure is simple, transporter->zone_table->country->state->city. There exists a one-to-many relationship between these tables.

How can we have a model driven action class implementing ModelDrven<ZoneTable> in which Transporter can be mapped to <s:select>, something like?

@Namespace("/admin_side")
@ResultPath("/WEB-INF/content")
@ParentPackage(value="struts-default")
public final class ZoneAction extends ActionSupport implements Serializable, ValidationAware, Preparable, ModelDriven<ZoneTable>
{
    @Autowired
    private final transient ZoneService zoneService=null;
    @Autowired
    private final transient SharableService sharableService=null;

    private ZoneTable entity=new ZoneTable(); //Getter and setter.
    private Long transporterId; //Getter and setter.
    private List<Transporter> transporters; //Getter only.

    @Override
    public ZoneTable getModel() {
        return entity;
    }

    @Override
    public void prepare() throws Exception {
        transporters=sharableService.getTransporterList();
    }
}

Doing like this doesn't work. It doesn't set the value of transporterId upon submission, since the action class is implementing ModelDriven<ZoneTable> and not ModelDriven<Transporter> like the first case.

Is this possible using the model driven approach?


EDIT:

ZoneTable.java

public class ZoneTable implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "zone_id", nullable = false)
    private Long zoneId;
    @Column(name = "zone_name", length = 45)
    private String zoneName;
    @JoinColumn(name = "transporter_id", referencedColumnName = "transporter_id")
    @ManyToOne(fetch = FetchType.LAZY)
    private Transporter transporterId;
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "zoneTable", fetch = FetchType.LAZY)
    private Set<ZoneCharge> zoneChargeSet;
    @OneToMany(mappedBy = "zoneId", fetch = FetchType.LAZY)
    private Set<Country> countrySet;

    //Getters and setters + constructors.
}

Zone.jsp

<s:form namespace="/admin_side" action="Zone" validate="true" id="dataForm" name="dataForm" cssClass="search_form general_form">

    <s:label key="label.zone.name" for="zone"/>
    <s:textfield id="zoneName" name="zoneName" cssClass="validate[required, maxSize[45], minSize[2]] text-input text"/>
    <s:fielderror fieldName="zoneName"/>

    <s:label key="label.transporter.name" for="transporterId"/>
    <s:select id="transporterId" name="transporterId" list="transporters" value="transporterId" listKey="transporterId" listValue="transporterName" headerKey="" headerValue="Select" listTitle="transporterName"/>
    <s:fielderror fieldName="transporterId"/>

    <s:text name="label.submit"/>
    <s:submit id="btnSubmit" name="btnSubmit" value="Submit" action="AddZone"/>

</s:form>

Since this post has already a lot of code, I'm not posting the action class ZoneAction.java here. In case, it is needed, it is available here.

Tiny
  • 27,221
  • 105
  • 339
  • 599
  • Post the code for model class. – Roman C Jan 06 '14 at 11:07
  • 1
    BTW using ModelDriven adds nothing except complexity :/ I would get rid of it asap... – Andrea Ligios Jan 07 '14 at 12:42
  • @AndreaLigios : Can we directly populate properties of a model without using `ModelDriven`, (without providing properties (and their setters & getters) of a model class in an action class)? – Tiny Jan 16 '14 at 18:43
  • Since `Transporter` is the parent of `ZoneTable`, and you need to handle `Transporter` (both in the page and in the Action)... shouldn't be `Transporter` your Model object, instead of `ZoneTable` ? – Andrea Ligios Jan 17 '14 at 10:45
  • @AndreaLigios : It should be `ModelDriven` because CRUD operations are performed on `ZoneTable` (not on `Transporter`. A list of of `Transporter`s is just needed to display in ``). The user interface looks like as shown in [this](http://oi42.tinypic.com/2czu1c6.jpg) image. – Tiny Jan 18 '14 at 15:16
  • @Tiny, if Transporter is the parent of ZoneTable, can't you simply use Transporter as model and then pass ZoneTable to the CRUD service ? I'm missing something... – Andrea Ligios Jan 20 '14 at 13:21
  • @AndreaLigios : I think, no. If there is a different scenario, for instance, if there are more than one `` on the same page then, this should not work. Later on, I would have three (or probably more) `` on the same page like `country`, `state`, `city`... in which this would not happen (at a glance). Can this be done? If I avoided `ModelDriven` then, I would have to deal with many properties of a model in the action class itself. I don't find a better alternative. – Tiny Jan 20 '14 at 17:44
  • @Tiny What do you mean by `I need this in another action class which implements ModelDriven.`. Can you post `ZoneTable.java` code. – Uchenna Nwanyanwu Jan 21 '14 at 05:06
  • @UchennaNwanyanwu : Given that class `ZoneTable.java`. I expect the selected value of `` to be set to the corresponding property in any way you may/can suggest. It may be with or without `ModelDriven`. The approach I'm following might be wrong and misleading. I expect people to show me a right approach. (It is not like that what I'm saying in my question should/must happen). – Tiny Jan 21 '14 at 10:46
  • @Tiny I now understand your question. One more thing, can you post the `jsp` that submits to the `ZoneAction.java`. I believe the fields you want to submit are all the fields in `ZoneTable.java` i.e. `(zoneId, zoneName, transporterId, zoneChargeSet and countrySet)`. – Uchenna Nwanyanwu Jan 21 '14 at 12:04
  • @UchennaNwanyanwu : Given that `Zone.jsp` looking ugly with a lot of code noise in it :). I want to submit only `zoneId`, `zoneName` and `transporterId` in this case. The rest of the fields are handled on other JSPs later on as and when required. – Tiny Jan 21 '14 at 12:19

1 Answers1

2

You need a converter to convert transporterId to Transporter Object. It goes like this:

package com.converter;

public class TransporterConverter extends StrutsTypeConverter {

    @Override
    public Object convertFromString(Map map, String[] strings, Class type) {

        String value = strings[0]; // The value of transporterId submitted from the jsp

        if (value != null && value.length() > 0) {
            try {
                Long longVal = Long.valueOf(value);
                //Integer intVal = Integer.valueOf(value);
                if (type == Transporter.class) {
                    Transporter data = find_transporter_from_the_back_by_transporter_id_using_longVal;
                    return data;
                }
            } catch (Exception ex) {}
        }
        return null;
    }

    @Override
    public String convertToString(Map map, Object o) {

        if ((o instanceof Transporter)) {
            Transporter data = (Transporter) o;
            //return the id of the Transporter Object
        }
        return null;
    }
}

The next thing to do is to map this class in a file called xwork-conversion.properties. This file must reside in your classpath i.e. in classes directory. Enter the following entries in xwork-conversion.properties

package_of_transporter_class.Transporter=com.converter.TransporterConverter

I have not tested it, but I think it should work.

If you need more information on how type converters work, follow this url.

Tiny
  • 27,221
  • 105
  • 339
  • 599
Uchenna Nwanyanwu
  • 3,174
  • 3
  • 35
  • 59
  • @Tiny I just discovered that you have `transporterId` in `ZoneAction.java`, you will need to rename it or else `struts2` will use it and ignore the one in your `Model`. – Uchenna Nwanyanwu Jan 21 '14 at 13:22
  • Yes this worked as intended! The nested property `transporterId` (actually the `transporter` object) inside the `ZoneTable` entity could be set by using this converter. The [actual converter](http://pastebin.com/wCdyqh6A) which works in my application is pretty much similar to the one shown in this answer. Thank you very much, sir. – Tiny Jan 21 '14 at 19:13
  • @Tiny You are welcome. Maybe you can award the bounty. – Uchenna Nwanyanwu Jan 22 '14 at 05:29
  • Sure but tomorrow, please :). Could you please wait until tomorrow? – Tiny Jan 22 '14 at 12:26
  • @Tiny That's Ok. If you award it, that will be my first bounty on `stackoverflow.com` – Uchenna Nwanyanwu Jan 22 '14 at 15:39