Re: Implementing Composite MVP Components in Javascript



adam.ratcliffe@xxxxxxxxx wrote:

Hi,

I'm building an Address Book application using Javascript/DHTML and am
interested in using the Model View Presenter pattern to enable resuse
of the model classes and to facilitate testing.


Greetings,

It's pretty easy to keep the model classes devoid of any display and
input handling stuff. That alone is a good and worthy achievement. A
great first base to get to.

Before looking at the pattern I had already implemented much of the
application using components that merged view creation and application
logic so I'm actually trying to decompose the existing code into MVP
triads.

You may need more than a triad, for instance MVP also has an
"Interactor".

Interactor is just another name for input handler, controller or
listener and it sits in between the Presenter and View. You may need
them in order to keep the views relatively dumb and the presenters
slim.

In many modern environments input handlers work best if they're inside
the view, for instance Swing Listeners. This is still allowed in MVP
you don't have to remove everything from the views.

I started out looking at the Java example provided by Martin
Fowler in his description of the pattern but this uses an elemental MVP
component only whereas I think I probably need composite components.


Yes I agree and MVP can certainly handle complex components.

If anyone thinks they might be able to help with this a description of
my existing application and the questions it's provoked follow:


It's hard without seeing the code but I'll give it a shot.

In my current object model I have an AddressCardViewer class that uses
DOM methods to create a toolbar and a scrollable viewing pane that
address cards will be displayed within. The AddressCardViewer is
provided with a list of addresses (data objects) as a constructor
parameter. For each address an AddressCard is constructed and laid out
in the scoll panel. The AddressCard class uses DOM methods to create
the cards visual representation and binds event handlers to it for
switching the card into edit mode - where its field values can be
changed.

It looks fairly straightforward to decompose the AddressCardViewer
class into an MVP triad but I've become a little confused in working
out how I should handle the composition of the AddressCards.
Specifically:

- In moving the application logic out of the AddressCard class into a
presenter should there be a one-to-one relationship between an
AddressCard and its presenter or can I have one presenter to handle
actions for all cards, passing a reference to the card when calling the
presenter's methods?

A Presenter's single responsibility is to "tie everything else
together" but ultimately, as its name implies, to make a
"presentation". It does this by managing everything else. It's job is
to employ all the right players (model, view...) and then to ensure
they have what they need to get their job done.

In essence the presenter is literally running the show but not by
itself.

To clarify, when application logic is removed from all the views it
doesn't mean it all ends up in *one* big place, for instance in a big
presenter. A presenter's job is not to become the single repository of
all application logic.

To encapsulate all the application logic in one big place is a step
beyond MVP. Whenever I've tried that, the ApplicationLogic class became
way too big and messy. I was in fact just trying to put an entire
application into one class! It's better to have smaller pieces each
with a single responsibility.

Yes the Presenter is the closest component to the user and the real
world so it's a decent place for higher level stuff but it should not
get too carried away and try to do it all ;-)

Some things that the presenter would need to do
are handle card selection, change the cards appearance when clicked;
re-position the card when it is to be edited.


Fair enough and of course to avoid presenter bloat it can employ those
down the line to do the actual input handling.

- Would the AddressCardViewer hold a reference to both the AddressCard
view object and its presenter after it has created them?


AddressCardViewer will certainly need a reference to the model
(collection of AddressCards) but the view shouldn't have a reference to
the presenter.

The presenter has full access to the model and views so it can pull
whatever strings from on high but the views shouldn't manipulate the
presenters.

- The AddressCard actions, handling card selection and re-positioning
the card require knowledge of the viewer's state. For instance if a
card is already selected, when selecting a new card the previous card
should be de-selected. How should the AddressCard presenter
communicate with the AddressCardViewer presenter? Should it delegate
the action to the viewer's presenter?


There's no hard and fast rule and with MVP you end up with many
options.

Yes MVP presenters, unlike MVC, do have the option to directly call
another presenter. Or you can still do it the good old MVC "Observer"
pattern way.

Another cool variation is for each Presenter and not the Model to be
Observable, the observers would be all the other presenters. It's easy
for the triggering presenter to know when the model changes it can then
just call observers.notifyAll.

Mind you when I only have one or two presenters, especially in web
apps, I don't bother to make anything observable. The browsers
themselves trigger the model (re)read.

And as I said before you can still just keep certain listeners inside
the views. Especially things like "isButtonClicked".

- The AddressCardViewer has a list of addresses as its model, when
creating an AddressCard view instance it is initialized from an address
data object. Should I the view be registered as a subscriber to the
data object at this point?

I'm not sure I fully understand that question but I think the answer is
yes ;-)

Cheers
Adam

I'm sorry this ended up being so long, I tried very hard to trim it
down but hopefully it helps.

MVP is harder to describe than to code, implementing it is somehow
natural.

Cheers.

.


Loading