mardi 26 mai 2015

How do I configure the controller or bind data for a Spring MVC form that submits fields for multiple entities?

Please bear with me as I ask my first question. I appreciate any feedback, especially ways I can improve the question.

I have a working Spring MVC app that was originally built with Roo. Users enter information about an Account, which is the main entity, but there are many associated entities as Accounts have dozens of POCs, Equipment, Training, Installations, Patches, Checklists, etc. Users don't want to sift through the fields that don't pertain to them, so they want to see all of the information they are responsible for in one update form, even though it might include a fields from multiple records. For example, one guy might be responsible for all of the foos, so he wants to set values for Account.fooA, PocA.fooB, PocB.fooB, PocC.fooB, EquipmentA.fooD, etc at the same time. I've created a metadata table in the database that maps each field to a category, and I'm able to dynamically generate the forms to meet this requirement. However, since the form now contains fields that are attributes of multiple entities, I am stuck on how to correctly bind the data so that it can read and then save to the database. My question is whether or not I should even attempt to bind the data? If not, what is the correct way to retrieve the values from the form? Is there a better way to meet this requirement?

Here's some of what I have now. My attempt is based on the only information I have found when searching for "dynamic forms in spring mvc" which all seem to do with creating Lists/LazyLists/AutoPopulatingLists:

The Data Element Entity, which is all meta data about other entities and attributes:

public class DataElement {

 private String dataTable;

 private String dataField;

 private String precondition;

 private String entity;

 private String entityField;

 private String entityId;

 @OneToOne
 private Phase phase;

 @OneToOne
 private Category category;

}

The Form Object:

public class DynamicForm {
private List<DataElement> dataElementList;

public DynamicForm() {
     this.dataElementList = new ArrayList<DataElement>();
}

public DynamicForm(List<DataElement> list) {
    this.dataElementList = list;
}

public List<DataElement> getDataElementList() {
    return dataElementList;
}

public void setDataElementList(List<DataElement> dataElementList) {
    this.dataElementList = dataElementList;
}

public void add(DataElement dataElement) {
    this.dataElementList.add(dataElement);
}

}

The Controller:

@RequestMapping("/dataform")
@Controller
public class DynamicFormController {

  @RequestMapping(method = RequestMethod.PUT, produces = "text/html")
    public String update(@ModelAttribute("what goes here?") Foo foo, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
      if (bindingResult.hasErrors()) {
            populateEditForm(uiModel, foo);
            return "dataform/update";
        }
        uiModel.asMap().clear();

        //for each DataElement being submitted by the form
            //set the value on the correct entity

        //merge whichever entities have been modified.

        return "redirect:/dataform/";

    }

    @RequestMapping(value = "/{id}", params = "form", produces = "text/html")
    public String updateForm(@PathVariable("id") Long id, @RequestParam(value = "account", required = false) Long accountId, Model uiModel, HttpServletRequest httpServletRequest) {
        Category category = Category.findCategory(id);
        List<DataElement> data = DataElement.findDataElementsByCategory(category).getResultList();          
        populateEditForm(uiModel, new DynamicForm(data));
        uiModel.addAttribute("account", Account.findAccount(accountId));            
        return "dataform/update";
    }

    private void populateEditForm(Model uiModel, DynamicForm form) {
        uiModel.addAttribute("dynamicForm", form);
        uiModel.addAttribute("date_format", DateTimeFormat.patternForStyle("M-", LocaleContextHolder.getLocale())); 
    }

}

Then the form itself takes all of the list elements and creates the inputs. Here I have used Account as the Model Attribute, because I had to put something in order to view the page:

<form:update id="fu_mil_navy_infosec_ekms_kmi_domain_Account" modelAttribute="account" path="/dataform" versionField="Version" z="user-managed">

<c:forEach items="${dynamicForm.dataElementList}" var="df" varStatus="i">           
       <field:dynamicinput field="${df.entityField}" id="${df.entityId }"/>
</c:forEach>

Update: My current efforts involve using reflection to call setters based on the details in the submitted form. I've never used reflection before, but from what I'm reading, reflection is expensive (which makes sense). I'm still wondering if there isn't a smarter way to do what I'm attempting here.





Aucun commentaire:

Enregistrer un commentaire