Announcement

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

    FormItemValueParser on FormItem containing multiple records

    I've been using SmartClient GWT for a few months, and I've been really impressed. The functionality is quite powerful and software skillfully crafted and designed.

    I have questions on how to use the FormItemValueParser. I have a TextItem that I've extended, and it is for child records of a parent record. I'd like this TextItem to display a comma separated list of codes from for the child records, and for the user to be able to quickly enter these codes as a comma separated list.

    For example, I have a form that lets you edit a country, and the TextItem mentioned above would be for the states in the country. The user could enter a list of comma separated states, and the FormItemValueParser would create the underlying records for each state. If the user entered "CO,FL,MA,NY" into the TextItem, 4 records would be created.

    This works for me if the TextItem has been pre-populated with an existing record. I can add and remove codes, and the parser works fine. However, I can't get it to work if the field is initially empty. I've tried returning a myriad of JavaScriptObjects, Records, arrays of either, etc, from my FormItemValueParser.parseValue(), and I just get exceptions back.

    So my question is, what is parseValue() expected to return when the TextItem is for a field that represents child data (multiple values)?

    Below is my code that shows a simple example of what I'm doing. It is self contained. I tried to keep it short as possible, given the complexity of what I need to do.

    Code:
    package com.smartgwt.sample.client;
    
    
    import com.google.gwt.core.client.EntryPoint;
    import com.google.gwt.core.client.JavaScriptObject;
    import com.smartgwt.client.core.KeyIdentifier;
    import com.smartgwt.client.data.DataSource;
    import com.smartgwt.client.data.DataSourceField;
    import com.smartgwt.client.data.Record;
    import com.smartgwt.client.types.DSDataFormat;
    import com.smartgwt.client.types.FieldType;
    import com.smartgwt.client.util.JSOHelper;
    import com.smartgwt.client.util.KeyCallback;
    import com.smartgwt.client.util.Page;
    import com.smartgwt.client.util.SC;
    import com.smartgwt.client.widgets.IButton;
    import com.smartgwt.client.widgets.events.ClickHandler;
    import com.smartgwt.client.widgets.form.DynamicForm;
    import com.smartgwt.client.widgets.form.FormItemValueFormatter;
    import com.smartgwt.client.widgets.form.FormItemValueParser;
    import com.smartgwt.client.widgets.form.fields.FormItem;
    import com.smartgwt.client.widgets.form.fields.FormItemIcon;
    import com.smartgwt.client.widgets.form.fields.TextItem;
    import com.smartgwt.client.widgets.layout.VLayout;
    
    
    /**
     * Entry point classes define <code>onModuleLoad()</code>.
     */
    public class BuiltInDS implements EntryPoint
    {
    
    	/**
    	 * This is the entry point method.
    	 */
    	public void onModuleLoad()
    	{
    		KeyIdentifier debugKey = new KeyIdentifier();
    		debugKey.setCtrlKey( true );
    		debugKey.setKeyName( "D" );
    
    		Page.registerKey( debugKey, new KeyCallback()
    		{
    			public void execute(String keyName)
    			{
    				SC.showConsole();
    			}
    		} );
    
    		final DynamicForm form = new DynamicForm();
    		DataSource ds = CountryDS.getInstance();
    		form.setDataSource( ds );
    
    		TextItem countryName = new TextItem( "countryName" );
    
    		MultiValueItem state = new MultiValueItem( "state", "States" );
    		state.setEditorValueParser( multiValueParser() );
    		state.setEditorValueFormatter( multiValueFormatter() );
    		form.setFields( countryName, state );
    		
    		final IButton populateButton = new IButton( "Populate" );
    		populateButton.addClickHandler( new ClickHandler()
    		{
    
    			public void onClick(com.smartgwt.client.widgets.events.ClickEvent event)
    			{
    				Record record = new Record();
    				record.setAttribute( "countryCode", "US" );
    				record.setAttribute( "countryName", "United States" );
    				Record states[] = new Record[2];
    				states[0] = new Record();
    				states[0].setAttribute( "stateName", "Colorado" );
    				states[0].setAttribute( "stateCode", "CO" );
    				states[1] = new Record();
    				states[1].setAttribute( "stateName", "Florida" );
    				states[1].setAttribute( "stateCode", "FL" );
    				record.setAttribute( "state", states );
    				form.editRecord( record );
    			}
    		} );
    		
    		final IButton validateButton = new IButton( "Validate" );
    		validateButton.addClickHandler( new ClickHandler()
    		{
    
    			public void onClick(com.smartgwt.client.widgets.events.ClickEvent event)
    			{
    				SC.warn( "getValue(state)=" + form.getField( "state" ).getValue() );
    				form.validate();
    			}
    		} );
    		
    		VLayout main = new VLayout();
    		main.addMember( form );
    		main.addMember( populateButton );
    		main.addMember( validateButton );
    
    		main.draw();		
    	}
    
    	public FormItemValueParser multiValueParser()
    	{
    		FormItemValueParser valueParser = new FormItemValueParser()
    		{
    			@Override
    			public Object parseValue(String value, DynamicForm form, FormItem item)
    			{
    				JavaScriptObject jso = (JavaScriptObject) item.getValue();
    				if ( jso == null )
    				{
    					Record items[] = new Record[ 1 ];
    					items[ 0 ] = new Record();
    					items[ 0 ].setAttribute( "stateCode", value );
    					
    					//return JSOHelper.convertToJavaScriptArray( items );
    					// The field was empty, and the user is entering data for the first time
    					// The item therefore has no JavaScriptObjects yet
    					// Not sure what to return here -
    
    					return null;
    				}
    
    				parse( jso, value );
    				return jso;
    			}
    
    			public void parse(JavaScriptObject parsed, String value)
    			{
    				String idents[] = value.split( "," );
    				int parsedLength = JSOHelper.arrayLength( parsed );
    				if ( parsedLength > idents.length )
    				{
    					for ( int j = idents.length ; j < parsedLength ; j++ )
    					{
    						removeLast( parsed );
    					}
    				}
    				for ( int i = 0 ; i < idents.length ; i++ )
    				{
    					JavaScriptObject existing = JSOHelper.getJSOArrayValue( parsed, i );
    					if ( ( existing == null )
    							|| ( idents[ i ].equals( JSOHelper.getAttribute( existing, "stateCode" ) ) == false ) )
    					{
    						JavaScriptObject jso = JSOHelper.createObject();
    						JSOHelper.setAttribute( jso, "stateCode", idents[ i ].toUpperCase() );
    						JSOHelper.arraySet( parsed, i, jso );
    					}
    				}
    			}
    
    			public native void removeLast(JavaScriptObject array) /*-{
    				array.length = array.length - 1;
    			}-*/;
    
    		};
    		return valueParser;
    	}
    
    	public FormItemValueFormatter multiValueFormatter()
    	{
    		FormItemValueFormatter valueFormatter = new FormItemValueFormatter()
    		{
    			@Override
    			public String formatValue(Object value, Record record, DynamicForm form, FormItem item)
    			{
    				String fieldname = "stateCode";
    				if ( value != null && JSOHelper.isJSO( value ) )
    				{
    					String str = "";
    					try
    					{
    						JavaScriptObject jso = (JavaScriptObject) value;
    						if ( JSOHelper.isArray( jso ) )
    						{
    							int len = JSOHelper.getArrayLength( jso );
    							if ( len > 0 )
    							{
    								JavaScriptObject elem1 = (JavaScriptObject) JSOHelper.arrayGetObject( jso, 0 );
    								str += JSOHelper.getAttribute( elem1, fieldname );
    								if ( len > 1 )
    								{
    									for ( int ix = 1 ; ix < len ; ix++ )
    									{
    										JavaScriptObject elem = (JavaScriptObject) JSOHelper.arrayGetObject( jso, ix );
    										str += "," + JSOHelper.getAttribute( elem, fieldname );
    									}
    								}
    							}
    						}
    					}
    					catch ( Throwable t )
    					{
    						str = "";
    					}
    					return str;
    				}
    				return "";
    			}
    		};
    		return valueFormatter;
    	}
    
    	private class MultiValueItem extends TextItem
    	{
    		public MultiValueItem(String name, String title)
    		{
    			super( name, title );
    			setName( name );
    			setTitle( title );
    			setEditorType( this );
    
    			FormItemIcon formItemIcon = new FormItemIcon();
    			setIcons( formItemIcon );
    		}
    	}
    
    	private static class CountryDS extends DataSource
    	{
    		// The DataSource would normally be defined external to any classes that use it.  
    
    		private static CountryDS instance = null;
    
    		public static CountryDS getInstance()
    		{
    			if ( instance == null )
    			{
    				instance = new CountryDS( "countryDS_XML" );
    			}
    			return instance;
    		}
    
    		public CountryDS(String id)
    		{
    			setID( id );
    			setDataFormat( DSDataFormat.XML );
    			setRecordXPath( "/List/country" );
    			DataSourceField countryCodeField = new DataSourceField( "countryCode", FieldType.TEXT, "Code" );
    			DataSourceField countryNameField = new DataSourceField( "countryName", FieldType.TEXT, "Country" );
    			DataSourceField capitalField = new DataSourceField( "capital", FieldType.TEXT, "Capital" );
    			DataSourceField stateField = new DataSourceField();
    			stateField.setName( "state" );
    			stateField.setTypeAsDataSource( StateDS.getInstance() );
    			stateField.setTitle( "States" );
    			setFields( countryCodeField, countryNameField, capitalField, stateField );
    			setDataURL( "ds/test_data/country.data.xml" );
    		}
    	}
    
    	private static class StateDS extends DataSource
    	{
    		// The DataSource would normally be defined external to any classes that use it.  
    
    		private static StateDS instance = null;
    
    		public static StateDS getInstance()
    		{
    			if ( instance == null )
    			{
    				instance = new StateDS( "stateDS_XML" );
    			}
    			return instance;
    		}
    
    		public StateDS(String id)
    		{
    			setID( id );
    			setDataFormat( DSDataFormat.XML );
    			setRecordXPath( "/List/country/state" );
    			DataSourceField stateCodeField = new DataSourceField( "stateCode", FieldType.TEXT, "Code" );
    			DataSourceField stateNameField = new DataSourceField( "stateName", FieldType.TEXT, "State" );
    			DataSourceField capitalField = new DataSourceField( "capital", FieldType.TEXT, "Capital" );
    			setFields( stateCodeField, stateNameField, capitalField );
    			setDataURL( "ds/test_data/country.data.xml" );
    		}
    	}
    
    }
    I'm using Smart GWT Pro 2.1, GWT 2.0.3, Firefox 3.6.6 on XP sp3.

    Thanks for your feedback!
    Chris

    #2
    Isomorphic, any thoughts on this?

    Comment


      #3
      Pull the latest nightly and I believe you will have success. There was an issue with both a formatter and parser working properly if the parser returned different objects even when the value is unchanged. The warning message shown was also extraneous.

      Here is the code I tested with for parsing:
      Code:
      public Object parseValue(String value, DynamicForm form, FormItem item)
      	{
                      if (value == null) 
                      	return null;
      
                      String idents[] = value.split( "," );
                      List<Record> values = new ArrayList<Record>();
                      for (int i = 0; i < idents.length; i++) {
                          if (idents[i] == null || idents[i] == "") continue;
                          Record r = new Record();
                          r.setAttribute("stateCode", idents[i]);
                          values.add(r);
                      }
                      return JSOHelper.convertToJavaScriptArray(values.toArray());
      	}

      Comment


        #4
        Hi David,

        Sorry I didn't follow up sooner. I've been on vacation.

        Thanks for the code! It did work for me with in combination with the latest nightly build.

        Chris

        Comment

        Working...
        X