Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    How to use FormItem#setValue() with 3.1?

    The implementation of FormItem.setValue(Object) changed quite drastically with SmartGWT 3.1. I didn't bother (let alone notice) until I got

    UnsupportedOperationException, Can not convert element <n> of the array to a JavaScriptObject. Instances of class <classname> can not automatically be converted
    We have a CanvasItem to which we pass List<our-class> using setValue(Object). Later we invoke getValue() and cast the Object back to List<our-class>. I didn't find any reference in the 3.1 release notes as for how to deal with the new implementation, or did I miss it?

    #2
    You should find that the List is returned to you unchanged, and the CanvasItem samples show a similar interaction (with a List of Records as the value).

    However there were fixes in this area that might have backfired for your use case. Can you show code that reproduces the problem you're having?

    Comment


      #3
      Don't have a standalone test case yet.

      The posted exception occurs while setting the value (FormItem#setValue() -> JSOHelper#convertToJavaScriptArray()). After that the CanvasItem contains some value of type JavaScriptObject. Obviously, when try to get the value there's a ClassCastException because we try to cast it to the List<our-class> that we passed in.

      This is with 3.1 from 2013-01-28.

      Comment


        #4
        Ok, here's the test case. It fails at canvasItem.setValue(createDoohs());.

        Code:
        public class SmartGwt implements EntryPoint {
        
          public void onModuleLoad() {
            HLayout layout = new HLayout();
            CanvasItem canvasItem = new CanvasItem();
            canvasItem.setValue(createDoohs());
            
            DynamicForm form = new DynamicForm();
            form.setFields(canvasItem);
            layout.addMember(form);
            layout.draw();
          }
          
          private List<Dooh> createDoohs(){
            List<Dooh> doohs = new ArrayList<Dooh>(10);
            for (int i = 0; i < 10; i++) {
              doohs.add(new Dooh());
            }
            return doohs;
          }
          
          private static class Dooh {
            private String foo;
            private int key;
            public Dooh() {
              foo = "foo";
              key = 4711;
            }
          }
        }
        Here's the exception with stacktrace:

        Code:
        java.lang.UnsupportedOperationException: Can not convert element 0 of the array to a JavaScriptObject.  Instances of class `com.corp.smartgwt.client.SmartGwt$Dooh' can not automatically be converted.  Please see the SmartClient documentation of RPCRequest.data for a table of Java types that can be converted automatically.
            at com.smartgwt.client.util.JSOHelper.convertToJavaScriptArray(JSOHelper.java:855)
            at com.smartgwt.client.widgets.form.fields.FormItem.setValue(FormItem.java:4757)
        Prior to Smart GWT 3.1 we could pass any serializable object to setValue().

        Comment


          #5
          Thanks for the clear test case, this has been fixed for tomorrow's 3.1 and 4.0 builds.

          Comment


            #6
            Originally posted by Isomorphic View Post
            this has been fixed for tomorrow's 3.1 and 4.0 builds.
            Not quite... setValue(Object) works fine now (guess that's because you removed the strict flag).

            However, the object you get from getValue() can still not be cast to the effective type it had when passed to setValue(). The debugger shows that getValue() returns some sort of JavaScriptObject$ which seems to contain the correct values:

            Code:
            com.corp.smartgwt.client.CanvasItemValueTest$Dooh@995862,com.corp.smartgwt.client.CanvasItemValueTest$Dooh@6b606e2b,...
            I updated the test case to invoke getValue(), too.

            Code:
            public class SmartGwt implements EntryPoint {
            
              public void onModuleLoad() {
                CanvasItem canvasItem = new CanvasItem();
                canvasItem.setValue(createDoohs());
                
                List<Dooh> output = (List<Dooh>) canvasItem.getValue();
                StaticTextItem staticTextItem = new StaticTextItem("listOutput", "Output");
                staticTextItem.setValue(output.toString());
                
                DynamicForm form = new DynamicForm();
                form.setFields(staticTextItem);
                addMember(form);
              }
              
              private List<Dooh> createDoohs(){
                List<Dooh> doohs = new ArrayList<Dooh>(10);
                for (int i = 0; i < 10; i++) {
                  doohs.add(new Dooh());
                }
                return doohs;
              }
              
              private static class Dooh {
                private String foo;
                private int key;
                public Dooh() {
                  foo = "foo";
                  key = 4711;
                }
              }
            }

            Comment


              #7
              Thanks for pointing this out - this issue has been addressed as well (for 3.1 and 4.0).

              Comment


                #8
                A quick follow up note. We have made a subsequent change in this area - by always converting arrays to Lists, we introduced a backwards compatibility bug (as reported here).

                We reviewed our implementation and realized that we should only be performing this conversion for fields with the "multiple" attribute set to true.
                As of the next nightly build (March 2 or greater), you'll therefore want to explicitly call setMultiple(true) on your item in order to have this type conversion occur

                Regards
                Isomorphic Software

                Comment


                  #9
                  Thanks for letting me know. I added the required setMultiple(true) call for our CanvasItem. Our specific case works fine.

                  However, isn't this conceptually broken? Based on the Javadoc in FormItem#setMultiple() (and me growing suspicious) I ran a few tests.

                  If this CanvasItem is storing multiple values, then the return type of getValue() is JavaScriptObject (a JavaScript array object) if multiple is null or false. However, if multiple is true, then the return type is always List.
                  CanvasItem.setValue(Map<Integer, MyClass>) fails because com.smartgwt.client.util.JSOHelper.convertMapToJavascriptObject(Map, boolean) assumes all map keys to be Strings!
                  CanvasItem.setValue(Map<String, MyClass>) works but the subsequent call to (Map<String, MyClass>) CanvasItem.getValue() fails as expected because getValue() returns an ArrayList.
                  Putting a Set into the item obviously fails for the same reason.

                  If an API has a setValue(Object) and a getValue():Object methods I expect to get back exactly what I put into it. Am I looking at this from the wrong angle?

                  Comment


                    #10
                    We actually made some further tweaks to the doc and behavior subsequent to fixing the backcompat bug. Please take a look now, we believe it conforms to what you're expecting.

                    Comment

                    Working...
                    X