Announcement

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

    ComboBoxItem, RejectInvalidValueOnChange

    Hi,

    The javadoc states that if you set rejectInvalidValueOnChange, you can revert to the previous value.

    I don't see that happening?!? Did I forget something?
    I have added a standalone test case.

    SmartClient Version: SC_SNAPSHOT-2011-03-07/EVAL Deployment

    I set an initial value on my comboBox (2 => Green). I type in purple and tab out.

    Originally posted by javadoc
    void setRejectInvalidValueOnChange(Boolean rejectInvalidValueOnChange)

    If validateOnChange is true, and validation fails for this item on change, with no suggested value, should we revert to the previous value, or continue to display the bad value entered by the user. May be set at the item or form level.

    Note : This is an advanced setting

    Parameters:
    rejectInvalidValueOnChange rejectInvalidValueOnChange Default value is false
    Code:
    public class Standalone implements EntryPoint {
    	
    	private static Canvas masterPanel = null;
    	
    	
    	 public void onModuleLoad() {  
    	  		
    		//masterPanel should be a Layout
    		masterPanel = new Canvas(); 
    		masterPanel.setHeight100();
    		masterPanel.setWidth100();
    		masterPanel.setStyleName("pageBackground"); //background style from skin
    				
    		ComboBoxItem comboBox = new ComboBoxItem();
    		comboBox.setName("test");
            comboBox.setTitle("Select");   
            comboBox.setHint("<nobr>A ComboBox with styled entries</nobr>"); 
            comboBox.setShowPickerIcon(true);
            comboBox.setAddUnknownValues(false);
            comboBox.setDefaultToFirstOption(false);
            comboBox.setCompleteOnTab(true);	            
            comboBox.setValidateOnChange(true);
            comboBox.setRejectInvalidValueOnChange(true);
            
            //set the initial value
            comboBox.setValue(2);
     
            LinkedHashMap<String, String> valueMap = new LinkedHashMap<String, String>();
    	    valueMap.put("1", "Red");
    	    valueMap.put("2", "Green");
    	    valueMap.put("3", "Blue");	            
            comboBox.setValueMap(valueMap);
            
            comboBox.addFocusHandler(new FocusHandler() {
    			
    			public void onFocus(FocusEvent event) {
    				event.getItem().showPicker();						
    			}
    		});
    
            //the order list grid   
    	    DynamicForm form = new DynamicForm();   
    	    form.setHeight(170);   
    	    form.setWidth(500);	    
    	    form.setFields(comboBox);
    	          
    		masterPanel.addChild(form);
    		masterPanel.draw();	
    	}
    }
    Last edited by bade; 9 Mar 2011, 06:08.

    #2
    Hi Bade
    We see where the confusion is here.
    The reason this isn't working for you is that the rejectInvalidValue only rejects the value if it fails validation - so to disallow null values you'd have to set required to true on the field.

    However this will have the side effect of displaying a validation error on the field which is probably not what you're after.

    The easiest way to make this work would be to add a change handler which simply rejects null values - something like this:

    Code:
            comboBox.addChangeHandler(new ChangeHandler() {
                
                @Override
                public void onChange(ChangeEvent event) {
                    // if the user cleared the value, reset to the previous value.
                    // Since addUnkonwnValues is false this also handles the
                    // user entering a value that isn't present in the valueMap
                    if (event.getValue() == null) event.cancel();
                    
                }
            });

    Comment


      #3
      Hi,

      SmartClient Version: SC_SNAPSHOT-2011-03-09/EVAL Deployment
      FireFox 3.6.15
      Windows 7

      Taking this one step further, I now have the following problem.

      I implemented the onChange as you said, but my FormItemInputTransformer is causing me a headache.

      Reading up on the documentation a bit, the FormItemInputTransformer is called before the ChangeHandler, so it too is receiving null. But the weird thing is it is only happening with my custom types... so when the values coming trough are actualy javascript objects representing our custom types.

      After some more investigation, the problem can be traced to the initialised value of my custom object (Integer.MIN_VALUE). This happens when we create a new object.

      What is happening is that the user can't switch the value.

      Test case: open drop down, select the color "Red".
      FormItemInputTransformer ALWAYS receives null for value.
      So, this blocks any custom logic I want to do.

      I have added a standalone test case below.

      Code:
      public class Standalone implements EntryPoint {
      		
      	private static Canvas masterPanel = null;
      	
      	public void onModuleLoad() {
      		   		
      		//masterPanel should be a Layout
      		masterPanel = new Canvas(); 
      		masterPanel.setHeight100();
      		masterPanel.setWidth100();
      		masterPanel.setStyleName("pageBackground"); //background style from skin
      		
      		masterPanel.addChild(testCase10());
      		masterPanel.draw();	
      	}
      
      
          public VLayout testCase10() {
          	
          	VLayout test = new VLayout();
          	test.setWidth100();
          	test.setHeight100();
              	
              //static data for this comboBox
        	  	ListGridRecord[] records = new ListGridRecord[3];
        	  	records[0] = new ListGridRecord();
        	  	records[0].setAttribute("enumeration_id", 1);
        	  	records[0].setAttribute("enumerationValue", "Red");
        	  	records[0].setAttribute("forUpdate", true);
        	  	records[1] = new ListGridRecord();
        	  	records[1].setAttribute("enumeration_id", 2);
        	  	records[1].setAttribute("enumerationValue", "Green");
        	  	records[1].setAttribute("forUpdate", false);//selecting green is not allowed
        	  	records[2] = new ListGridRecord();
        	  	records[2].setAttribute("enumeration_id", 3);
        	  	records[2].setAttribute("enumerationValue", "Blue");
        	  	records[2].setAttribute("forUpdate", true);
        	  	
              DataSourceIntegerField pkDS = new DataSourceIntegerField("enumeration_id");   
      //        pkDS.setHidden(true);   
              pkDS.setPrimaryKey(true);
        	  	
              DataSourceTextField enumValueDS = new DataSourceTextField("enumerationValue");
              
              DataSourceBooleanField forUpdateDS = new DataSourceBooleanField("forUpdate");
        	  	
        	  	final DataSource datasource = new DataSource();
        	  	datasource.setID("testDS");
        	  	datasource.setFields(pkDS, enumValueDS, forUpdateDS);
        	  	datasource.setCacheData(records);
        	  	datasource.setClientOnly(true);  	    
        	    	        
              ListGrid pickListProperties = new ListGrid();
              pickListProperties.setAutoFetchData(false);
              pickListProperties.setWidth(500);
              pickListProperties.setHeight(300);
              pickListProperties.setDataSource(datasource);
              pickListProperties.setEmptyMessage("No data");
              pickListProperties.setRecordCanSelectProperty("forUpdate");
              pickListProperties.setShowHeader(false);
              pickListProperties.setCellFormatter(new CellFormatter() {   
      
                  public String format(Object value, ListGridRecord record, int rowNum, int colNum) {   
                  	
                      Boolean forUpdate = record.getAttributeAsBoolean("forUpdate");   
                      String enumValue = record.getAttribute("enumerationValue");
                      if(forUpdate)//blue
                      	return "<span style='color:#113377'>" + enumValue + "</span>";
                      else//grey
                      	return "<i><span style='color:#aaaaaa'>" + enumValue + "</span></i>";
                      	
                  }   
              });
                      
              
      //        pickListProperties.addRecordClickHandler(new RecordClickHandler() {
      //			
      //			public void onRecordClick(RecordClickEvent event) {
      //				 Boolean forUpdate = event.getRecord().getAttributeAsBoolean("forUpdate");
      //				 if(!forUpdate) event.cancel();
      //			}
      //		});
          
              ListGridField valueField1 = new ListGridField("enumerationValue");
              
              ComboBoxItem combo =new ComboBoxItem("enumeration_id");
              combo.setOptionDataSource(datasource);   
              combo.setValueField("enumeration_id");   
              combo.setDisplayField("enumerationValue");   
              combo.setPickListProperties(pickListProperties);
              combo.setPickListFields(valueField1);
              combo.setAddUnknownValues(false);
              combo.setCompleteOnTab(true);
              combo.setRejectInvalidValueOnChange(true);
              combo.addChangeHandler(new ChangeHandler() {
                              
                  public void onChange(ChangeEvent event) {
                      // if the user cleared the value, reset to the previous value.
                      // Since addUnkonwnValues is false this also handles the
                      // user entering a value that isn't present in the valueMap
                      if (event.getValue() == null)
                      	event.cancel();               
                  }
              });
              
              combo.setInputTransformer(new FormItemInputTransformer() {
      			
      			public Object transformInput(DynamicForm form, FormItem item, Object value,	Object oldValue) {
      				if(value == null){
      					SC.logWarn("Value in inputtransformer is NULL");//why?!?
      					return item.getValue();//fallback in this case
      				}			
      				
      				//return the transformed value (newId)
      		        JavaScriptObject jsObject = JSOHelper.createObject();
      		        JSOHelper.setAttribute(jsObject, "enumeration_id", (Integer) value);
      		        JSOHelper.setAttribute(jsObject, "dataType", 9);
      				return jsObject;				
      			}
      		});
              
              combo.setEditorValueFormatter(new FormItemValueFormatter() {
      			
      			public String formatValue(Object value, Record record, DynamicForm form, FormItem item) {
      				if (value == null) {
      					return "";
      				}
      
      				if (value instanceof JavaScriptObject) {
      					JavaScriptObject jsObject = (JavaScriptObject) value;
      					String id = JSOHelper.getAttribute(jsObject, "enumeration_id");
      					Integer enumeration_id = new Integer(id);
      					
      					if (enumeration_id.equals(Integer.MIN_VALUE)) {
      						return "";
      					}
      					
      					//retrieve the records for this enumeration => cached datasource
      					Record[] values = datasource.getCacheData();
      					for(int i=0; i < values.length; i++){
      						if(enumeration_id.equals(values[i].getAttributeAsInt("enumeration_id")))
      							return values[i].getAttribute("enumerationValue");							
      					}
      					
      					return id;//unknown value
      				}
      
      				return (String) value;				
      			}
      		});
                             
              DynamicForm form = new DynamicForm();   
              form.setHeight(300);   
              form.setWidth(500);
              form.setFields(combo);
              //initialised values => pay attention to the MIN_VALUE
              JavaScriptObject jsObject = JSOHelper.createObject();
              JSOHelper.setAttribute(jsObject, "enumeration_id", Integer.MIN_VALUE);
              JSOHelper.setAttribute(jsObject, "dataType", 9);
              
              form.setValue("enumeration_id", jsObject);
              
              test.addMember(form);
      		return test;
          }
      }
      Last edited by bade; 16 Mar 2011, 06:29.

      Comment


        #4
        Ok we see the problem and we'll let you know when we have a resolution

        Comment


          #5
          Hi again,
          We've made some changes to ComboBoxItem which should resolve this odd behavior. Something was quite broken by a recent change which has now been put right.

          However this does look like quite a complicated use case.
          Things will hopefully just work with the next nightly build but if you continue to encounter oddities here it might help if you lay out exactly what you're trying to achieve here - what your data pattern is and what user interaction behavior you're trying to get working.

          Thanks
          Isomorphic Software

          Comment


            #6
            You can find the "use case" description on the private forum.
            Last edited by bade; 21 Mar 2011, 00:03.

            Comment


              #7
              Talking about "oddities", if I now set the initial value to '3' (last one in the list) it's not selected in the dropdown anymore (when clicking open the drop down).

              Something that did work before the fix.

              Using the same standalone test case, change the initial value as follows:

              Code:
                      //initialised values => pay attention to the MIN_VALUE
                     //set initial value to "Blue" (last one in the list)
                      JavaScriptObject jsObject = JSOHelper.createObject();
                      JSOHelper.setAttribute(jsObject, "enumeration_id", 3 /*Integer.MIN_VALUE*/);
                      JSOHelper.setAttribute(jsObject, "dataType", 9);
                      
                      form.setValue("enumeration_id", jsObject);

              Comment


                #8
                Any update on this one?

                Thanx
                Bart

                Comment


                  #9
                  We're looking into it - probably have a resolution tomorrow

                  Comment


                    #10
                    Hi Bart,
                    After spending a little time looking at this, it looks like an approach that's going to be very difficult to support.

                    The problem is that there are 2 tiers of abstraction here - the display-field value to the data-field value, and then the data-field value to the final custom data object.

                    The comboBoxItem value selection ultimately relies on the item's value (in this case the final data object) to determine which record to highlight in the list, and uses standard databinding methods to find the relevant item in the list of data returned from the option dataSource. Because of the additional transformation to a wrapped data object this logic is failing to find the appropriate record - the ComboBoxItem doesn't understand how to use the custom object for searching or filtering.

                    To get this working we'd need an new override/entry point method to allow you to write custom code to compare the wrapped data value with records in the picklist so the pickList could call this code to identify the correct record. To do this right we'd probably need to ensure this new API was called in multiple places in the pickList - essentially make the pickList "understand" custom data types - which has implications for other functionality such as sorting, etc.

                    This is basically a variation on the issue we're discussing in another thread about attempting to use client-side filtering when field values are custom Java objects.

                    ---
                    Would it be possible to get rid of the extra layer of abstraction at this level and work with simple atomic values within the form, then convert to the complete custom object later in your code path?
                    In other words - have the comboBoxItem simply have optionDataSource, valueField and displayField specified, then when you call getValues() on the form, you'd get back just the simple enumeration_id value for this field, and you could build the custom object from that.

                    Comment


                      #11
                      Hi Isomorphic,

                      I think I can see where you're going with your suggestion and it could be a valid solution. I'm going to take some time to think about this and discuss this with the team as well.

                      Might just be that we need to discuss with somebody of Isomorphic where we might come across some more hurdles for our custom object approach?Like the client side filtering (for instance), formula builder, etc. The sooner we catch this in the project, the better!

                      Let me know what you think and I'll get back with an answer asap.

                      Regards,
                      Bart

                      Comment


                        #12
                        Yes, client-side filtering, also sorting, simple equality checks like seeing if valuesHaveChanged(), serialization and de-serialization, a need for a sort of meta-type indicator so we know eg that a SimpleType can produce a numeric value for use in the formula builder - and even this long list we think is still just the "tip of the iceberg".

                        This appears to be a lot of extra work on your part even beyond the necessary sponsorship of all the new APIs needed to fully support custom field values that aren't based on a normal atomic type (String, Number, etc).

                        Comment

                        Working...
                        X