Could you please explain how dynamic form value management for form items works ? I tried going through the documentation but could not find a clear explanation how this works.

I've implemented a custom CanvasItem based form item and I'm trying to get the CanvasItem behave correctly in my dynamic form. I managed to get the canvasitem delegate the disabled state correctly to my custom canvas by overriding the "public void setDisabled(Boolean disabled)" method on CanvasItem.

I'm having trouble getting the value from my (datasource bound) dynamic form to be delegated to my custom canvas though. I've tried overriding the "public Object getValue()" and "public void setValue(String value)" methods on my CanvasItem but it seems I'm not getting any calls to these methods at all. Could you please explain the pattern that should be used here to

a) get the value from dynamic form's datasource to be displayed on my custom canvas(item) and
b) deliver the edited value from my custom canvas(item) back to the datasource when the dynamic form submits ?

I have a custom canvas (currently a rich text editor) as the custom item's canvas. This canvas item is used in a DynamicForm which is bound to a datasource. I am sure the form itself works 100% because I have other fields (TextItems etc) in the form which work ok. When I use this TestCanvasItem with the form I never get any calls to getValue or setValue methods which I then could use to pass the values to myCustomCanvas. I don't really understand how your example MyItem could work in a DynamicForm which is bound to a datasource ? Any ideas ?

Now, when clicking the Submit button, the form never submits because CustomTextItem has been set as required and for some reason, SmartGWT always sees the CustomTextItem as empty. The interesting part is that the NormalTextItem works, but in NEITHER CASE DOES FormItem.getValue() or Form.getValue(fieldName) EVER GET CALLED. Nor do any of the other methods on the DynamicForm that I added debug statements too.

The only thing I can think of at this point is that the DynamicForm is doing some javascript magic under the hood that allows it to retrieve the entered values from the formitems which isn't exposed through the Java API. And that's basically the root of the problem with CanvasItem - you can override getValue() all you want, it never actually get invoked; so unless you manually invoke it and then add the value to the Record underlying the form, it will never get submitted. It also means that you cannot do validation against the CanvasItem because while you can "fake" adding the data to the Record in the way I just described, you cannot "fool" the validation - it will always fail on a CanvasItem, no matter how you implement validate() or getValue().

Someone from Isomorphic needs to take a look at this - I'm at the end of my rope.

Incidentally, while you're at it - can you explain to me the following inconsistencies in the API?

All this JUST to submit a multi-value property? In fact, if you look at the posts of the other people trying to use CanvasItem, most of them are doing this precise requirement: they're adding a ListGrid to a Form because they need to handle a multi-value property; something like a list of emails; for instance. That's a very common, basic requirement but the way SmartGWT wants you to handle this is by defining two different domain objects (one for your main record, one for the list of email addresses) and then use the foreign-key magic in the datasource to tie the two together. In other words; you now need to set up DataSource for every single multi-value property that you may have and, if you're using DMI, as I am, that's a HUGE amount of work.

I really feel there's times where SmartGWT is trying to be a little bit too smart, no pun intended. Some more basic functionality and more control over the atomic behavior would be better than all this magic-out-of-the-box stuff that never quite fits a real-world scenario and you can't actually use in a real application; i.e. something real, as opposed to idealized situations from the showcase.

If you're using the latest, you'll notice the advice in the docs for CanvasItem to call setValue() rather than anything you've attempted with getValue() et al overrides. It's true that int[] and String[] convenience methods should be added, however, note the setValue() variant that takes a JavaScript Object - you can use this for now via creating a JSArray of int or String.

Ok, so that works for submitting data; i.e. calling super.setValue(). I think it's terrible design, personally - you need to now add ChangedHandlers all over the place to ensure super.setValue() gets called (not to mention that ChangedHandlers and things like EditorValueFormatters don't work together). Frankly, override getValue() and actually have the form call getValue() on each FormItem during saveData() would be much better, but whatever: it works.

What about the other side of the binding equation? How do you actually populate a CanvasItem with data from a dataSource?

What I mean is this:

CanvasItem implementation (basically a CanvasItem wrapper around a TextItem):

When you now select a record in the listGrid, the textField has data, but the customTextField does not - again, because apparently, DynamicForm.editRecord(Record record) never actually calls setValue() on any of the FormItems that are part of the form, but instead injects values under the hood through some proprietary javascript solution. Correct?

So what you're telling me is that inside the SelectionChangeHandler of the ListGrid; I now need to put this, right?

Both are terrible solutions; they go against every principle of black-boxing and reusable design. In the first solution, the ListGrid needs to know about the specific structure of the Form and what fields it has. In the second solution, the form has custom logic that deals with a specific FormItem. Basically, you now need to implement all this extra logic to make the CanvasItem work OUTSIDE the Canvasitem implementation itself.

Central change handling (dynamicForm.addItemChangedHandler()) is among many reasons why items must signal the form of value changes rather than have the form call getValue().

FormItem.setValue() is called when the form is populated with values and an override here is one way for a CanvasItem to be notified of calls like dynamicForm.editRecord(). However, it's not set up as a SmartGWT override point yet. The reason it hasn't been prioritized is that the most common reason to use a CanvasItem is for a complex external editor dealing with singular or multiple subobjects, like this use case, where there is already a need for some special case code.

It's not clear why you've headed down this path. If you want a custom TextItem, you can subclass TextItem - is there a reason you want to use CanvasItem here?

As an aside, it would be nice if you could be a little less caustic with your remarks. You're throwing around phrases like "terrible design", but mostly, you have a few misconceptions about how the design actually works.

I apologize if my comments gave offense; it's nothing personal, that's just the frustration talking.

The CustomTextItem was a hugely simplified example to try to figure out what's going on. Of course, this isn't something I would actually use.

I think perhaps I should show you the actual screens and widgets I'm working on, and I can try and show you why I think the current relationship between DynamicForm and FormItems isn't a good design - it's certainly caused me no end of headaches.

Here is a record detail screen from my application:

http://yfrog.com/05didorderdetailp

It contains several custom implementations of CanvasItem; I've numbered them in the graphic. All of them are fully functional at this point; but I'm not entirely happy with how I had to implement the underlying logic

1: NumberIncrementItem: this is a pretty simple CanvasItem implementation based on a textfield that ensures that the data entered is numeric. A plus and minus button allow you to increment or decrease the number that was entered.

2: EmailSelector: Basically a ListGrid that takes an array of Strings, where each String is supposed to be an email address. Adding a non-email address generates a validation error

3: DIDSelector: This is, by far, the most complex of the CanvasItems I've built. In fact, the actual implementation you're seeing here is what I call a ComplexDIDSelector. It allows users to add DIDs (a DID is basically a phone extension) to an order.

However, DIDSelectors come in different flavors. Here is an other example:

http://yfrog.com/jvdidtransferp

These are two implementations of the basic DIDSelector. It allows users to make a subselection of DIDs from the master DIDOrder and add them to the DIDs for a specific child-carrier order. The buttons in the middle are a "TransferStack", a configurable set of buttons that moves records back and forth between two listgrids.

Now, the ComplexDIDSelector I showed you in the first screen is actually an extension of the basic DIDSelector. It maintains the basic functionality; but adds a toolbar with two buttons (again, configurable) to the two. Click those buttons opens two sliding windows (with a nice little animation effect) which allow users two different ways of adding DIDs to the list. Either by searching from the pool of existing DIDs, like so:

http://yfrog.com/6bdidsearchp

Or by adding new DIDs to the pool and then automatically adding those DIDs to the order you were working on, like so:

http://yfrog.com/j7didwizardp

Now, why am I having problems with the CanvasItem value management for this widget?

First of all, DIDs can actually be added to a DIDSelector in a number of different ways, as you can see from the graphic:

1) They can be added through a wizard like interface which allows you create whole new ranges of DIDs and add them to the listgrid all at once.
2) They can be dragged and dropped, either from the did pool in a different window or from another did selector on the same screen.
3) They can be transfered by clicking a button on a two different types of TransferStacks (OneWayTransferStack and TwoWayTransferStack)
4) They can be added by double-clicking a record; either in the DID pool or in a different DID selector on the same screen.

They can also be removed through a number of different ways:
1) They can be deleted by clicking the delete graphic in the ListGrid
2) They can be dragged out again
3) They can be transferred out by clicking a button on one of the transferstacks

Now, if I really wanted to make the DIDSelector work the way the SmartGWT design demands it; I would have to add changeHandlers for all those different data-entry methods and each time ensure that CanvasItem.super.setValue() gets called, or, alternatively, DynamicForm.steValue(). Also, because neither FormItem nor DynamicForm right now does not take a int or String arrays as an argument; I'd have to construct and deconstruct JSArrays each time to wrap the ids of the DIDsin the ListGrid.

The problem with that approach is that there are a LOT of different components that can add data to the ListGrid. I've already determine that I can't just add handlers to the listgrid itself because not every form of data entry is detected by the listgrid. So I'd have to add it to the widgets that initiate the data-entry in the first place (the transferstack buttons, for instance). The problem THERE is that not all of them have a reference to the CanvasItem OR to the Form and it's not practical to throw those references around.

I check whether a record was passed to the form (these typically come wrapped in an event from the eventBus, because the actiion to open a form can come from a totally different part of the application). If this is the case, I tell the form to edit the record, which populates all the standard formItem fields. I then MANUALLY get data out of the record and populate the custom CanvasItem implementations.

Not graceful, but it works. My beef here is that you can't just stick a custom canvasItem on a form, because of it's own accord, the CanvasItem does not work. Instead, the Form needs specialized logic to make it work; as opposed to the standard FormItems (TextItem, SelectItem, etc) which you just sick on the form and they work, period. That's what I mean with the design violating the principle of encapsulation.

Quite frankly, I'm not happy with this code at all. It's ugly and it's messy. First of all, I get a copy of the record backing the form (through a call to DynamicForm.getValuesAsRecord()). I'd rather have worked with the record directly, but there's no method exposed to allow you to get that.

I then manually extract data from the custom CanvasItems and add them to the record.

Next, I need to set the DIDSelector to some bogus value because apparently, the validation logic of the DynamicForm checks whatever the backing value is behind CanvasItem.getValue() - BUT WITHOUT EVER CALLING DIDSelector.getValue() or DIDSelector.validate(). It doesn't look at the Record behind the form -which is what you'd expect - it looks somehow inside the super class of your custom CanvasItem at whatever hidden variable stores the data.

Next, I need to manually validate the form and the emailselector. Again, because I have no control whatso-ever over the validation logic that happens inside a DynamicForm; I have no way of telling the form that the EmailSelector is part of that it needs to check the ListGrid inside the EmailSelector for validation errors. So I have to do that manually.

Finally, I tell the form to re-edit the copy of the record I just obtained and modified. Unfortunately, calls to DynamicForm.editRecord() automatically switch the Form from DSOperationType.ADD to DSOperationType.UPDATE, so I have to undo that and switch it back (after checking whether the ID for the item has been set; this tells me whether or not I'm dealing with a new or an existing record.

Now, imagine how much easier this would be if the DynamicForm, on saveData() called FormItem.getValue() and FormItem.getValidate() on each of it's fields. All you would have to do would be override those two methods on your custom CanvasItem implemenation and you'd be good to go. No messing with Handlers and no hacking the validation and data-binding logic.

I understand the point you made about the centralized value management benefits DynamicForm provides - for instance, this way, you would know if a user modified the form at all and you get set a "not-saved indicator". But there's other ways of doing this, namely:

That's a LOT less code and lot more straighforward than the amount of code it takes to get a custom CanvasItem() to submit data. Not to mention that I may not always care about whether a form has been modified or not but I will certainly care about the values in the FormItems each and every time.

You know, you may be right - I probably had some misconceptions around how the design worked. There's certainly parts of the SmartGWT that I still don't get. But let's be fair - it's not like there's a lot of documentation and what does exist leaves a lot unsaid. I mean, here's the javadoc for CanvasItem():

I doesn't mention anything about the need to manage the value of the super-class. Nor does the Javadoc on DynamicForm or FormItem mention that in fact, the value management of data by forms is done top-down by the form to its form-items, rather than bottom-up by the formItems to their form.

Any logical person is simply going to look at the API doc for FormItem and assume they have to override getValue() and validate() - which is precisely what a lot of the threads on CanvasItem on this forum show other users as doing; and then discovering that that doesn't actually work.

SmartGWT is no doubt very very powerful. Don't get me wrong, you guys have built some very powerful and attractive widgets. But the documentation leaves a lot to be desired and the development process is painstackingly slow with huge amounts of trial and error.

For instance, when I built the NumberIncrementItem, I - naively - assumed that a TextItem could have BOTH an EditorValueFormatter AND a ChangedHandler. But apparently, it can't, because once you add an EditorValueFormatter to a TextItem, none of it's ChangeHandlers or ChangedHandlers() ever get notified. That is again not mentioned in the documentation, you simply have to discover it the hard way. The end-result is that what should have been a really simple widget to built (I mean, a textfield with two buttons to increment/decrement a number by 1 is about as basic as it gets) but between that issue and the general "how-the-hell-do-I-get-my-custom-canvas-item-to-actually-show-data-from-the-datasource) it took a day and a half.

When I started this smartGWT project, our initial estimate was 8 weeks. It's now been 4.5 months and counting, most of which has been learning all the undocumented and undescribed in-and-outs of the inner workings of SmartGWT.

I'm not saying this to be negative or to attack SmartGWT. I'm trying to provide feedback as a paying customer that my experience with SmartGWT has been painful, but rewarding in the end - but most importantly - far, far too time-consuming; time that most organizations cannot afford. From my perspective, SmartGWTs API either needs to be a lot of self-evident than it is OR the documentation needs to be a lot more comprehensive and explain some of these inner complexities. And I'm pretty sure that I'm not the first person to point that out.