Announcement

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

    Major: Custom editor in grid, value not set in Grid

    Hi,

    SC_SNAPSHOT-2011-03-21/EVAL Deployment
    FireFox 3.6.15

    I think that due to a recent fix, the following issue has (re)-appeared.

    Repo-case:

    Grid with inline editing, edit the row, press the icon.. enter a decimal value in the input field of the dialog that appears and press the button "OK".
    Value from dialog box is not saved/set to the grid!

    Post that might (and I stress 'might') have caused this issue to have re-appeared:
    http://forums.smartclient.com/showthread.php?t=15871

    I think the solution that was added for this post, was removed or changed:
    http://forums.smartclient.com/showthread.php?t=12044

    Here is my standalone test case:

    Code:
    public class Standalone implements EntryPoint {
    		
    	private static Canvas masterPanel = null;
    	
    	public void onModuleLoad() {
    		 
    		masterPanel = new Canvas(); 
    		masterPanel.setHeight100();
    		masterPanel.setWidth100();
    		masterPanel.setStyleName("pageBackground"); //background style from skin
    		
    		masterPanel.addChild(testCase3());
    		masterPanel.draw();	
    	}
    
        public ListGrid testCase3() {   
    
    	DataSourceField myPercentField = new DataSourceField();   
    	myPercentField.setName("percentageField");   
    	myPercentField.setTitle("percentageField");   
    	myPercentField.setType(new MyPercentageItem());   
    	myPercentField.setEditorType(new MyPercentageEditor());
      	
                 DataSource dataSource = new DataSource(); 
                 dataSource.setFields(myPercentField);   
                 dataSource.setClientOnly(true);
                 
                 //set the initial value        
    	JavaScriptObject jsObject = JSOHelper.createObject();
    	JSOHelper.setAttribute(jsObject, MyPercentageItem.FIELD_DOUBLE, new Double(5.89));		
            
            ListGridRecord[] result = new ListGridRecord[1];
            result[0] = new ListGridRecord();
            result[0].setAttribute("percentageField", jsObject);
               
            //the order list grid   
            ListGrid ordersList = new ListGrid();   
            ordersList.setHeight(170);   
            ordersList.setWidth(500);
            ordersList.setCanEdit(true);
            ordersList.setDataSource(dataSource);
            ordersList.setData(result);
       
            return ordersList;
        }
    
    public class MyPercentageItem extends SimpleType {   
    	
    	public final static String FIELD_DOUBLE = "floatValue";
    	private NumberFormat formatter;
    	
        public MyPercentageItem() {   
            super("myPercentageItem", FieldType.ANY);   
            
            formatter = NumberFormat.getFormat("0.00%");
            
            this.setShortDisplayFormatter(new MyPercentageFormatter());
            this.setNormalDisplayFormatter(new MyPercentageFormatter());
    
        }  
        
        public class MyPercentageFormatter implements SimpleTypeFormatter {
        	
        	public String format(Object value, DataClass field,	DataBoundComponent component, Record record) {
        		if (value == null) {
        		return "";
        	}
        
        	if (value instanceof JavaScriptObject) {
        		SC.logWarn("formatValue called");
        		
        		JavaScriptObject jsObject = (JavaScriptObject) value;
        
        		Float floatValue = JSOHelper.getAttributeAsFloat(jsObject, MyPercentageItem.FIELD_DOUBLE);
        		if(floatValue.floatValue() == Float.MIN_VALUE)
        			return "";
       		
        		return formatter.format(floatValue);
        	}
        
        	return value.toString();	
        	}
        	
        }
        
    
    }
    
    public class MyPercentageEditor extends TextItem {
    	
    	private NumberFormat formatter;
    	private String dataFormat = "0.00%";//display format
    
    	private Float floatValue;
    	
        private static Dialog dialog;   
        private static MyPercentageEditor currentEditor;   
    	
        public MyPercentageEditor() {
            super();        
            this.setChangeOnKeypress(false);
            this.setSelectOnFocus(true);
            this.setWidth("*");
            
    		FormItemIcon icon = new FormItemIcon();
    		icon.setSrc("[SKIN]/actions/edit.png");
    		icon.setTabIndex(-1);    		
    		setIcons(icon);
    		setShowIcons(true);
    
    		addIconClickHandler(new IconClickHandler() {   
                public void onIconClick(IconClickEvent event) {   
    
                    // get global coordinates of the clicked picker icon   
                    Rectangle iconRect = getIconPageRect(event.getIcon());   
    
                    // lazily create the YesNoMaybe picker dialog the first time a yesNoMaybe editor is clicked   
                    if (MyPercentageEditor.dialog == null) {   
                    	MyPercentageEditor.makeDialog();   
                    }   
                    // remember what editor is active, so we can set its value from the picker dialog   
                    MyPercentageEditor.currentEditor = MyPercentageEditor.this;   
    
                    // show the picker dialog   
                    MyPercentageEditor.showDialog(EventHandler.getX(), EventHandler.getY());   
                }   
            });
    		       
            formatter = NumberFormat.getFormat(this.dataFormat);
                    
            // link the formatter to the editor
    		this.setEditorValueFormatter(new SilkPercentageValueFormatter());
    		this.setEditorValueParser(new SilkPercentageValueParser());
            
            
        }
        
    	public class SilkPercentageValueFormatter implements FormItemValueFormatter {
    
    		public String formatValue(Object value, Record record, DynamicForm form, FormItem item) {
    
    			if (value == null) {
    				return "";
    			}
    
    			if (value instanceof JavaScriptObject) {
    				SC.logWarn("formatValue called");
    				
    				JavaScriptObject jsObject = (JavaScriptObject) value;
    
    				floatValue = JSOHelper.getAttributeAsFloat(jsObject, MyPercentageItem.FIELD_DOUBLE);
    				if(floatValue.floatValue() == Float.MIN_VALUE)
    					return "";
    
    				return formatter.format(floatValue);//show rounded decimals
    			}
    
    			return (String) value;	
    		}
    	}
    
    	public class SilkPercentageValueParser implements FormItemValueParser {
    
    		public Object parseValue(String value, DynamicForm form, FormItem item) {	
    
    			JavaScriptObject jsObject = null;
    			if (item.getValue() instanceof JavaScriptObject) {
    				jsObject = (JavaScriptObject) item.getValue();
    			}
    
    			Float floatValue = Float.MIN_VALUE;
    			
    			try{
    				//onBlur, store the value as entered by the user...
    				//check if it contains a percentage, if so => divide by 100 and store!
    				String tmp = value.replaceAll("%", "");
    				if(!tmp.equals(value)){//did the original value contain '%'
    					floatValue = Float.parseFloat(tmp) / 100;
    				}
    				else{
    					floatValue = Float.parseFloat(value);	
    				}				
    			}catch(NumberFormatException ex){
    				//could not parse input => return MIN_VALUE
    			}				
    					
    			JavaScriptObject newJsObject = JSOHelper.createObject();
    			JSOHelper.setAttribute(newJsObject, MyPercentageItem.FIELD_DOUBLE, floatValue);
    			return newJsObject;
    
    
    		}
    
    	}
    	
    	private static void makeDialog() {
    		
    		dialog = new Dialog();
    		dialog.setDismissOnEscape(true); // close on esc (not working?)
    		dialog.setDismissOnOutsideClick(true); // close when clicked outside
    		dialog.setAutoCenter(true);
    		dialog.setIsModal(true);
    		dialog.setShowEdges(false);
    		dialog.setBorder("1px solid #555555"); // show a line around the box
    		dialog.setShowHeader(false);
    		dialog.setShowToolbar(false);
    		dialog.setAutoSize(true);
    		dialog.setBackgroundColor("#FFFFFF"); // without this you could see true
    		
    		// the dialog in some browsers
    	
    		final DynamicForm dialogForm = new DynamicForm();
    		
    	              
    	    	final TextItem textItem = new TextItem("input");
    	            	  
       
    		final ButtonItem btnItem = new ButtonItem("Ok");
        		btnItem.addClickHandler(new ClickHandler() {
        			public void onClick(ClickEvent event) {
    	    		    	JavaScriptObject jsObject = JSOHelper.createObject();
    	                	Float value = new Float((String) textItem.getValue());
    	                	JSOHelper.setAttribute(jsObject, MyPercentageItem.FIELD_DOUBLE, value);                	
    	                    MyPercentageEditor.setCurrentValue(jsObject);
    	    			}
    	    		});
    	    		btnItem.setColSpan(2);
    	    		btnItem.setAlign(Alignment.RIGHT);
    	    		btnItem.setWidth(100);
    	
    	    		dialogForm.setItems(textItem, btnItem);
    	   
    	  
    	               
    	            dialog.addItem(dialogForm);   
    	}   
    	   
    		// set the specified value and dismiss the picker dialog   
            private static void setCurrentValue(Object value) {   
            	SC.logWarn("value before setValue is called: " + JSOHelper.getAttribute((JavaScriptObject) value, MyPercentageItem.FIELD_DOUBLE));
                currentEditor.setValue(value);   
                dialog.hide();   
            }   
    	  
    	    // show the picker dialog at the specified position   
    		private static void showDialog(int left, int top) {   
    		    dialog.show();   
    		    dialog.moveTo(left, top);               
    		}   
    
    }

    #2
    Hi Bart,
    I'm not sure if this is the regression you think it is.
    When you call ListGridField.setEditorType(<FormItem instance>); you're essentially passing in a template set of properties to the form item that'll be shown for this column when the grid goes into edit mode - you're not defining the live item that will be displayed.
    As such if make changes to the form item (or set its value) after the editor is showing in the grid it won't update the value visible to the user.

    The typical way to handle this is to use event.getItem() to get at the live item. So in your case if you change currentEditor to be of type FormItem

    Code:
        private FormItem currentEditor;   
       ...
    and modify your click handler to do this
    Code:
            addIconClickHandler(new IconClickHandler() {   
                public void onIconClick(IconClickEvent event) {   
    
                    // get global coordinates of the clicked picker icon   
                    Rectangle iconRect = getIconPageRect(event.getIcon());   
    
                    // lazily create the YesNoMaybe picker dialog the first time a yesNoMaybe editor is clicked   
                    if (dialog == null) {   
                        makeDialog();   
                    }   
                    // remember what editor is active, so we can set its value from the picker dialog   
                    currentEditor = event.getItem();
    
                    // show the picker dialog   
                    showDialog(EventHandler.getX(), EventHandler.getY());   
                }   
            });
    I think things will start to work for you

    Comment


      #3
      Hi Isomorphic,

      Coming to work this morning, I realised I made the mistake you found! I'm sorry about that :-/
      However, it is happening in our application (there I'm using event.getItem()).

      I'm going to make the necessary adjustments and post a new standalone test case.

      Regards,
      Bart
      Last edited by bade; 22 Mar 2011, 23:30.

      Comment


        #4
        Alright, bare with me on this one ;-)

        I changed the code and did some more investigation and I discovered the following...

        Repo case:

        1) Edit the field, click open the dialog and set "10.0" in the input field. Click "OK" and you will see that the field is changed to "1000.00%" (correct).

        2) Leave the record (quit edit mode) and then edit the record again "1000.00%" disappears and goes back to the value: "589.00%" (initial value) ?!?

        Now, difference with our app (and why we see this happening directly) is that our code is triggering a redraw (somewhere).
        So, if you want to see what we see in the app, uncomment the item.redraw in the ClickHandler of the button and do the first part of the repo case.


        Code:
        public class MyPercentageEditor extends TextItem {
        	
        	private NumberFormat formatter;
        	private String dataFormat = "0.00%";//display format
        
        	private Float floatValue;
        		
            private Dialog dialog;   
        	
            public MyPercentageEditor() {
                super();        
                this.setChangeOnKeypress(false);
                this.setSelectOnFocus(true);
                this.setWidth("*");
                
        		FormItemIcon icon = new FormItemIcon();
        		icon.setSrc("[SKIN]/actions/edit.png");
        		icon.setTabIndex(-1);    		
        		setIcons(icon);
        		setShowIcons(true);
        
        		addIconClickHandler(new IconClickHandler() {   
                    public void onIconClick(IconClickEvent event) {   
        
                        // get global coordinates of the clicked picker icon   
                        Rectangle iconRect = getIconPageRect(event.getIcon());   
        
                        if (dialog == null) {   
                        	makeDialog( event.getItem());   
                        }   
        
                        // show the picker dialog   
                        showDialog(EventHandler.getX(), EventHandler.getY());   
                    }   
                });
        		       
                formatter = NumberFormat.getFormat(this.dataFormat);
                        
                // link the formatter to the editor
        		this.setEditorValueFormatter(new SilkPercentageValueFormatter());
        		this.setEditorValueParser(new SilkPercentageValueParser());        
            }
            
        	public class SilkPercentageValueFormatter implements FormItemValueFormatter {
        
        		public String formatValue(Object value, Record record, DynamicForm form, FormItem item) {
        
        			if (value == null) {
        				return "";
        			}
        
        			if (value instanceof JavaScriptObject) {
        				SC.logWarn("formatValue called");
        				
        				JavaScriptObject jsObject = (JavaScriptObject) value;
        
        				floatValue = JSOHelper.getAttributeAsFloat(jsObject, MyPercentageItem.FIELD_DOUBLE);
        				if(floatValue.floatValue() == Float.MIN_VALUE)
        					return "";
        		
        				return formatter.format(floatValue);//show rounded decimals
        			}
        
        			return (String) value;	
        		}
        	}
        
        	public class SilkPercentageValueParser implements FormItemValueParser {
        
        		public Object parseValue(String value, DynamicForm form, FormItem item) {	
        
        			JavaScriptObject jsObject = null;
        			if (item.getValue() instanceof JavaScriptObject) {
        				jsObject = (JavaScriptObject) item.getValue();
        			}
        
        			Float floatValue = Float.MIN_VALUE;
        			
        			try{
        				//onBlur, store the value as entered by the user...
        				//check if it contains a percentage, if so => divide by 100 and store!
        				String tmp = value.replaceAll("%", "");
        				if(!tmp.equals(value)){//did the original value contain '%'
        					floatValue = Float.parseFloat(tmp) / 100;
        				}
        				else{
        					floatValue = Float.parseFloat(value);	
        				}				
        			}catch(NumberFormatException ex){
        				//could not parse input => return MIN_VALUE
        			}				
        					
        			JavaScriptObject newJsObject = JSOHelper.createObject();
        			JSOHelper.setAttribute(newJsObject, MyPercentageItem.FIELD_DOUBLE, floatValue);
        			return newJsObject;
        
        
        		}
        
        	}
        	
        	private void makeDialog(final FormItem item) {
        		
        		dialog = new Dialog();
        		dialog.setDismissOnEscape(true); // close on esc (not working?)
        		dialog.setDismissOnOutsideClick(true); // close when clicked outside
        		dialog.setAutoCenter(true);
        		dialog.setIsModal(true);
        		dialog.setShowEdges(false);
        		dialog.setBorder("1px solid #555555"); // show a line around the box
        		dialog.setShowHeader(false);
        		dialog.setShowToolbar(false);
        		dialog.setAutoSize(true);
        		dialog.setBackgroundColor("#FFFFFF"); // without this you could see true
        		
        		// the dialog in some browsers
        	
        		final DynamicForm dialogForm = new DynamicForm();
        			              
        	    final TextItem textItem = new TextItem("input");
        	    
        		final ButtonItem btnItem = new ButtonItem("Ok");
            	btnItem.addClickHandler(new ClickHandler() {
            		public void onClick(ClickEvent event) {
            	    	JavaScriptObject jsObject = JSOHelper.createObject();
                       	Float value = new Float((String) textItem.getValue());
                       	JSOHelper.setAttribute(jsObject, MyPercentageItem.FIELD_DOUBLE, value);                	
                        setCurrentValue(item, jsObject);
                        dialog.hide();   
        //                item.redraw();
            		}
            	});
        	    btnItem.setColSpan(2);
        	    btnItem.setAlign(Alignment.RIGHT);
        	    btnItem.setWidth(100);
        	
        	    dialogForm.setItems(textItem, btnItem);
        	  
        	    dialog.addItem(dialogForm);   
          
        	}   
        	   
        	// set the specified value and dismiss the picker dialog   
            private void setCurrentValue(final FormItem item, Object value) {   
            	SC.logWarn("value before setValue is called: " + JSOHelper.getAttribute((JavaScriptObject) value, MyPercentageItem.FIELD_DOUBLE));
                item.setValue(value);   
            }   
        	  
            // show the picker dialog at the specified position   
        	private void showDialog(int left, int top) {   
        	    dialog.show();   
        	    dialog.moveTo(left, top);               
        	}   
        
        }
        Thanks,
        Bart

        Comment


          #5
          Ok. Interestingly there are 2 issues here.

          The issue with immediate item.redraw() is a real bug. I've just made a change to resolve this so it should show up in the next nightly build.

          In addition to this, in this standalone test case the issue where, with redraw() commented out, save, then start editing again loses values is caused by the fact that we're editing local data but the grid has a specified dataSource (with no primary keys) and is attempting to save to that.
          If you call 'setSaveLocally(true)' on the grid the issue goes away.

          Alternatively if you want to be editing data from a datasource, you will need it to have a primaryKeyField specified and have the ListGrid get populated via a fetch.

          I suspect this second issue is only present in this test case rather than being a problem in your app. However you could resolve it via something like this:
          Code:
              public ListGrid testCase3() {   
                  
                  DataSourceField pkField = new DataSourceField();
                  pkField.setName("primaryKeys");
                  pkField.setType(FieldType.SEQUENCE);
                  pkField.setPrimaryKey(true);
                  pkField.setHidden(true);
              
                  DataSourceField myPercentField = new DataSourceField();   
                  myPercentField.setName("percentageField");   
                  myPercentField.setTitle("percentageField");   
                  myPercentField.setType(new MyPercentageItem());   
                  myPercentField.setEditorType(new MyPercentageEditor());
              
                  DataSource dataSource = new DataSource(); 
                  dataSource.setFields(myPercentField, pkField);   
                  dataSource.setClientOnly(true);
                       
                  //set the initial value        
                  JavaScriptObject jsObject = JSOHelper.createObject();
                  JSOHelper.setAttribute(jsObject, MyPercentageItem.FIELD_DOUBLE, new Double(5.89));      
                  
                  ListGridRecord[] result = new ListGridRecord[1];
                  result[0] = new ListGridRecord();
                  result[0].setAttribute("percentageField", jsObject);
                  result[0].setAttribute("primaryKeys", 0);
                  
                     
                  //the order list grid   
                  ListGrid ordersList = new ListGrid();   
                  ordersList.setHeight(170);   
                  ordersList.setWidth(500);
                  ordersList.setCanEdit(true);
                  ordersList.setDataSource(dataSource);
                  
                  // local data pattern
          //        ordersList.setData(result);
          //        ordersList.setSaveLocally(true);
          
                  // databound edit pattern (requires primary key)
                  dataSource.setTestData(result);
                  ordersList.setAutoFetchData(true);
                  
                  
             
                  return ordersList;
              }
          ===
          Update: By the way if you need an immediate workaround, one option would be to call grid.setEditValue(rowNum,fieldName,newValue) before performing the redraw on the item.

          Comment


            #6
            Alright, sounds good! Thanx.

            Yes, in our app we are working with generated datasources. So there I have a primary key field.
            I try to setup the test cases as simple as possible without re-taking all our code.
            It's sometimes hard to know if forgetting something is not causing a false positive (so to speak).

            Regards,
            Bart

            Comment


              #7
              Hi,

              Just tested with the latest build and it seems to be working.
              However, for some reason the value is not appearing in the changedValues when saving (RPC tab).
              Something that used to be working.

              I'm investigating further, but if you happen to stumble on anything that might help... please tell.

              ====== UPDATE ======

              I see the values in the RPC tab when updating via my test datasource (clientOnly=true).
              However, the generated datasource of our app is using 'dropExtraFields' and 'sparseUpdates'.
              Is there a way that I can set this up in a simple test case?

              Problem is that all of this was working before...

              ====== UPDATE ======

              Alright must be something with my code, tried in another grid with another custom type and that was working.
              Sorry for the hastle, but if you can give hints or pointers on setting up test cases using the above described datasources
              I will be happy to hear.

              Regards,
              Bart
              Last edited by bade; 24 Mar 2011, 12:01.

              Comment

              Working...
              X