Announcement

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

    ListGrid SelectItem editor - Storing display and data value using option datasource

    Hello,

    I am having some trouble using a SelectItem editor backed by an option data source to add a listgrid record with both a data value and a display value.

    I have an editable listgrid with one of the fields requiring a databound dependant select. The field has both a human readable text value and a numeric id. I want the text value to be displayed in the grid and picklist and the id to be stored in a hidden field in the record.

    I can make this work by setting the option data source on the field directly and also by setting a Value Map on the editor. Neither of these methods are suitable for me because I have a large data set than needs to be filtered by other fields in the record, so I am using a SelectItem editor backed by an option data source.

    The problem is that whem I use an option datasource with the SelectItem editor, the numeric id field is stored in both the name and id fields of the list grid record. I would expect the numeric id to be stored in the id field and the human readable value to be diplayed in the name field (i.e. the behaviour seen when setting a static value map on the editor).

    I have looked at the showcase examples and I don't think they cover my specific use case. Databound select items in the examples seem to return and store only the display value.

    I am using SmartGwt 2.5 and GWT 2.3.

    Here is a complete code sample showing what I have tried. I have also shown developer console output showing the difference between Option Data Source and Value Map approaches.

    Thanks a lot.

    Code:
    	private static final String NAME = "name";
    	private static final String ID = "id";
    	
    	public void onModuleLoad() {
    
    		final ListGrid grid = new ListGrid();
    		grid.setCanEdit(true);
    		
    		grid.setDataSource(new GridDataSource());
    		grid.setAutoFitData(Autofit.BOTH);
    
    		ListGridField field = new ListGridField();
    		field.setTitle("Item");
    		field.setWidth(100);
    		
    		// I want to store the value of the id 
    		field.setName(ID);
    		field.setValueField(ID);
    		// I want to display the human readable name in the grid and pick list
    		field.setDisplayField(NAME);
    
    //		If I set the option data source on the field only the 
    //		ID is stored.  I want both name and id in the record, following the
    //		advice in the java doc for large value sets 
    
    //		field.setOptionDataSource(new OptionDataSource());
    		
    //		So I use a select item editor instead, backed by a datasource to provide the
    //		pick list values.  In real life, this will be a dependent select based
    //		on other values in the record, so the criteria will be set, but this is omitted.
    //		This displays the pick list ok, but saves the ID in both the name and id fields 
    //		of the record.  I have tried various combos of setValueField() and setDisplayField()
    //		with no success
    
    		SelectItem editor = new SelectItem();
    		editor.setOptionDataSource(new OptionDataSource());
    		
    //		Setting the value map on the editor like this gives the desired behaviour.  The id 
    //		and names are stored in the correct fields and everything looks ok.  But I need to use the 
    //		optionDataSource so I can do dynamic data lookup for dependent selects
    
    //		LinkedHashMap<String, String> valueMap = new LinkedHashMap<String, String>();
    //		valueMap.put("1", "Item One");
    //		valueMap.put("2", "Item Two");
    //		valueMap.put("3", "Item Three");
    //		editor.setValueMap(valueMap);
    		
    		field.setEditorType(editor);
    		
    		grid.setFields(field);
    		
    		Button editButton = new Button("Edit New");
    		editButton.addClickHandler(new ClickHandler() {
    			
    			public void onClick(ClickEvent event) {
    				grid.startEditingNew();
    			}
    		});
    		
    		Button logButton = new Button("Log");
    		logButton.addClickHandler(new ClickHandler() {
    			
    			public void onClick(ClickEvent event) {
    				for(Record record : grid.getDataAsRecordList().toArray()) {
    					SC.logEcho(record.getJsObj());
    				}
    			}
    		});
    				
    		VLayout vLayout = new VLayout();
    		vLayout.setMembersMargin(10);
    		vLayout.addMember(grid);
    		vLayout.addMember(editButton);
    		vLayout.addMember(logButton);
    
    		vLayout.draw();
    	}
    
    	private static class GridDataSource extends DataSource {
    
    
    		private GridDataSource() {
    			setClientOnly(true);
    
    			DataSourceTextField idField = new DataSourceTextField(ID);
    			DataSourceTextField nameField = new DataSourceTextField(NAME);
    			
    			setFields(idField, nameField);
    		}
    	}
    	
    	private static class OptionDataSource extends DataSource {
    
    		private OptionDataSource() {
    			setClientOnly(true);
    
    			DataSourceTextField idField = new DataSourceTextField(ID);
    			DataSourceTextField nameField = new DataSourceTextField(NAME);
    			
    			Record option1 = new Record();
    			option1.setAttribute(ID, "1");
    			option1.setAttribute(NAME, "Item 1");
    			
    			Record option2 = new Record();
    			option2.setAttribute(ID, "2");
    			option2.setAttribute(NAME, "Item 2");
    			
    			Record option3 = new Record();
    			option3.setAttribute(ID, "3");
    			option3.setAttribute(NAME, "Item 3");
    	
    			setFields(idField, nameField);
    			
    			setCacheData(option1, option2, option3);
    		}
    	}
    Option data source console output:

    Code:
    0:43:09.030:TMR8:INFO:ResultSet:isc_ResultSet_0 (created by: isc_ListGrid_0):Updating cache: operationType 'add' submitted by 'isc_ListGrid_0',1 rows update data:
    [
    {id: "2",
    name: "2"}
    ]
    Value map console output:

    Code:
    10:44:57.307:TMR1:INFO:ResultSet:isc_ResultSet_0 (created by: isc_ListGrid_0):Updating cache: operationType 'add' submitted by 'isc_ListGrid_0',1 rows update data:
    [
    {id: "2",
    name: "Item Two"}
    ]
    Last edited by stevesloss; 5 Sep 2011, 04:46.

    #2
    Generally you should not store both the name and id of a related object. Instead you store the id only, and the name is looked up by doing a join or its equivalent in what storage system you use.

    Hence when a SmartGWT widget saves, what it saves is the id only. Then the server is free to respond with a Record which reflects the new name indicated by the id, and this will show up in the grid via cache sync (see ResultSet docs). See also operationBinding.cacheSyncOperation - this is how you can set a join as the operation used to return data for cache sync.

    Comment


      #3
      Hi. Thanks for your response.

      I don't think I was clear enough in my original post. I agree that I only need to persist the ID when I save the data to the server. However my problem is storing the display field locally in the record while the user is still editing records in the client.

      The javadoc for ListGrid.setOptionDataSource() recommends having the id field and the display field both included in the record, and using setDisplayField() on the id field to show the human readable value. This is what I am trying to achieve. It works for records read from the server. I am running into problems editing a new record. I look up the name / id pairs from the server using a datasource. The names are shown in the picklist and it looks as expected. However, when the user selects an item it stores the numeric value in both the id and display fields (locally), so the user immediately sees a numeric value in the field after selecting a text value from the picklist. This is demonstrated in the code sample in my first post.

      Maybe I am barking up the wrong tree, but it I think I am following the recommended approach and it does work with the static value map, so it seems like I'm nearly there.

      Thanks again for your help.

      Comment


        #4
        Looking at your code again, the problem appears to be that you haven't told the *SelectItem* what the displayField and valueField are.

        Comment


          #5
          Hi,


          Thanks for getting back. I have tried that. I don't think it makes any difference:

          Code:
          editor.setDisplayField(NAME);
          editor.setValueField(ID);
          Gives the same result.

          Comment


            #6
            Oh, the problem is more basic than that. You're showing this with a client-only DataSource, and that DataSource doesn't know that changing the id field to a new value should result in a new value for the name field. The ListGrid does not try to save a new value for the name field, and should not. See our first response for how this works with a real DataSource.

            Comment


              #7
              Hi,

              Not sure I follow you, sorry. The problem occurs while the user is still editing the row. I am comfortable with the idea of saving the record and returning the record "as saved", but surely I don't want to be doing that in the middle of editing a row. I have already looked up the name and the id from the server to populate the pick list, so it doesn't make sense to have another round trip. I don't want to save the row to the server until the user has finished editing it in the client. It works fine with the static value map, which does not require a trip to the server to look up the name associated with the id.

              Sorry if I am being slow :-)

              Comment


                #8
                Why do you think you are still "in the middle of editing the row"? Have you read the Editing Overview which explains when automatic saves occur? Are you watching the RPC tab in the Developer Console to see when saves are occurring?

                Comment


                  #9
                  Ok - I have been using a local datasource with the intention of allowing the user to build up a list of records in the client, edit until happy, and then hit a save button that would use the real datasource to persist the records. Would you advise against this approach?

                  Anyway, let me review those docs and get the grid hooked up to a real datasource during editing and see how I go with that.

                  Thanks a lot for the help and advice - much appreciated.

                  Comment


                    #10
                    Although I can get this to work by returning the correct name and and id fields when the record is saved, I still think something is not quite right.
                    When the add request is sent it includes the numeric id in both fields. This means that I need to do an extra join in the db to return the name field back to the client.
                    As with the client only datasource, using a value map instead of an option data source causes the name field to be sent in the add dsrequest, so I can just persist the id and read the name value from the request to return to the client.
                    This isn't a big problem but the fact that the request contains the name field populated with the wrong value and that the value map approach results in the correct value being sent in the name field, makes me think something is still amiss.

                    Here is the request that is generated when I use a Value Map. Note "name":"Item Two",
                    Code:
                    {
                        "dataSource":"adjDS", 
                        "operationType":"add", 
                        "componentId":"isc_ListGrid_0", 
                        "data":"\r<request>\r    <data>\r        <record>\r            <id>2</id>\r            <name>Item Two</name>\r            <desc>desc 2</desc>\r        </record>\r    </data>\r    <dataSource>adjDS</dataSource>\r    <operationType>add</operationType>\r    <componentId>isc_ListGrid_0</componentId>\r    <oldValues></oldValues>\r</request>", 
                        "callback":"isc_ListGrid_0.$337(dsResponse, dsRequest)", 
                        "willHandleError":true, 
                        "showPrompt":false, 
                        "clientContext":{
                            "saveCallback":null, 
                            "newValues":{
                                "id":"2", 
                                "name":"Item Two", 
                                "desc":"desc 2"
                            }, 
                            "editInfo":{
                                "editValuesID":"_0", 
                                "rowNum":0, 
                                "colNum":1, 
                                "values":'$$BACKREF$$:.dataSource.operationType.componentId.data.callback.willHandleError.showPrompt.clientContext.saveCallback.newValues', 
                                "oldValues":null, 
                                "editCompletionEvent":"enter"
                            }
                        }, 
                        "requestId":"adjDS$6270"
                    }
                    And here is the request when I use an option data source. Note "name":"2",
                    Code:
                    {
                        "dataSource":"adjDS", 
                        "operationType":"add", 
                        "componentId":"isc_ListGrid_0", 
                        "data":"\r<request>\r    <data>\r        <record>\r            <id>2</id>\r            <name>2</name>\r            <desc>desc 2</desc>\r        </record>\r    </data>\r    <dataSource>adjDS</dataSource>\r    <operationType>add</operationType>\r    <componentId>isc_ListGrid_0</componentId>\r    <oldValues></oldValues>\r</request>", 
                        "callback":"isc_ListGrid_0.$337(dsResponse, dsRequest)", 
                        "willHandleError":true, 
                        "showPrompt":false, 
                        "clientContext":{
                            "saveCallback":null, 
                            "newValues":{
                                "id":"2", 
                                "name":"2", 
                                "desc":"desc 2"
                            }, 
                            "editInfo":{
                                "editValuesID":"_0", 
                                "rowNum":0, 
                                "colNum":1, 
                                "values":'$$BACKREF$$:.dataSource.operationType.componentId.data.callback.willHandleError.showPrompt.clientContext.saveCallback.newValues', 
                                "oldValues":null, 
                                "editCompletionEvent":"enter"
                            }
                        }, 
                        "requestId":"adjDS$6271"
                    }

                    Comment


                      #11
                      Can you show a test case where an id is sent instead of a name?

                      Comment


                        #12
                        Hi,
                        The test case in the first post in this thread demonstrates this. I produced the RPC output in the previous post by replacing the client only datasources in that example with real ones.

                        The solution of replacing the name in the record when the save returns is not suitable when auto save edits is disabled. In that case the cells display the numeric id until all the edits are saved and the name values are returned from the server. I really feel the name value needs to be added to the record in the client when the user selects their choice in the picklist (this is what happens with the value map and works fine -seems like that's how this is intended to work, and that I must either have a stupid misconfiguration or there is a bug).

                        Thanks. Please let me know if the original test case does not give you what you need.

                        Comment


                          #13
                          Sorry, there's a large difference between what you've posted - something based on clientOnly DataSources, which did not demonstrate a problem - and a test case using actual DataSources. Lots of things could have gone wrong in conversion and we have test cases that show the right behavior happening.

                          Let us know if you have a test case that suggests a framework bug.

                          Comment


                            #14
                            I will try and put something together tomorrow.
                            However, I feel the whole datasource thing is a red herring. If I have a list grid with a databound picklist editor the user displayable value is not shown until the record is saved and the server returns the display value in the correct field.

                            If I am not saving the records as they are added - i.e setAutoSaveEdits(false) - which is a reasonable thing to do and is supported by the framework -then the numeric ids are shown in the cell, not the user displayable value. This is because the numeric id is being stored in the record in the field that is supposed to contain the user displayable value. This happens before there is any interaction with the remote datasource.

                            As far as I can see the behaviour is the same for both client only datasource and real datasource. The wrong value ends up in the user display field. In the case of the remote datasource this can be corrected on the server and the name returned in the correct field.

                            At least one other person has had a similar problem:
                            http://forums.smartclient.com/showthread.php?t=13239

                            Comment


                              #15
                              Both behaviors seem to be already correct according to our automated tests, and the other thread seems to be unrelated.

                              Comment

                              Working...
                              X