|
GettingStarted
A short introduction to the library.
Featured IntroductionThis is a short introduction to the API, essentially providing a simple 'Hello World'-style application. DetailsThe Model-View-Presenter pattern consists of three parts. This API only implements some basic piping for the Presenter part - it doesn't dictate any particular Model or View implementation. However, it does have some simple support for standard GWT widgets, which this example will use. ImportingWe will need to add the jar file to the project's library files, and then add the following into the application's *.gwt.xml file: <inherits name='net.customware.gwt.presenter.Presenter' /> The PresenterThe first class off the rack is the HelloWorldPresenter. Since we're working with GWT widgets, we'll use the WidgetPresenter for convenience, but you could base it directly on Presenter or BasePresenter, depending on what you need. public class HelloWorldPresenter extends WidgetPresenter<HelloWorldPresenter.Display> {
public interface Display extends WidgetDisplay {
public HasValue<String> getName();
public HasClickHandlers getGo();
}
public static final Place PLACE = new Place("HelloWorld");
public HelloWorldPresenter( Display display, EventBus eventBus ) {
super( display, eventBus );
}
@Override
protected void onBind() {
// 'display' is a final global field containing the
// Display passed into the constructor.
display.getGo().addClickHandler( new ClickHandler() {
public void onClick( ClickEvent event ) {
Window.alert( "Hello, " + display.getName().getValue() + "!" );
}
} );
}
@Override
protected void onUnbind() {
// Add unbind functionality here for more complex presenters.
}
public void refreshDisplay() {
// This is called when the presenter should pull the latest data
// from the server, etc. In this case, there is nothing to do.
}
public void revealDisplay() {
// Nothing to do. This is more useful in UI which may be buried
// in a tab bar, tree, etc.
}
/**
* Returning a place will allow this presenter to automatically trigger
* when '#HelloWorld' is passed into the browser URL.
*/
@Override
public Place getPlace() {
return PLACE;
}
@Override
protected void onPlaceRequest( PlaceRequest request ) {
// Grab the 'name' from the request and put it into the 'name' field.
// This allows a tag of '#HelloWorld;name=Foo' to populate the name field.
String name = request.getParameter( "name", null );
if ( name != null )
display.getName().setValue( name );
}
}The ViewIn this case, the view is a GWT Widget. The main thing is that it has to implement HelloWorldPresenter.Display. public class HelloWorldPanel extends Composite implements HelloWorldPresenter.Display {
private final TextBox name;
private final Button go;
public HelloWorldPanel() {
FlowPanel panel = new FlowPanel();
initWidget( panel );
panel.add( new Label( "What is your name?" ) );
name = new TextBox();
name.setText( "World" );
panel.add( name );
go = new Button( "Go" );
panel.add( go );
}
public HasValue getName() {
return name;
}
public HasClickHandlers getGo() {
return go;
}
/**
* Returns this widget as the {@link WidgetDisplay#asWidget()} value.
*/
public Widget asWidget() {
return this;
}
public void startProcessing() {
// Do nothing for the moment.
}
public void stopProcessing() {
// Do nothing for the moment.
}
}Putting it all togetherPretty simple, really. Now, we just have to hook it all up. In your EntryPoint, put something like this: public class HelloWorldEntryPoint implements EntryPoint {
public void onModuleLoad() {
// Build the default event bus
EventBus eventBus = new DefaultEventBus();
// Build the display and presenter
HelloWorldPanel display = new HelloWorldPanel();
HelloWorldPresenter presenter = new HelloWorldPresenter( display, eventBus );
// Bind the presenter to the display.
presenter.bind();
RootPanel.get().add( presenter.getDisplay().asWidget() );
// Trigger any passed-in history tokens.
PlaceManager placeManager = new PlaceManager( eventBus );
placeManager.fireCurrentPlace();
}
}GIN/GuiceThis also works great with Google GIN, which is a Google Guice-like API for Direct Injection (DI) in GWT. There is a basic AbstractGinModule subclass called AbstractPresenterModule which provides some helper methods for binding Presentation/Display classes easily. There is still some improvement to be made, such as automatically registering the PlaceManager and DefaultEventBus, which for the moment you will have to configure yourself. |
Is there something missing ? I don't see how the "name" parameter is stored into the History via addItem. Or I'm not understanding it right ?
I used GWT 1.7. I'm not sure if this caused any problems, but I had to change your example to get it to compile:
In HelloWorldPresenter?.java
to
In HelloWorldPanel?.java
to
Add two methods.
In Apache Labs, the Hupa project (GWT based Webmail for IMAP-Servers), has implemented this framework.
http://svn.apache.org/repos/asf/labs/hupa/
I still need to try out a few more things to get my head around all the details.
I'm currently implementing this for one of our internal systems. Still on the learning curve :)
Nice work.
The gwt-mvp-sample project has implemented this in a branch in svn. Is a good example of how to use this.
Im in the same boat as norman.maurer. Can we get some Javadocs up so that we can at least see more details about what we are trying to use?
Now, on the client-side, we first need to import the library into our .gwt.xml file. Just add this:
<inherits name='net.customware.gwt.presenter.Presenter' />
There is another sample application on: http://code.google.com/p/gwt-mvp-sample/source/browse/#svn/branches/gwt-presenter
Get the src code with: svn checkout http://gwt-mvp-sample.googlecode.com/svn/branches/ gwt-mvp-sample-branch
This is great. Just want to add my vote for a more complete example.
A complete getting started tutorial
http://blog.hivedevelopment.co.uk/2009/08/google-web-toolkit-gwt-mvp-example.html
Great work, thank you !
What about remove start/stopProcessing methods from Display and put them in some other interface that extends Display we should call ProcessingDisplay? for instance. DisplayCallback? would have to be modified accordingly.
Hi all, I've been lax in checking the comments on this page. I need to figure out how to get comment notifications somehow...RSS feed perhaps? I would suggest posting questions to the Google Group if you want faster responses. Anyway, to answer the questions...
@norman.maurer: The BasicPresenter parent class will automatically add a PlaceRequestHandler into the EventBus for any subclasses that return a Place from getPlace(). It will use the ID for that place (in this case "HelloWorld") to decide whether to trigger the onPlaceRequest method of the Presenter. PlaceManager, at the other end, will listen for History requests and will also listen for PlaceChangedEvents, updating the History with the new value.
@forbes.al: Thanks for the fixes - I've updated the examples. Thanks also for the sample app links and the tutorial link.
@olivierhedin: Separating that out (and the getPlace() method) is probably doable, although it means there will be more 'instanceof' checking in various places. Maybe create a new issue with details of the suggestion.
Why do you define Display interface in the presenter itself? in other words why is the display interface nested? Wouldn't it be better if it were not a nested interface?
For example sth like:
interface MyWidgetPresenter? extends BasePresenter?<T extends BaseDisplay?> { .... }
interface Display extends BaseDisplay?<?> { ... }
In my opinion nested interfaces are not that intuitive as interfaces that are not nested.
@mark: Mostly it's because that's how Ray defined it in his presentation, so that's where it was to begin with, and I haven't moved it since...
I think that putting the Display interface nested in the Presenter makes it clear that the Presenter is the one that mandates the UI to support this interface. It does have the negative effect that you can not have 2 different Presenter implementations - but I guess that in most cases you don't need to. When unit testing (and that is one of the reasons for the pattern) you don't have the need to mock the Presenter with something else, it's the Display implementation that you want to mock.
I must say that the words are a bit confusing. We are always talking about a Model View Presenter pattern... yet the Presenter class is actually the View part and Presenter becomes the Display. so it becomes a MPD pattern! Just a little confusing.
Yeah, the terminology does get a little muddy at times. I see it as follows:
Can this be used with SmartGWT?
@sujaydutta: I'm not familiar with SmartGWT, but I'm guessing it uses a different 'widget' hierarchy than comes with GWT? If so, it's still possible, you'll just need to create something similar to the WidgetPresenter? and WidgetDisplay? for use with SmartGWT classes. The API itself is not tied to any particular widget implementation.
Funny, posted javadoc describes BasicPresenter?<D extents Display> class, but I can't find BasePresenter? class.
BasicPresenter? still exists. What version are you using?
What is the best practice to create parametrized presenters/views using Gin and your framework? For example ContactDetails? presenter depends from Contact entity and i can't create it using Provider.get() method from parent presenter.
@AssistedInject? looks what i need, but it doesn't work with Gin. I see two ways - to use factory which seems to be overkill and to set entity via setter after creation (not via constructor). But both ways looks not elegant.
@aksonov: looking at the gin commit messages, assisted injection should work by now
I'm trying gwt-presenter for the first time, trying to see how I'll integrated within my project, first question about the presenter class implemented here :
why PLACE is public static then it's also returned throw the getPlace() methode ?
The reason was so that you could create hyperlinks to the location without needing the Presenter instance. However, the 'replace' branch, which will soon become the main trunk, has a new Place API, which does not require Presenter to know about it at all. Instead, you create and register places directly with the PlaceManager? upon construction.
I have a problem with the PlaceManager? implementation. PlaceManager? class implements PlaceRequestHandler?, but it doesn't listen for PlaceRequestEvents? from the EventBus?. Is this a bug? How is PlaceManager?.onPlaceRequest method called?
Any updates on the replace branch becoming the TRUNK ?
I would really like to find out more about the updates to PlaceManager?...Will there be changes to the API anytime soon?
yes,I fund 1.1.1 out in http://borglin.net/gwt-project/?page_id=383,but this place have not any change,why?
I am also interested on gwt-presenter-1.1.1-replace-SNAPSHOT. Any updates on when will it be available through the maven repository?
After GWT 2.1 now has its own "Place", this framework could be adapted.
Yeah, looks interesting, but I couldn't find any good documentation on the new MVP stuff in GWT 2.1. Any links?
Where can i find tutorials of GWT "Place"?
class and am receiving the following error.
'net.customware.gwt.presenter.client.place.PlaceManager?' must be a class [
Does anyone know the cause of this?
i use: gwt-dispatch-1.1.0.jar gwt-presenter-1.1.1.jar
many thanks
This sample code doesn't seem to compile with gwt-presenter-1.1.1. I get errors on all of the Place instantiations (Place is an abstract class, apparently), and getPlace and onPlaceRequest don't seem to be overrides. Are these changes from 1.0.0?
Also, make sure you copy your NEW gwt-servlet.jar to the War/webinf/lib folder. It throws out a bullshit security warning otherwise (missing Serilization Exception). Wow, Im getting this rush of memories, reminding of how much I love(sarcasm) Java.
@Juppie7 : Given that you've expressed your desire to share the new code, if you can provide a link to a short replacement article I would be more than happy to update this page.
Many thanks,
Robert
The above tutorial is broken for gwt-presenter 1.1.1
Compile error: Cannot instantiate the type Place
Any fixes?
bad...i had to download 1.0.0 again after 1.1.1 just because sample cant be complied correctly.
in 1.1.1 use DefaultPlaceManager? to create your own version.