Results 1 to 9 of 9

Thread: Spring 3 mvc nested form:select problem

  1. #1
    Join Date
    Apr 2012
    Posts
    10

    Default Spring 3 mvc nested form:select problem

    In my scenario, i'm developing nested select on a jsp, binded to a bean:

    Bean

    Code:
    public class WizardPanelp1Bean {
    
    private Integer id;
    private Stringofpanels stringofpanels;
    private Paneltype paneltype;
    private Integer number;
    private String paneltypestring;
    //and getters/setters... [Omitted]
    Now i have the Paneltype object, another simple bean

    Paneltype

    Code:
    private Integer id;
    private double peakpower;
    private double weight;
    private String name;
    private double dimension;
    private double conversion;
    private Set functions = new HashSet(0);
    private Set sensors = new HashSet(0);
    private Set panels = new HashSet(0);
    //[Getters/setters omitted as usual]
    So, i prepare the view, with a bean named wb a simple arraylist of panels

    Code:
     public class PanelsBean {
        private ArrayList<WizardPanelp1Bean> panels =new ArrayList<WizardPanelp1Bean>();
    and finally i go to the jsp (please note this is in a )

    Code:
    <tbody>
             <c:forEach items="${wb.panels}" varStatus="loop" var="item">
                <tr>
                    <td>${item.id}</td>
                    <td>
                        <form:select  path="panels[${loop.index}].paneltype" >
                            <c:forEach var="type" items="${types}">
                                <c:choose>
                                    <c:when test="${item.paneltype.id==type.id}">
                                        <form:option selected="selected" value="${type.id}" label="${type.name}" />
                                    </c:when>
                                    <c:otherwise>
                                        <form:option value="${type.id}" label="${type.name}" />
                                    </c:otherwise>
                                </c:choose>
                            </c:forEach>
                        </form:select>
                    </td>
                        <td><form:input style="width:180px" path="panels[${loop.index}].number" /></td>
                        <td>
                        <div>
                            <form:input style="visibility: hidden ; width:0px" path="panels[${loop.index}].id" disabled="disabled" />
                            <a href="javascript:remove(${item.id},${stringofpanels.id})" class="wb.panels.remove" >Rimuovi</a>
                        </div>
                        </td>
                    </tr>
                </c:forEach>    
         </tbody>
    every time i get a null reference to paneltype. I obviousely used a @initbinder on the controller: Initbinder

    Code:
    @InitBinder
        protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
                binder.registerCustomEditor(Paneltype.class, "paneltype", new PropertyEditorSupport() {
                    @Override
                    public void setAsText(String text) {
                        int i=0;
                        PaneltypeDAO pDAO=new PaneltypeDAO();
                        setValue(pDAO.findById(Integer.parseInt(text)));
                    }
                });
            }
    but the code never reach this. It's like the jsp is sure that the value is "null".

    Suggestions? Thanks

  2. #2
    Join Date
    Apr 2012
    Posts
    10

    Default

    i forgot the controller.

    Code:
    @RequestMapping(value = "/admin/wizard/endPanelsp1Wizard/{id}", method = { RequestMethod.GET,
    			RequestMethod.POST })
    	public ModelAndView endPanelsWizard(@Valid @ModelAttribute PanelsBean wb,BindingResult result ,@PathVariable Integer id){
    	
    		StringofpanelsDAO sDAO=new StringofpanelsDAO();
    		Stringofpanels s = sDAO.findById(id);
    		if (result.getErrorCount()==0){
    			ImplantpanelDAO iDAO= new ImplantpanelDAO();
    			for (WizardPanelp1Bean wib:wb.getPanels()){
    				
    				Implantpanel current= iDAO.findById(wib.getId());	
    				if (null!=current){
    					//aggiorno i campi
    					current.setNumber(wib.getNumber());
    					current.setPaneltype(wib.getPaneltype());
    					pstDAO.updateObject(current);
    				}else{//errore GRAVE
    					log.fatal("Errore durante l'aggiornamento delle stringhe! id:"+id);
    					return prepareMavForPanelp1Wizard(s, "Aggiornamento dati", "**Negativo: problema durante aggiornamento inverter");
    				}
    			}
    			return prepareMavForPanelp1Wizard(s, "Aggiornamento dati", "positivo");
    		}else{
    			return prepareMavForPanelp1Wizard(s, "Aggiornamento dati", "**NEGATIVO, DATI NON SALVATI,riscontrati "+result.getErrorCount()+" errori.");
    		}

  3. #3
    Join Date
    Jun 2006
    Location
    The Netherlands
    Posts
    13,632

    Default

    For starters your code is weird (at least imho that is ) next work with the framework instead of against the framework. Now lets clarify.

    Why are you constructing new instances of your daos etc? I would expect those would be injected by spring and not constructed by the code.

    Code:
    <tbody>
             <c:forEach items="${wb.panels}" varStatus="loop" var="item">
                <tr>
                    <td>${item.id}</td>
                    <td>
                        <form:select  path="panels[${loop.index}].paneltype" >
                            <c:forEach var="type" items="${types}">
                                <c:choose>
                                    <c:when test="${item.paneltype.id==type.id}">
                                        <form:option selected="selected" value="${type.id}" label="${type.name}" />
                                    </c:when>
                                    <c:otherwise>
                                        <form:option value="${type.id}" label="${type.name}" />
                                    </c:otherwise>
                                </c:choose>
                            </c:forEach>
                        </form:select>
                    </td>
                        <td><form:input style="width:180px" path="panels[${loop.index}].number" /></td>
                        <td>
                        <div>
                            <form:input style="visibility: hidden ; width:0px" path="panels[${loop.index}].id" disabled="disabled" />
                            <a href="javascript:remove(${item.id},${stringofpanels.id})" class="wb.panels.remove" >Rimuovi</a>
                        </div>
                        </td>
                    </tr>
                </c:forEach>    
         </tbody>
    This is working against the framework then rather work with the framework. Spring should take care of that. Which would leave you with a simple form:select..

    Code:
    <tbody>
             <c:forEach items="${wb.panels}" varStatus="loop" var="item">
                <tr>
                    <td>${item.id}</td>
                    <td>
                        <form:select path="panels[${loop.index}].paneltype" items="${types} itemValue="id" itemLabel="name" />
                    </td>
                        <td><form:input style="width:180px" path="panels[${loop.index}].number" /></td>
                        <td>
                        <div>
                            <form:input style="visibility: hidden ; width:0px" path="panels[${loop.index}].id" disabled="disabled" />
                            <a href="javascript:remove(${item.id},${stringofpanels.id})" class="wb.panels.remove" >Rimuovi</a>
                        </div>
                        </td>
                    </tr>
                </c:forEach>    
         </tbody>
    The problem here is that as soon as you use itemValue/itemlabel is that spring doesn't use the PropertyEditor anymore, it simply uses reflection to get the value/label. There are several issues regarding this (and not sure what the status on those is). You could remove the itemValue that way your PropertyEditor should be invoked for the getAsText method.

    Next you have another problem and that is that you are binding to a Set and not a List (or another indexed collection). You should also create a CustomCollectionEditor which rebuilds the set or change the set to a indexed collection to do proper binding.
    Marten Deinum
    Java Consultant / Pragmatist / Open Source Enthousiast / Author


    Pro Spring MVC: With Web Flow
    Conspect

    Have you read the reference guide.
    Use the [ code ] tags, young padawan

  4. #4
    Join Date
    Apr 2012
    Posts
    10

    Default

    Thanks Marten Deinum for the reply.

    Quote Originally Posted by Marten Deinum View Post
    For starters your code is weird (at least imho that is ) next work with the framework instead of against the framework. Now lets clarify.
    i'm definitely a beginner with spring

    Quote Originally Posted by Marten Deinum View Post

    Why are you constructing new instances of your daos etc? I would expect those would be injected by spring and not constructed by the code.

    Code:
                        <form:select  path="panels[${loop.index}].paneltype" >
                            <c:forEach var="type" items="${types}">
                                <c:choose>
                                    <c:when test="${item.paneltype.id==type.id}">
                                        <form:option selected="selected" value="${type.id}" label="${type.name}" />
                                    </c:when>
                                    <c:otherwise>
                                        <form:option value="${type.id}" label="${type.name}" />
                                    </c:otherwise>
                                </c:choose>
                            </c:forEach>
                        </form:select>
    This is working against the framework then rather work with the framework. Spring should take care of that. Which would leave you with a simple form:select..


    Code:
                        <form:select path="panels[${loop.index}].paneltype" items="${types} itemValue="id" itemLabel="name" />
    
    i agree with you mr Deinum, the problem is i need to select the value of the entity(it's a modify not a .
    The form:select itself not bind to the current value of the entity.

    so this is why i used this code:

    <c:when test="${item.paneltype.id==type.id}">

    to select the current item.
    You are asking to me that form:select bind current value with a selected="selected"?

    Quote Originally Posted by Marten Deinum View Post
    The problem here is that as soon as you use itemValue/itemlabel is that spring doesn't use the PropertyEditor anymore, it simply uses reflection to get the value/label. There are several issues regarding this (and not sure what the status on those is). You could remove the itemValue that way your PropertyEditor should be invoked for the getAsText method.

    Next you have another problem and that is that you are binding to a Set and not a List (or another indexed collection). You should also create a CustomCollectionEditor which rebuilds the set or change the set to a indexed collection to do proper binding.
    for the first step, thanks, i will try.
    For the second, the sets are unnecessary because they are the hibernate ORM mappings.

    I'm shy but i need to admit that i have done a full backing of arraylists against sets, and remapped them when i need to persist, to avoid the problem of Set type (no index)

  5. #5
    Join Date
    Jun 2006
    Location
    The Netherlands
    Posts
    13,632

    Default

    i agree with you mr Deinum, the problem is i need to select the value of the entity(it's a modify not a .
    The form:select itself not bind to the current value of the entity.

    so this is why i used this code:

    <c:when test="${item.paneltype.id==type.id}">

    to select the current item.
    You are asking to me that form:select bind current value with a selected="selected"?
    Spring will do that for you. If the current item is in the items list spring will select it, it depends however on the proper configuration of your PropertyEditors/Converters.


    For the second, the sets are unnecessary because they are the hibernate ORM mappings.
    And why would that need to be a Set? I used Lists to do mappings without problems (even without explicit indexes), drawback is that you need to check for yourself if an item is already added to the list (a set has only unique items).
    Marten Deinum
    Java Consultant / Pragmatist / Open Source Enthousiast / Author


    Pro Spring MVC: With Web Flow
    Conspect

    Have you read the reference guide.
    Use the [ code ] tags, young padawan

  6. #6
    Join Date
    Apr 2012
    Posts
    10

    Default

    Quote Originally Posted by Marten Deinum View Post
    Spring will do that for you. If the current item is in the items list spring will select it, it depends however on the proper configuration of your PropertyEditors/Converters.
    definitely agree, it works at the same as my old code. Never stop learning

    However, still getting no answer from my @InitBinder, i get the same old story:

    Field error in object 'panelsBean' on field 'panels[0].paneltype': rejected value [...Paneltype@2e22e4]; codes [typeMismatch.panelsBean.panels[0].paneltype,typeMismatch.panelsBean.panels.paneltyp e,typeMismatch.panels[0].paneltype,typeMismatch.panels.paneltype,typeMisma tch.paneltype,typeMismatch.it.pstmarche.model.Pane ltype,typeMismatch]; arguments [org.springframework.context.support.DefaultMessage SourceResolvable: codes [panelsBean.panels[0].paneltype,panels[0].paneltype]; arguments []; default message [panels[0].paneltype]]; default message [Failed to convert property value of type 'java.lang.String' to required type '....Paneltype' for property 'panels[0].paneltype'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [....Paneltype] for property 'paneltype': no matching editors or conversion strategy found]

    so my InitBinder ...is not binding

    Quote Originally Posted by Marten Deinum View Post
    And why would that need to be a Set? I used Lists to do mappings without problems (even without explicit indexes), drawback is that you need to check for yourself if an item is already added to the list (a set has only unique items).

    this is the .hbm of the paneltype class:


    Code:
    <hibernate-mapping>
        <class name="....Paneltype" table="paneltype" schema="public">
            <id name="id" type="integer">
                <column name="id" />
                <generator class="identity" />
            </id>
            <property name="peakpower" type="double">
                <column name="peakpower" precision="17" scale="17" not-null="true" />
            </property>
            <property name="weight" type="double">
                <column name="weight" precision="17" scale="17" not-null="true" />
            </property>
            <property name="name" type="string">
                <column name="name" />
            </property>
            <property name="dimension" type="double">
                <column name="dimension" precision="17" scale="17">
                    
                </column>
            </property>
            <property name="conversion" type="double">
                <column name="conversion" precision="17" scale="17">
              
                </column>
            </property>
            <set name="functions" inverse="true">
                <key>
                    <column name="idpaneltype" not-null="true" />
                </key>
                <one-to-many class="....Function" />
            </set>
            <set name="sensors" inverse="true">
                <key>
                    <column name="idpaneltype">
                        <comment>id del pannello collegato con il sensore</comment>
                    </column>
                </key>
                <one-to-many class="it.pstmarche.model.Sensor" />
            </set>
            <set name="panels" inverse="true">
                <key>
                    <column name="idpaneltype" not-null="true" />
                </key>
                <one-to-many class="...Panel" />
            </set>
        </class>
    </hibernate-mapping>
    we have mapped this with sets because is the default of the rev-eng of myeclipse

  7. #7
    Join Date
    Apr 2012
    Posts
    10

    Default

    i think i found the problem, but i cannot figure out a solution!

    if i bind with

    Code:
    @InitBinder
    	protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
    	    binder.registerCustomEditor(Paneltype.class, "panels[0].paneltype", new PropertyEditorSupport() {
    	        @Override
    	        public void setAsText(String text) {
    	            PaneltypeDAO pDAO=new PaneltypeDAO();
    	            setValue(pDAO.findById(Integer.parseInt(text)));
    	        }
    	    });
    	}
    note: panels[0].paneltype it works and the initbinder is correctly called.
    However, this is an array, and i have dynamical data.

    There is a workaround or i'm missing something(probably this )?

  8. #8
    Join Date
    Jun 2006
    Location
    The Netherlands
    Posts
    13,632

    Default

    Use the registerCustomEditor method with 2 arguments, this will register the editor for all fields of the given type.
    Marten Deinum
    Java Consultant / Pragmatist / Open Source Enthousiast / Author


    Pro Spring MVC: With Web Flow
    Conspect

    Have you read the reference guide.
    Use the [ code ] tags, young padawan

  9. #9
    Join Date
    Apr 2012
    Posts
    10

    Default

    Quote Originally Posted by Marten Deinum View Post
    Use the registerCustomEditor method with 2 arguments, this will register the editor for all fields of the given type.
    thank you, it worked!

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •