ValuesManager to control multiple forms

I’m trying to use a ValuesManager to control multiple forms (with different datasource on them) at the same time. When i add a form to the values manager instance i get the following error:

06:26:35.836 [ERROR] Failed to create an instance of 'net.keydriving.backoffice.gui.crm.client.form.basic.CompanyForm' via deferred binding
com.google.gwt.core.client.JavaScriptException: (TypeError): this.getDataSource() is undefined  fileName: http://localhost:8080/net-keydriving-backoffice-gui-crm-0.0.1-SNAPSHOT/crm/sc/modules/ISC_Forms.js  lineNumber: 2633  stack: ([object Object])@http://localhost:8080/net-keydriving-backoffice-gui-crm-0.0.1-SNAPSHOT/crm/sc/modules/ISC_Forms.js:2633 ([object GWTJavaObject])@http://localhost:8080:40 @:0 ([object GWTJavaObject],131229,[object GWTJavaObject])@http://localhost:8080/net-keydriving-backoffice-gui-crm-0.0.1-SNAPSHOT/crm/hosted.html?crm:56 ([object Object],0,0)@http://localhost:8080:2398 anonymous([object Object],0,0)@http://localhost:8080/net-keydriving-backoffice-gui-crm-0.0.1-SNAPSHOT/crm/sc/modules/ISC_Grids.js:828 ([object Object],0,0)@http://localhost:8080/net-keydriving-backoffice-gui-crm-0.0.1-SNAPSHOT/crm/sc/modules/ISC_Grids.js:696 (0,0)@http://localhost:8080/net-keydriving-backoffice-gui-crm-0.0.1-SNAPSHOT/crm/sc/modules/ISC_Grids.js:693 ([object Object],(void 0))@http://localhost:8080/net-keydriving-backoffice-gui-crm-0.0.1-SNAPSHOT/crm/sc/modules/ISC_Grids.js:690 ([object Object],(void 0))@http://localhost:8080/net-keydriving-backoffice-gui-crm-0.0.1-SNAPSHOT/crm/sc/modules/ISC_Core.js:2924 ([object Object],"click")@http://localhost:8080/net-keydriving-backoffice-gui-crm-0.0.1-SNAPSHOT/crm/sc/modules/ISC_Core.js:1453 ([object Object])@http://localhost:8080/net-keydriving-backoffice-gui-crm-0.0.1-SNAPSHOT/crm/sc/modules/ISC_Core.js:1314 ([object MouseEvent],(void 0))@http://localhost:8080/net-keydriving-backoffice-gui-crm-0.0.1-SNAPSHOT/crm/sc/modules/ISC_Core.js:1301 ([object MouseEvent])@http://localhost:8080/net-keydriving-backoffice-gui-crm-0.0.1-SNAPSHOT/crm/sc/modules/ISC_Core.js:1292 ((function (_1, _2) {var _3 = isc.EH;if (isc.Browser.isIE && !_3.$j6) {var _4 = _3.lastEvent;_4.eventType = _3.MOUSE_DOWN;_3.handleMouseDown(null, _3.lastEvent);}if (!_2) {_3.$ku = true;}var _5 = _3.$k5(_1, _2);_3.$ku = false;if (isc.Browser.isSafari) {_5 = true;}return _5;}),[object MouseEvent])@http://localhost:8080/net-keydriving-backoffice-gui-crm-0.0.1-SNAPSHOT/crm/sc/modules/ISC_Core.js:1513 anonymous([object MouseEvent])@http://localhost:8080/net-keydriving-backoffice-gui-crm-0.0.1-SNAPSHOT/crm/sc/modules/ISC_Core.js:60  

 	at com.google.gwt.dev.shell.BrowserChannelServer.invokeJavascript(BrowserChannelServer.java:195)
 	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.invokeNativeVoid(ModuleSpace.java:284)
 	at com.google.gwt.dev.shell.JavaScriptHost.invokeNativeVoid(JavaScriptHost.java:107)
 	at com.smartgwt.client.widgets.form.ValuesManager.addMember(ValuesManager.java)
 	at net.keydriving.backoffice.gui.crm.client.form.basic.AbstractBasicForm.createFormLayout(AbstractBasicForm.java:346)

It seems that you have to assign a datasource to the values manager to get it working, the documentation does not specify this but suggest this is optional? I added a Datasource to work around the issue, but only a little further i’m stuck once again.

I have a VStack which creates a DynamicForm and adds it to itself as a member and to the ValuesManager. Some other forms are also generated in code and all of them are added to the same ValuesManager. But when trying to access the forms on the ValuesManager via the getMembers method i’m greeted with a ClassCastException.

Code which generates the first form added:

protected void createFormLayout(final FormType formType, final Boolean useAllDataSourceFields) {
        GWT.log("Creating the underling dynamicform.", null);
        _form = new DynamicForm();
        _form.setAutoFocus(Boolean.TRUE);
        _form.setUseAllDataSourceFields(useAllDataSourceFields);
        // General layout stuff
        _form.setWidth100();
        _form.setNumCols(2);
        _form.setColWidths("50%", "50%");
        _form.setTitleOrientation(TitleOrientation.TOP); // Labels above the boxes
        // Set the datasource for the form, and add the form to the values manager
        _form.setDataSource(DataSource.get(formType.getDataSourceId()));
        getValuesManager().addMember(_form);

        GWT.log("Creating the header for the form.", null);
        _header = new FormHeader(this);
        addRecordChangedHandler(_header);

        addMember(_header);
        addMember(_form);
    }

The form gets added here to the values manager, but when a retrieval is done, like for example:

    public void enable() {
        GWT.log("Enabling forms via valuesManager: " + getValuesManager());
        for (final DynamicForm form : getValuesManager().getMembers()) {
            GWT.log("Enabling form: " + form);
            form.enable();
        }
    }

A ClassCastException is thrown:

06:49:57.636 [ERROR] Uncaught exception escaped
java.lang.ClassCastException: net.keydriving.backoffice.gui.crm.client.form.basic.CompanyForm cannot be cast to com.smartgwt.client.widgets.form.DynamicForm
 	at com.smartgwt.client.widgets.form.DynamicForm.getOrCreateRef(DynamicForm.java:87)
 	at com.smartgwt.client.widgets.form.ValuesManager.convertToDynamicFormArray(ValuesManager.java:649)
 	at com.smartgwt.client.widgets.form.ValuesManager.getMembers(ValuesManager.java:638)
 	at net.keydriving.backoffice.gui.crm.client.form.basic.AbstractBasicForm.enable(AbstractBasicForm.java:326)
 	at net.keydriving.backoffice.gui.crm.client.form.AbstractForm$1.onSuccess(AbstractForm.java:62)
 	at net.keydriving.backoffice.gui.crm.client.form.AbstractForm$1.onSuccess(AbstractForm.java:1)
 	at com.google.gwt.user.client.rpc.impl.RequestCallbackAdapter.onResponseReceived(RequestCallbackAdapter.java:216)
 	at com.google.gwt.http.client.Request.fireOnResponseReceived(Request.java:287) 	at com.google.gwt.http.client.RequestBuilder$1.onReadyStateChange(RequestBuilder.java:393) 	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 	at java.lang.reflect.Method.invoke(Method.java:597)
 	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.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.NativeMethodAccessorImpl.invoke0(Native Method)
 	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 	at java.lang.reflect.Method.invoke(Method.java:597) 	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(Thread.java:637)

I tried assigning a ID to the form, but each time the Class which nested the created form is returned as a member of the valuesManager? Why am i getting the class encapsulating the DynamicForm added instead of the DynamicForm itself which was added?

Thanks in advance

Probably because that’s what you accidentally passed to a SmartGWT that expects a DynamicForm. Could explain the other problem too. Try simplifying to a testcase if you think there’s a SmartGWT bug here.

Ok, i’ve done some additional testing and still run into the same problem.

As requested in the FAQ some data:

  • GWT version: 2.0.2
  • SmarGWT version: 2.1 (but also 2.0 suffers from this)
  • Development mode accessed via firefox webbrowser.

I’ve attached a minimal test case which uses the Animals datasource from the smartgwt showcase as a example.

If you run the test case you will get a error like this:

00:06:40.702 [ERROR] Unable to load module entry point class net.keydriving.smartgwt.client.Test (see associated exception for details)
com.google.gwt.core.client.JavaScriptException: (TypeError): this.getDataSource() is undefined  fileName: http://localhost:8080/SmartGWTTest/test/sc/modules/ISC_Forms.js  lineNumber: 2633  stack: ([object Object])@http://localhost:8080/SmartGWTTest/test/sc/modules/ISC_Forms.js:2633 ([object GWTJavaObject])@http://localhost:8080:417 connect("http://localhost:8080/SmartGWTTest/?gwt.codesvr=127.0.0.1:9997","<tA{>s3##Q-#`^xq","127.0.0.1:9997","test","2.0")@:0 ((void 0),"test","http://localhost:8080/SmartGWTTest/test/")@http://localhost:8080/SmartGWTTest/test/hosted.html?test:264 z()@http://localhost:8080/SmartGWTTest/test/test.nocache.js:2 (-6)@http://localhost:8080/SmartGWTTest/test/test.nocache.js:11
  	at com.google.gwt.dev.shell.BrowserChannelServer.invokeJavascript(BrowserChannelServer.java:195)
 	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.invokeNativeVoid(ModuleSpace.java:284)
 	at com.google.gwt.dev.shell.JavaScriptHost.invokeNativeVoid(JavaScriptHost.java:107)
 	at com.smartgwt.client.widgets.form.ValuesManager.addMember(ValuesManager.java)
 	at net.keydriving.smartgwt.client.TestStack.<init>(TestStack.java:37)
 	at net.keydriving.smartgwt.client.Test.onModuleLoad(Test.java:11)
 	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 	at java.lang.reflect.Method.invoke(Method.java:597)
 	at com.google.gwt.dev.shell.ModuleSpace.onLoad(ModuleSpace.java:369)
 	at com.google.gwt.dev.shell.OophmSessionHandler.loadModule(OophmSessionHandler.java:185)
 	at com.google.gwt.dev.shell.BrowserChannelServer.processConnection(BrowserChannelServer.java:380)
 	at com.google.gwt.dev.shell.BrowserChannelServer.run(BrowserChannelServer.java:222)
 	at java.lang.Thread.run(Thread.java:637)

If you then uncomment the rule which attaches a datasource to the values manager the test case works like expected. But i don’t need the datasource on the values manager in my real world case, each added form will have it’s own datasource to manage the data.

Test.java (528 Bytes)

TestStack.java (1.46 KB)

Test.gwt.xml (665 Bytes)

Hi,
On re-reading this thread, I realize this is actually working as designed. We don’t support having a ValuesManager managing multiple forms bound to different dataSources (or for that matter a ValuesManager whos dataSource doesn’t match a member form).

We’ll take a look at the docs and see if we can make this more obvious.
Sorry for the confusion.

Not sure what your final use-case is here but if you can’t see a way to achieve it, or if you are still seeing other bugs, please let us know and we’ll see if we can recommend a better approach

Thanks

This is related to another post of my concerning custom components. I’m getting closer to a working solution. But what i’m trying to do is the following:

I have a DataSource for Company related data. This is declared as Company.ds.xml and this metadata is used to generate a DynamicForm which lays out the fields in 2 nice columns.

In this for there are 2 fields related to Address information, the actual company address and a possible different facturation address. When adding these fields with as type attribute “Address” (which is also a DataSource in itself, declared as Address.ds.xml with a number of address related fields).

When the Company form is generated as a Dynamic Form it renders the form correctly, but the Address fields are layed out each below each other. This breaks the layout of the general form. I’ve been unable to get the Address in a specific layout by adding additional metadata in the Address and Company datasource declarations.

So my first idea was to create a custom address component. (See the thread located at: http://forums.smartclient.com/showthread.php?t=9868 ). I got the component working as intended.. But only by removing the type=“Address” from both address fields in the Company datasource. If i leave this attribute on them the layout is controlled by something in the framework (It generated a nestedEditor or something like that from what i see in the Developer Console).

So having control over the layout is done by removing the type attribute information in the Company Datasource. So now i have a form with a layout i want. But i now need to find a way to save the data manually. (Because the direct link to the Address datasource save was removed with the removal of the type attribute on the address fields in the Company datasource).

So i took a look at the ValuesManager documentation. I’m now at the point where i made the ValuesManager with the main form’s datasource as datasource (in this case the Company datasource). The address information is now stored in the nested forms with their own datasource attached to them.

I added all forms to the ValuesManager and i’m now in the progress of making the save (which i’ll trigger via a custom save method) to do all the necessary saving for me. This will be in a sequence of first saving the nested forms their information. And since i save this information myself, my main form only uses the identifiers of the address information to reference them. After saving the nested forms, i’ll update the identifier (if needed) on the main Company form and then perform the actual save.

A lot of workaround, but it seems to be going in the right direction. The main goal of all of this is to have a small framework on top of smartGWT which automatically generates forms based on the datasource metadata with a specific layout. Also related entities are displayed based on this metadata. since this needs to be written anyway. So why create each form myself when i have a bunch of metadata already available to do it all for me.

As a example i’ve attached a screenshot to show the difference in layout in custom component which layout a the address fields, and the layout which gets generated with the DataSource set as the type attribute for the fields.

You don’t need “workarounds”, you just need to understand the architecture better :slight_smile:

Whether the DynamicForm tries to automatically display complex subobjects (like an Address) is controlled by the showComplexFields flag. You can use setEditorType to provide your own sub-form if you don’t like the default rendering.

Then the question is the style of saving you want to do. This sample shows loading and saving sub-records with the parent record. In this case the sub-records are treated as an attribute of the parent record and never need independent IDs.

The alternative is to treat the records as independent but related (like database tables with foreignKey relationships). This sample covers this style, showing a master “order” record being saved in a single HTTP turnaround along with detail “orderItem” records that are related by foreignKey.

Thanks alot for the tip about showComplexFields, this now shows the Country field :-).

But the setEditorType thing… i’m still having a hard time grasping that. I tried it a bit before, only to remember being greeted by a bunch of javascript errors in the trend of ‘_4 is undefined’ if i remember correctly.

In attachment in include the datasource configuration as i currently have them (including my own custom attributes for the workaround). I cleaned up the address field (not the facturationAddress field).

Actual code to create the main form:

_form = new DynamicForm();
_form.setAutoFocus(Boolean.TRUE);
_form.setUseAllDataSourceFields(useAllDataSourceFields);
_form.setShowComplexFieldsRecursively(true);

// General layout stuff
_form.setWidth100();
_form.setNumCols(2);
_form.setColWidths("50%", "50%");
_form.setTitleOrientation(TitleOrientation.TOP); // Labels above the boxes

// Set the datasource for the form
_form.setDataSource(formType.getDataSourceId());

The company datasource is set on a dynamic form which has 2 columns and the rest of the form layout is controlled by the colSpan attributes included in the metadata of the Company. But for the PostalAddress we in fact need a layout which uses 6 columns for its layout.

So i should include a editorType on the address fields to gain control over the layout. My first question about this is the following: can this editorType be given in the ds.xml as metadata (passing the FQCN of the editortype)? Or is this only possible via actual code itself?

Thanks for the help already :slight_smile:

Company.ds.xml (3.22 KB)

PostalAddress.ds.xml (2.04 KB)

Hi dvankeer,

At the moment, setEditorType() works only for subclasses that set simply properties or add event handlers, it won’t work for method overrides (we’re working on this, but having to find a way around GWT not supporting Java reflection APIs).

What we’d recommend for now is creating a subclass of DynamicForm that inspects the DataSource in order to creating FormItems of your special subclass type.