Howdy,
> I have followed your advice and your suggestions work. So thanks a lot
> for your feedback, which was very useful.
Glad it helped.
> After looking at your solution, it seems to me that in the "UI
> Commands & Buttons" example of
>
>
http://scratchpad.pietschy.com/pectin/PectinDemo.html
>
> you could not easily add validation because again, the
> AbstractDynamicList for the cheeses does not support "out-of-the-box"
> the validate() and confirm() methods for its text boxes. It is the
> same problem as in our Contract example. Correct?
It has a problem but it's not quite that one in this case... In the
demo the validate/commit methods aren't needed since the values are
simple strings and can be validated in the FormModel (using a
ListValidator). The demo's problem is the messages generated
validator won't be automatically displayed by the widgets. To
overcome this in other projects I've made my AbstractDyamicList
implement ListValidationDisplay and had it delegate to to each item
editor that in turn implements ValidationDisplay, then the bindings
automatically wire them up.
The validate/commit problem arises when the list item requires a
compound editor (since putting something like a ListValidator<Address>
on the main FormModel would become unweildy.. i.e. it would be
difficult/ugly to associate a "please enter a postcode" message in the
FormModel with a particular widget in the AddressEditor component).
> So it seems that even for "simple" forms we need some notion of
> "nested" widgets. In your approach so far, views (TextBox'es,
> CheckBox'es, etc.) just have values and don't know about models.
> However, the view AbstractDynamicList suddenly needs to know about the
> models of its subwidgets...
Yep, there's a definite a break once you need to start nesting. I
think in there a 4 different cases to consider:
1. Non-compound values that map directly to simple widgets
2. Lists of non-compound values that map to a list of simple
widgets
3. Compound values bound to a compound editor
4. List of compound values bound to a list of complex editors.
Of these 1 is the current standard pectin usage, 2 is what
AbstractDynamicList was originally built to solve (but needs
improvement). 3 & 4 aren't as straight forward. In the past I'd
approached 3 from the perspective of using reusable Widgets and
models, i.e. an AddressEditor and AddressModel being used something
like addressEditor.setModel(personModel.addressModel) although it's
not as clean as I'd like from a binding perspective. 4 is more
difficult again since the layout and presentation of the "list of
editors" is almost always going to be so application specific.
> Is there an easy fix for this? Or do you intend to go a completely
> different path like the Cocoa-style controllers you mentioned, which
> is MVC IIUC?
I don't have an absolute opinion, but I think:
1. Nested controllers are the most flexible and generalised
approach.
2. An improved AbstractDynamicList that has out of the box support
for validation in case 2 above would be worth while.
3. There's probably some gray area between the basic approaches of 1
& 2. i.e. nested/specialised widgets vs full blown controllers.
As far as I can see if we try and avoid controllers then we're heading
down the one-model-to-rule-them-all path, and given the generally
demanding and varied requirements of UIs I can't see this working at
all. Using controllers lets you layer the design and break it into
managable pieces. I.e. form models deal with the details of how the
low level widgets behave, the controller works at the next layer up to
mediate between the various bits and to handle higher level things
like validate/commit cycles etc. It can expose state appropriate for
the controllers role such as hasUnsavedChanges, selection etc. A
master controller can then make decisions based on the state of it's
child controllers, models and components etc. For example I have a
contoller in my current app that has a child SelectionController<T>
and EditorController<T>. The SelectionController exposes an
InterceptedValueModel<T> for the selection. The master controller
intercepts selection changes and if the EditorController is dirty
prompts to save/cancel otherwise it allows the selection to proceed
and the EditorController is updated via a binding. e.g.
// During initialisaiton we:
// 1. add an interceptor for selection changes.
selectionController.selection().interceptUsing(unsavedChangesInterceptor);
// 2. bind the selection to our editor controller
Binder.bind(selectionController.selection()).to(editorController));
The editorController can split T into various Value/ListModels as
required. It could bind controllers to these models or have it's own
FormModel or both. There's still a fair bit to think of in the above
example though, i.e. the item update event (after saving) would need
to propegate to both the selection controller and the
editorController. I'm halfway through cleaning this up in my current
app and will likely go down the path where the master controller owns
the selection ListModel<T> & selection ValueModel<T> and passes them
to the controllers as requried.
I'm also reluctant to use the term MVC since now days there are so
many different variations to choose from that it has become somewhat
meaningless unless you explain exactly how you're implementing the
various bits, i.e. I have models, views and controllers, my models are
almost always observable, my controllers typically have commands,
models (either FormModels or simple Value/ListModels) and possibly
other controllers. The controllers mediate between the various bits,
commands (often) operate on the models (or interfaces models then
implement), contollers (often) intercept commands. Commands usually
model their own state (enabled, text etc). I aim to have passive
views (which are generally passed the the models and commands and
simply bind the models and commands to widgets). So from that
perspective I'm not really sure how it fits into popular understanding
of the MVC concept. It's certainly a different implementation from
the MVC I learnt from Swing, but as I see it this is because the
problem/context is different; I'm building an app, and the Swing
engineers were building a widget toolset.
Anyway, I've prattled on for ages... hope at least some of that helped
you decide what approach might work for you.
Cheers
Andrew