Announcement

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

    ServerObject: Update of custom object returns formatted value?

    Hi,

    Smart GWT EE (latest nightly build) + internet explorer

    I'm working with my own custom objects for which I have created simple types; implemented the necessary editors and formatters.

    My serverobject implements the CRUD operations (following the DMI example).
    Fetch works fine and values are displayed nicely in the grid or form.

    However, when editing and performing the update I don't get my custom object back but just the formatted value?

    I was expecting to get my custom object back (or a javascript object from which I could reconstruct my custom object) but all I got is the formatted value?

    Ofcourse calling the DataTools.setProperties(record, existingRecord) fails because I don't have a setter-method on my custom object that takes a string iso the object itself.

    I have included some screenshots, so you can see what I mean.

    The strange thing is that the "old" values do contain my custom objects (I guess).

    Output from the RPC window (I removed some of the other fields from the old values as they are not relevant for now):

    Code:
        "actionURL":"http://127.0.0.1:8888/silkroad/sc/IDACall", 
        "showPrompt":false, 
        "transport":"xmlHttpRequest", 
        "promptStyle":"dialog", 
        "bypassCache":true, 
        "data":{
            "criteria":{
                "billingitem_id":506146
            }, 
            "values":{
                "billingitem_id":506146, 
                "billingitemtype_enumid":"458", 
                "amount":"500 EUR"
            }, 
            "operationConfig":{
                "dataSource":"238", 
                "repo":null, 
                "operationType":"update"
            }, 
            "componentId":"isc_SilkListGrid_0", 
            "appID":"builtinApplication", 
            "operation":"238_update", 
            "oldValues":{
                "amount":{
                    "curAmount":1.401298464324817e-45, 
                    "currencyId":-2147483648, 
                    "data":{
                    }, 
                    "dataType":7, 
                    "defaultObject":209.35, 
                    "defaultValue":"209.35", 
                    "fieldNames":[
                        "value", 
                        "mcId", 
                        "curValue"
                    ], 
                    "null":false, 
                    "numeric":true, 
                    "refAmount":209.35, 
                    "zero":false
                }, "billingitem_id":506146, 
                "billingitemtype_enumid":{
                    "attributeType":382, 
                    "dataType":9, 
                    "defaultObject":450, 
                    "defaultValue":"450", 
                    "enum_id":450, 
                    "fieldNames":[
                        "value"
                    ], 
                    "null":false, 
                    "numeric":true, 
                    "sofEnumId":450
                }, 
                "correctiondate":"1900-01-01 00:00:00", 
                "creationdate":"2005-09-15 13:53:39", 
                   }
    }

    Exception thrown by the setDataTools (internal exception is thrown by our back end because we expect our own datatype):

    Code:
    outpost.dto.ISilkDTO server.stores.SilkAncestorStore.update(java.util.Map) throws java.lang.Exception
    
    with arg types: org.apache.commons.collections.map.LinkedMap
    Miles 5.16r build 26741, InternalErrorException - Created with msg: Cannot convert <458> to a basecamp.parameter.SofEnum: incorrect number of tokens.
    === 2010-06-30 15:33:34,149 [l0-4] DEBUG DataSourceDMI - Invocation threw exception
    java.lang.IllegalArgumentException: Can't convert value of type java.lang.String to target type basecamp.parameter.SofEnum
    	at com.isomorphic.util.DataTools.convertType(DataTools.java:2714)
    	at com.isomorphic.util.DataTools.createSetterArgument(DataTools.java:2606)
    	at com.isomorphic.util.DataTools.coerceProperty(DataTools.java:2553)
    	at com.isomorphic.util.DataTools.setProperties(DataTools.java:2395)
    	at com.isomorphic.util.DataTools.setProperties(DataTools.java:2307)
    	at server.stores.SilkAncestorStore.update(SilkAncestorStore.java:99)
    Thanx
    Attached Files
    Last edited by bade; 14 Oct 2010, 04:44.

    #2
    First, be sure to use DataSource.setProperties() rather than DataTools.setProperties() - this allows hint properties like dataSourceField.javaClass where automatic construction is not possible for whatever reason.

    Second, we can't really tell what's going on here from the information provided. You haven't shown the DataSource file, the code to save, or explained what your formatting logic does or how the data is being edited.

    Comment


      #3
      Hi Isomorphic,

      I used DataTools because it is in the DMI example... Keep in mind that everything I'm doing is based on what's in the DMI example.

      How can I use the setProperties on a DataSource if all I'm getting in the update is a "Map record"?
      + DataSource.setProperties is not static (DataTools.setProperties is)?
      Further more, I don't have (and want) an instance of the relevant datasource in my serverobject.

      The good thing about the DMI example was that I can use 1 abstract ancestor class that does most of the logic for me (eg. default CRUD behaviour).
      All the other stores inherit from this one and can define extra logic if necessary by letting the ancestor call an (abstract) executeAdd, executeUpdate, etc.

      It could be that I'm not getting a javascript object because of my own editor with implemented valueformatter + valueparser?
      Can you tell (by the code provided below) if this might the cause of not getting a javascript object in the update?

      The relevant fields from the ds.xml:

      Code:
      <DataSource ID="238">
      <fields>
      <field name="id" title="billingitem_id" primaryKey="true" type="integer" required="true" >
      </field>
      <field name="amount" title="amount" type="silkMCurrency" javaClass="basecamp.parameter.MCurrency" required="true" >
      </field>
      <field name="billingitemtype_enumid" title="billingItemTypeEnumID" type="silkEnum" javaClass="basecamp.parameter.SofEnum" required="true" >
      <valueMap>
      <value ID="455">Buffer Correction</value>
      <value ID="798">Buffer Correction With Hold</value>
      <value ID="456">Charge On</value>
      <value ID="451">Compensate</value>
      ...
      </valueMap>
      </field>
      </fields>
      <serverObject lookupStyle="new" className="server.stores.billingitem.BillingItemStore"/>
      </DataSource>
      </field>
      Code that's does the update (as in DMI example):

      Code:
      	public final ISilkDTO update(Map record) 
      		throws Exception 
      	{
              // look up the existing data by primary key		 
              ISilkDTO existingRecord =  this.getItemByID(record);
              existingRecord.addStatus(SofStatus.UPDATED);
                
              // DataTools.setProperties() is a SmartClient API that applies a Map of properties to a   
              // Bean via reflection, matching Map keys to setter methods on the Bean.   
              DataTools.setProperties(record, existingRecord);   
        
              // store it, and return the record-as-saved so SmartClient can update client-side caches.
      		// sync cache
      		cache.put(existingRecord.getId(), existingRecord);       
              return existingRecord; 
      	}
      The formatter used for the simpleType as short and normal display formatter.

      Code:
      public class MCurrencyFormatter implements SimpleTypeFormatter {
      	
      		
      	public String format(Object value, DataClass field, DataBoundComponent component, Record record) {
      		
      		if(value == null)
      			return "";
      		
      		if(value instanceof JavaScriptObject){
      			JavaScriptObject object = (JavaScriptObject) value;
      			
      			Double amount = JSOHelper.getAttributeAsDouble(object, SilkMCurrency.FIELD_REFAMOUNT);
      			Double curAmount = JSOHelper.getAttributeAsDouble(object, SilkMCurrency.FIELD_CURAMOUNT);
      			Integer currencyID = JSOHelper.getAttributeAsInt(object, SilkMCurrency.FIELD_CURRENCY_ID);
      			
      			
      			if (amount == Float.MIN_VALUE){
      				return "";
      			}
      			
      			if (currencyID == Integer.MIN_VALUE){
      				return "" + amount + " " + SilkClientCache.getReferenceCurrencyISOCode() /* + " \u20AC"*/;
      			}
      				
      			String code = SilkClientCache.getCurrencyISOCode(currencyID);
      			
      			
      		 return "" + amount  + " " + code;	
      		}
      	 return (String) value;
      	}
      
      }
      The value formatter and value parser of our MCurrencyEditor

      Code:
      public class SilkMCurrencyEditor extends TextItem {
      
      	private Double refAmount;
      	private Double curAmount;
      	private Integer currencyID ;
      	
      	public SilkMCurrencyEditor() {
      		super();
      
      		//link the formatter to the editor
      		super.setEditorValueFormatter(new MCurrencyEditorValueFormatter());
      		super.setEditorValueParser(new MCurrencyEditorValueParser());
                   }
      }
      				
      		
      	public class MCurrencyEditorValueFormatter implements FormItemValueFormatter {
      		
      		public String formatValue(Object value, Record record, DynamicForm form, FormItem item) {
      			
      			if(value == null)
      				return "";
      
      			if(value instanceof JavaScriptObject){
      				JavaScriptObject object = (JavaScriptObject) value;
      				
      				refAmount = JSOHelper.getAttributeAsDouble(object, SilkMCurrency.FIELD_REFAMOUNT);
      				curAmount = JSOHelper.getAttributeAsDouble(object, SilkMCurrency.FIELD_CURAMOUNT);
      				currencyID = JSOHelper.getAttributeAsInt(object, SilkMCurrency.FIELD_CURRENCY_ID);
      				
      				
      				if (refAmount == Float.MIN_VALUE){
      					return "";
      				}
      				
      				if (currencyID == Integer.MIN_VALUE){
      					return "" + refAmount + " " + SilkClientCache.getReferenceCurrencyISOCode() /* + " \u20AC"*/;
      				}
      					
      				String code = SilkClientCache.getCurrencyISOCode(currencyID);
      				
      				
      			 return "" + refAmount  + " " + code;
      			}
      			
      		 return (String) value;
      		}
      	}
      	
      	public class MCurrencyEditorValueParser implements FormItemValueParser {
      
      		public Object parseValue(String value, DynamicForm form, FormItem item) {
      			return value;
      		}
      		
      	}

      Let me know if you need more info and appreciate the input!
      Last edited by bade; 30 Jun 2010, 23:37.

      Comment


        #4
        OK, pretty straightforward then. Your SimpleType formats the original data and does no parsing (parseValue() does nothing), it just takes the formatted value as the new logical value. So when edited, what's saved is the formatted value, and you've provided no logic (client or server) to parse the formatted value and extract the data as you actually want it saved.

        Comment


          #5
          Hmmm... not so straightforward I'm afraid.
          I got what you were saying so I quickly implemented the code below.
          Code:
          	public class MCurrencyEditorValueParser implements FormItemValueParser {
          
          		public Object parseValue(String value, DynamicForm form, FormItem item) {
          			JavaScriptObject jsObject = (JavaScriptObject) item.getValue();
          			JSOHelper.setAttribute(jsObject, SilkMCurrency.FIELD_REFAMOUNT, new Double(1000));
          		 return jsObject;
          		}
          		
          	}
          However this gives an error (see below). There is no setValues( ) that takes a JavaScriptObject, so I'm a little lost now... how is it supposed to work?
          What kind of "Object" should I return?

          Keep in mind that I'm working with my own custom object behind the scenes.

          Code:
          16:17:15.202:TMR1:WARN:Log:Unrecognized type of value [object Object] returned by the FormItemValueParser::parseValue
          com.smartgwt.client.core.JsObject$SGWT_WARN: 16:17:15.202:TMR1:WARN:Log:Unrecognized type of value [object Object] returned by the FormItemValueParser::parseValue
          	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
          	at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
          	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
          	at java.lang.reflect.Constructor.newInstance(Unknown Source)
          	at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:105)
          	at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
          	at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:157)
          	at com.google.gwt.dev.shell.BrowserChannel.reactToMessagesWhileWaitingForReturn(BrowserChannel.java:1713)
          	at com.google.gwt.dev.shell.BrowserChannelServer.invokeJavascript(BrowserChannelServer.java:165)
          	at com.google.gwt.dev.shell.ModuleSpaceOOPHM.doInvoke(ModuleSpaceOOPHM.java:120)
          	at com.google.gwt.dev.shell.ModuleSpace.invokeNative(ModuleSpace.java:507)
          	at com.google.gwt.dev.shell.ModuleSpace.invokeNativeObject(ModuleSpace.java:264)
          	at com.google.gwt.dev.shell.JavaScriptHost.invokeNativeObject(JavaScriptHost.java:91)
          	at com.google.gwt.core.client.impl.Impl.apply(Impl.java)
          	at com.google.gwt.core.client.impl.Impl.entry0(Impl.java:188)
          	at sun.reflect.GeneratedMethodAccessor24.invoke(Unknown Source)
          	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
          	at java.lang.reflect.Method.invoke(Unknown Source)
          	at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:103)
          	at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
          	at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:157)
          	at com.google.gwt.dev.shell.BrowserChannel.reactToMessages(BrowserChannel.java:1668)
          	at com.google.gwt.dev.shell.BrowserChannelServer.processConnection(BrowserChannelServer.java:401)
          	at com.google.gwt.dev.shell.BrowserChannelServer.run(BrowserChannelServer.java:222)
          	at java.lang.Thread.run(Unknown Source)

          Comment


            #6
            It looks like that warning should probably be removed, however, it doesn't prevent the value from being saved to the form. Are you seeing some kind of subsequent problem?

            Comment


              #7
              I agree... (and sorry, I didn't see that it was a warning).

              However, now I get the following problem... the value "1000" is indeed returned to the listgrid, but somehow it no longer recognizes the fact that this field was changed, the update status is "lost" =>
              The update operation is no longer called on my serverobject.

              Brings me back to the question on what you guys "expect" to be returned by this operation + why there isn't a setValue(javascriptobject) operation.

              Code:
              	public class MCurrencyEditorValueParser implements FormItemValueParser {
              
              		public Object parseValue(String value, DynamicForm form, FormItem item) {
              			JavaScriptObject jsObject = (JavaScriptObject) item.getValue();			
              			JSOHelper.setAttribute(jsObject, SilkMCurrency.FIELD_REFAMOUNT, new Double(1000));
              			//will throw warning... but value will still be returned
              		 return jsObject;
              		}
              		
              	}

              Comment


                #8
                Can you explain the following in more detail:

                However, now I get the following problem... the value "1000" is indeed returned to the listgrid, but somehow it no longer recognizes the fact that this field was changed, the update status is "lost" =>
                The update operation is no longer called on my serverobject.
                What are you expecting to happen that does not happen, and or what APIs are you calling that makes you believe this?

                Comment


                  #9
                  Well, I get into the valueparser (by changing the value in the listgrid).
                  My code is called, the 1000 I hardcoded in the valueparser gets to the grid.
                  I click outside the grid and I expect the DataSource "update" to be called
                  and to get inside my serverobject.update operation.

                  FYI, I tried the valueparser (as was) just returning the formatted string and then the update on my serverobject was called! Which is perfect... however DataTools.setProperties failed because I got the formatted value (see first post). Now, I get that I implemented the value parser wrong... but why isn't it calling the update operation anymore when I return a javascript object?
                  So, this let me to believe that somehow an update status was lost (maybe because I return a javascript object).

                  The thing I'm trying to do (ultimatly) is get my custom object to the update operation so I can save my custom object to our own persistence system.

                  In the dsRequest.oldValues I can see my custom objects as I expect them to be, I want the newValues to be in the same javascript object "format"
                  (so to speak).

                  Regards,
                  Last edited by bade; 6 Jul 2010, 23:01.

                  Comment


                    #10
                    Any update on this? Please...

                    Comment


                      #11
                      Hi Isomorphic,

                      Could you please reply on this issue?

                      I have verified that we can do the basic operations (fetch, remove, custom, add) with smart GWT EE the only one that we are still missing is the "update" or "save".

                      Any help would be greatly appreciated!

                      Comment


                        #12
                        Can you confirm after the parse that the value of the field is correct even though the "update" is not called? I believe that's what you said earlier but I want to confirm. Additionally, on your grid can you call hasChanges or on your edit row, rowHasChanges, to see what is reported there after the change?

                        It appears that the problem is you are updating just a single property of the object value for the field and therefore the object being assigned is technically equal (the same) as the value originally obtained from getValue() therefore the grid doesn't see the "change". If instead your FormItemValueParser returns a newly created JsObj with the updated value along with the other properties, the update should be called correctly. Can you give this a try and report back?

                        Comment


                          #13
                          BTW, the warning has been removed and will be updated in the next nightly.

                          Comment


                            #14
                            Hey David,

                            Many thanx for the answer...

                            Tried your suggestion, did not work yet, but I think we are getting closer!
                            Implemented a sort of "clone" function... copying properties and values + returning a new jsObject.

                            Code:
                            public JavaScriptObject clone(JavaScriptObject jsObject){
                              JavaScriptObject newJsObject = JSOHelper.createObject();
                              String[] properties = JSOHelper.getProperties(jsObject);
                              for(int i=0; i < properties.length; i++){
                                 JSOHelper.setAttribute(newJsObject, properties[i], JSOHelper.getAttribute(jsObject, properties[i]));					
                              }
                             JSOHelper.setAttribute(newJsObject, SilkMCurrency.FIELD_REFAMOUNT, new Double(1000));
                             return newJsObject;
                            }
                            
                            public Object parseValue(String value, DynamicForm form, FormItem item) {
                              JavaScriptObject jsObject = (JavaScriptObject) item.getValue();	  
                             return this.clone(jsObject);
                            }
                            Good news is: in the RowEditorExitHandler the hasChanges and rowHasChanges is both "true"...
                            Don't know if this is important, BUT the getEditValues() is null (even when returning the value as a string)?!?

                            Code:
                            master.addRowEditorExitHandler(new RowEditorExitHandler() {
                            	
                              public void onRowEditorExit(RowEditorExitEvent event) {
                                 event.getEditValues();
                                GWT.log("Row has changes: " + master.rowHasChanges(event.getRowNum()));
                                 GWT.log("ListGrid has changes: " + master.hasChanges());
                               }
                            });
                            Originally posted by davidj6
                            Can you confirm after the parse that the value of the field is correct even though the "update" is not called?
                            With the new code (as written above) it does not work... however, first assigning the value to the jsObject (item.getValue() )
                            before cloning, it does work. This leds me to believe that the jsObject behind "item.getValue()" is still used in the grid and is NOT the newJsObject.
                            If this makes sense...

                            Code:
                            public Object parseValue(String value, DynamicForm form, FormItem item) {
                              JavaScriptObject jsObject = (JavaScriptObject) item.getValue();	  
                              JSOHelper.setAttribute(jsObject, SilkMCurrency.FIELD_REFAMOUNT, new Double(1000));
                             return this.clone(jsObject);
                            }
                            Last edited by bade; 14 Jul 2010, 02:48.

                            Comment


                              #15
                              If I read this correctly, returning the new JsObj fixes the hasChanges and therefore results in the correct "update" call on the DataSource. However, to get the correct value saved, you must actually update the in-use value (item.getValue) directly as well.

                              So your final code results in your desired outcome?

                              Comment

                              Working...
                              X