Mvp4g and GXT

1,730 views
Skip to first unread message

plcoirier

unread,
Sep 14, 2009, 4:57:58 PM9/14/09
to Mvp4g
I started to implement the Employee Admin example with GXT to
demonstrate how to use GXT with Mvp4g. You can checkout the example
here: http://code.google.com/p/mvp4g/source/browse/#svn/trunk/v1.0/examples/EmployeeAdminWithGXT.

There are two different ways of using GXT in a MVP architecture:
-Solution 1: use GXT only in the view implementation. View interface
and presenter use only basic GWT interfaces and extra classes are
created to wrap GXT widgets with these interfaces.
The bottom left frame of the example is implemented with this
solution.
-Solution 2: use GXT directly in the view interface and presenter.
The bottom right frame of the example is implemented with this
solution.


I'm not sure which one is the best solution because they both have
advantages and also annoying drawbacks.

Solution 1
================
Advantages:
-You can easily switch widget libraries
-You can easily test presenters with JUnit and Mock libraries

Drawbacks:
-You have to create a lot of wrapping classes.
-You lose GXT logic by using GWT events instead of GXT ones. Hard work
might be needed in order GXT widgets to answer correctly to GWT
events.

Solution 2:
================
Advantages:
-No extra class needed, it's easier to code.
-You keep GXT logic

Drawbacks:
-You can't test presenters with JUnit or Mock libraries since GXT
widgets can only be tested with GWTTestCase (http://www.extjs.com/
forum/showthread.php?t=63053).
-Switching widget library has a big impact on your code


I guess in a perfect world GXT widgets would implement GWT
interfaces...(http://www.extjs.com/forum/showthread.php?
t=76968&highlight=mock)

Alex

unread,
Sep 14, 2009, 7:40:53 PM9/14/09
to Mvp4g
Great explanations, especially those ones about advantages and
drawbacks!

In one of your comments on FAQ thread in the MVP4G blog section,
you've already mentioned those two ways of implementing GXT
components. I know that you suggested implementation of GWT interfaces
for GXT components (when it's necessary) and first I had tried to use
it that way, but in the end I gave up and chose second approach.
Hm, why?

First reason is GWT interface implementation on GXT components - it
could make things even more complicated for some exotic functions.
For example, I have made class TextField4MVP which extends
TextField<String> and implements HasValue<String> interface. This is
the one of the easier views to implement this way, but even in this
case I've spent some time on addValueChangeHandler function
implementation, until ensured myself that's implement correctly. I can
only guess what could happen with some more demanding views.
Second reason is simplicity of the second approach - all I need is to
declare GXT views as getters' return parameters of the interfaces and
define them in the views and it will work without any additional
changes. It's a simple solution and easy to make it works with no
problems.
Also, on my project are working several guys with different levels of
GWT and GXT knowledge. In the last two weeks I've seen some mess in
using GWT buttons, GXT buttons, different panels, etc. until I finally
set this project to work with this framework and made some "rules"
based upon the second approach. After those changes, and defining in
interfaces themselves that Button could only be GXT Button, this
stronger typing help me to bring the project into more ordered state.

You have really beautifully explained the main advantages and
disadvantages of the both approach and all I can do is put my sign on
every your word. I also agree that basically all GXT components should
met appropriate GWT widgets, but my experience has also shown that in
some circumstances GWT interfaces are just not rich enough to support
all facilities given by GXT components.

The similar problems I had when I was creating views based upon the
GXT panels (VerticalPanel for example). Those views implement
Composite interface by GWT, and when I tried to use Composite by GXT I
got the error: parent doesn't implement HasWidgets interface, or
something like that. But, when I was using Composite by GWT I had the
problem that my View has been losing some characteristics that I
defined it for its basic panel (for example SetScroll to Auto for the
mentioned VerticalPanel object).
I found two solutions to this problem:
1.) Instantiate the additional VerticalPanel (VP) and add him the view
based on the same VerticalPanel (VPView). On VP I performed all
necessary adjustments that I basically wanted to perform only in the
VPView constructor. Of course, that means I had double panel defining
and that's what I want to avoid, for sure.
2.) Implementation of HasWidgets interface in the views with GXT
panels and getPanel() function which returns to me directly already
initialized GXT panel that I can inject as my view where it's needed.
Of course, I would like to avoid something like this, but in my cases
that was necessary since I couldn't find another (and easy) way to
resolve it. However, still I think that is pretty clean solution,
since all necessary works on the view are done in its constructor and
functions or interacting with its presenter - panel is returned to
another view which only task is to display him and it works fine for
now.

P.S. My project has about hundred views and appropriate presenters and
it works really stable and quick even with some experimentation's like
this one mentioned above. :) I'm very satisfied with choosing MVP4G as
framework and hopeful that my feedback could contribute to its further
development.

Thanks for your great work!

Alex

Ravi Varanasi

unread,
Oct 13, 2009, 3:17:49 PM10/13/09
to Mvp4g
First of all, thanks for this thread. I was looking for information
for the exact question you have addressed. However, I am still unclear
about the approach.
Using option 1 you lose GXT logic (especially for more complex ones).
Can you elaborate on how much we would need to do this ourselves ?
Also, does this mean that taking option 2, you went down the path of
using GWTTestCase for unit testing ?
Ravi

plcoirier

unread,
Oct 13, 2009, 6:07:44 PM10/13/09
to Mvp4g
If you want to keep GXT logic with solution 1, you need to:
- find a way for each widget to translate each GXT events with basic
GWT events (ie basic DOM events). For example, you can translate a GXT
Change FieldEvent by a GWT ValueChange event. However in a lot cases,
it will be more complex (like GXT Valid FieldEvent).
- implement GWT handler in order them to act on widgets so that they
react as they received a GXT event

As Alex said, it seems really complicated even for simple widgets as
TextField.

Maybe now there are other solutions as the ones I described before.
You could define your view interfaces without using GWT interfaces.
For example instead of having this:

public interface MyButtonInterface extends HasClickHandlers {

}

you could have:

public interface MyButtonInterface {

public void addListener(EventType eventType, Listener<? extends
BaseEvent> listener);

}

Instead of having widgets that have to implement GWT methods
addClickHandler, addValueChangeHandler...they will just have to
implement the GXT addListener method (and all GXT widgets do).

This solution allows you to keep GXT logic and let you test your
presenters with JUnit (the main drawback is that you can't switch
widgets library easily, but this may not be the most important point).

I haven't tried this solution but it may be a good approach, what do
you think?
> > Alex- Hide quoted text -
>
> - Show quoted text -

plcoirier

unread,
Oct 14, 2009, 11:36:20 AM10/14/09
to Mvp4g
So I implemented the top frame of the example with GXT using the third
(last) solution I talked about. I also added tests to verify that
solution 1 & 3 could be easily tested with JUnit.

I must say that this last solution seems to be pretty convenient as
you have the advantages of solution 1 (you can easily test your
presenters) without the main drawback (having to recreate GXT logic
with GWT events and handlers).

I changed the svn structure of the project so you can now find the
example here (http://code.google.com/p/mvp4g/source/browse/#svn/trunk/
examples/EmployeeAdminWithGXT).

Here is a summary of the different solutions.

Solution 1 (bottom left frame, UserProfilePresenter.java)
================
Description:
use GXT only in the view implementation. View interface
and presenter use only basic GWT interfaces and extra classes are
created to wrap GXT widgets with these interfaces.

Advantages:
-You can easily switch widget libraries
-You can easily test presenters with JUnit and Mock libraries

Drawbacks:
-You have to create a lot of wrapping classes.
-You lose GXT logic by using GWT events instead of GXT ones. Hard
work
might be needed in order GXT widgets to answer correctly to GWT
events.


Solution 2 (bottom right frame, UserRolePresenter.java):
================
Description:
use GXT directly in the view interface and presenter.

Advantages:
-No extra class needed, it's easier to code.
-You keep GXT logic


Drawbacks:
-You can't test presenters with JUnit or Mock libraries since GXT
widgets can only be tested with GWTTestCase (http://www.extjs.com/
forum/showthread.php?t=63053).
-Switching widget library has a big impact on your code


Solution 3 (top frame, UserListPresenter.java):
================
Description:
use GXT widget only in the view implementation but use GXT events
directly in presenters and view interfaces (so in presenters and view
interfaces, instead of using addClickHandler, addValueChangeHandler...
methods, GXT addListener method is used).
Extra classes are still needed to wrap GXT widgets.

Advantages:
-You can easily test presenters with JUnit and Mock libraries
-You keep GXT logic
-Wrapping classes are easy to code

Drawbacks:
-You have to create a lot of wrapping classes.
-Switching widget library has an impact on your code


After having tested the three solutions, the last one seems to me the
best because it let you easily decouple your presenter from the view
in order to test it with JUnit and Mock libraries.
> > - Show quoted text -- Hide quoted text -

Toine

unread,
Oct 15, 2009, 2:58:04 AM10/15/09
to Mvp4g
Pierre

How do i implement the BeanModelReader

via a example it is

public class MenuItem {
private String caption;
}

public class Group extends MenuItem implements BeanModelTag {
private List<MenuItem> childs;
}

public class Item extends MenuItem implements BeanModelTag {
private String function;
}

// in in the panel where the tree is displayed
MemoryProxy<List<BaseMenuItem>> proxy = new
MemoryProxy<List<BaseMenuItem>>(getRootMenuItem());

BeanModelReader<ModelData> reader = new
BeanModelReader<ModelData>();
BeanModelReaderAdapter<ModelData> adapter = new
BeanModelReaderAdapter<ModelData>(reader);

TreeLoader loader = new BaseTreeLoader(proxy, adapter) {
public boolean hasChildren(ModelData parent) {
Object subItems = parent.get("children");
return (subItems != null && ((java.util.Collection)
subItems).size() > 0);
}
};

where do i load dataproxy for instance.
a lot of the GXT functions use a proxy ..
Do i need some other proxys in the presenter or do mis something

Because i don't want to create all the beans ( with a BeanModel )
rather using create getters and setters..

GXT example >>> http://www.extjs.com/blog/2008/07/14/preview-java-bean-support-with-ext-gwt/

Probably i miss something.


Can you please advice where to put what ...

Thx

plcoirier

unread,
Oct 16, 2009, 4:16:25 PM10/16/09
to Mvp4g
Ok so I added a pager to the users list in the GXT example to test GXT
proxy, BeanModelReader/BeanFactory & Loader.

/* Presenter */
public class UserListPresenter extends
Presenter<UserListViewInterface> {

//private static BeanModelFactory factory = BeanModelLookup.get
().getFactory( UserBean.class );

protected int indexSelected = -1;

private UserServiceAsync service = null;

private PagingLoader<PagingLoadResult<ModelData>> loader = null;
private ListStore<BeanModel> store = null;

@SuppressWarnings( "unchecked" )
@Override
public void bind() {

RpcProxy<PagingLoadResult<UserBean>> proxy = new
RpcProxy<PagingLoadResult<UserBean>>() {

public void load( Object loadConfig,
AsyncCallback<PagingLoadResult<UserBean>> callback ) {
service.getUsers( (PagingLoadConfig)loadConfig, callback );
}
};

// loader
loader = new BasePagingLoader<PagingLoadResult<ModelData>>( proxy,
new BeanModelReader() );
store = new ListStore<BeanModel>( loader );
view.getToolBar().bind( loader );

view.buildWidget( store );
...

}

public void onStart() {
eventBus.dispatch( EventsEnum.CHANGE_TOP_WIDGET, view.getViewWidget
() );
loader.load(0, 4);
...

}
}

/* View */
public class UserListView extends ContentPanel implements
UserListViewInterface, MyWidgetInterface {

private MySimpleGXTButton delete = new MySimpleGXTButton( "Delete" );
private MySimpleGXTButton newButton = new MySimpleGXTButton( "New" );
private MySimpleGXTButton yes = new MySimpleGXTButton( "Yes" );
private MySimpleGXTButton no = new MySimpleGXTButton( "No" );
private MyLabel confirmText = new MyLabel( "Are you Sure?" );

private MyGXTTable userList = null;
private MyGXTPagingToolBar toolBar = new MyGXTPagingToolBar( 4 );

public UserListView() {

setBodyBorder( false );
setHeading( "Users" );
setButtonAlign( HorizontalAlignment.CENTER );

HorizontalPanel buttons = new HorizontalPanel();
buttons.setSpacing( 4 );
buttons.add( delete );
buttons.add( newButton );
buttons.add( confirmText );
buttons.add( yes );
buttons.add( no );

add( buttons );

setBottomComponent( toolBar );

}

public void buildWidget( ListStore<BeanModel> store ) {
List<ColumnConfig> configs = new ArrayList<ColumnConfig>();

configs.add( new ColumnConfig( "username", "Username", 150 ) );
configs.add( new ColumnConfig( "firstName", "First Name", 150 ) );
configs.add( new ColumnConfig( "lastName", "Last Name", 150 ) );
configs.add( new ColumnConfig( "email", "Email", 150 ) );
configs.add( new ColumnConfig( "department", "Department", 150 ) );

ColumnModel cm = new ColumnModel( configs );

userList = new MyGXTTable( store, cm );
userList.setBorders( true );
userList.setAutoHeight( true );
userList.setStripeRows( true );

add( userList );

}

...
}

I added the proxy to the presenter because proxy needs to have access
to services and only the presenter must know the services.
I also added to the presenter:
-the loader because this class is part of the business, not of the
display
-the ListStore because I think it will be easier to have the same
model for the presenter and GXT widgets (even if in the MVP model only
the presenter is supposed to have access to it).
The problem I had is that I had to delay the construction of the grid
(MyGXTTable extends GXT Grid) because to build the grid, I needed to
build the proxy and the loader first. That's why I had to create the
buildWidget method.

I commited the changes under
http://code.google.com/p/mvp4g/source/browse/#svn/trunk/examples/EmployeeAdminWithGXT

The one thing I don't like with this solution is that I can't test it
directly with JUnit because
Loader calls BeanModelReader that calls GWT.create(-) at one point.
I'm gonna try to see if I can resolve the problem using
GWTMockUtilities.
> GXT example >>>http://www.extjs.com/blog/2008/07/14/preview-java-bean-support-with-e...
> ...
>
> read more »- Hide quoted text -

Toine

unread,
Oct 16, 2009, 5:38:30 PM10/16/09
to Mvp4g
Pierre

Thx for the answer.
I will work on with it
> I commited the changes underhttp://code.google.com/p/mvp4g/source/browse/#svn/trunk/examples/Empl...
> ...
>
> meer lezen »

plcoirier

unread,
Oct 17, 2009, 4:57:50 PM10/17/09
to Mvp4g
Here is the line why JUnit tests doesn't work right away: instance =
GWT.create(BeanModelLookup.class);

I looked at GWTMockUtilities and I've been able to test the presenter
with JUnit test. I had to modify the GWTMockUtilities to be able to
build my own BeanModelLookup class. I found valuable information on
how to use GWTMockUtilities here: http://www.assertinteresting.com/2009/05/unit-testing-gwt/

Here are the changes I made:

//Test Class
public class UserListPresenterTest implements Constants {

...

@Before
public void setUp() {
GWTMockUtilities.disarm();
...
}

@After
public void tearUp(){
GWTMockUtilities.restore();
}

...
}


//My GWT Mock Utilities
public class GWTMockUtilities {

public static void disarm() {
GWTBridge bridge = new GXTBridge();
//Inject my own bridge to be able to
create my own BeanModelLookup
setGwtBridge( bridge );
}
...
}


//My GWT Bridge
public class GXTBridge extends GWTBridge {

@Override
public <T> T create( Class<?> classLiteral ) {
//if we try to create an instance of BeanModelLookup,
//return an instance of my own BeanModelLookup
//otherwise return null
T obj = null;
if ( BeanModelLookup.class.equals( classLiteral ) ) {
obj = (T)new BeanModelLookupTest();
}

return obj;
}
...
}

//My BeanModelLookup for test
public class BeanModelLookupTest extends BeanModelLookup {

@Override
public BeanModelFactory getFactory( Class<?> bean ) {
//just return a Test factory
return new BeanModelFactoryTest();
}

}

//My Factory for test
public class BeanModelFactoryTest extends BeanModelFactory {

//Have to create a subclass to have access to the constructor
public class BeanModelTest extends BeanModel{

}

@Override
protected BeanModel newInstance() {
//Just create a simple instance of BeanModel
//No need for specific Bean Model since they
//won't be used by GXT widgets
return new BeanModelTest();
}

}

(all the changes are avalaible here:
http://code.google.com/p/mvp4g/source/browse/#svn/trunk/examples/EmployeeAdminWithGXT)


Thanks to these changes, I can test the Bean introspection features
developed by GXT (BeanModelReader, BeanModelFactory...) directly with
JUnit.

Pierre

jonnyo

unread,
Nov 29, 2009, 7:26:11 PM11/29/09
to Mvp4g
On the GXT Employees sample, there are 2 types of Buttons.

- public class MyGXTButton extends Button implements MyButtonInterface

- public class MySimpleGXTButton extends Button implements
MyGXTButtonInterface

** MyButtonInterface **
public interface MyButtonInterface extends HasClickHandlers {

public void setEnabled( boolean enabled );

public void setVisible( boolean isVisible );

public void setText(String text);
}

** MyGXTButtonInterface **
public interface MyGXTButtonInterface {

public void addListener( EventType eventType, Listener<? extends
BaseEvent> listener );

public void setEnabled( boolean enabled );

public void setVisible( boolean isVisible );

public void setText( String text );
}



Could you explain why did you create 2 of them? Just as an example
that you can go both ways on these buttons? Using the Listener, or
using GWT ClickHandlers, they both work?

Thanks
Jonnyo

Pierre

unread,
Nov 29, 2009, 8:07:15 PM11/29/09
to Mvp4g
Hi Jonnyo,

I tested with this example different approaches of GXT and MVP. Here
is a sum up of my tests:
in order to test it with JUnit and Mock libraries.


This is why you have a MyButtonInterface (solution 1) and
MyGXTButtonInterface (solution 3). I would suggest you to go with
solution 3.

-Pierre

Arnaud

unread,
May 26, 2010, 11:20:02 PM5/26/10
to plcoirier, mv...@googlegroups.com
I tried to run JUnit test of your GXT sample project (http://
code.google.com/p/mvp4g/source/browse/#svn/trunk/examples/
EmployeeAdminWithGXT) with the last version of GXT (2.1.1) but it's
not working anymore because of these change in BeanModelLookup class:
Before (2.0.1):
public static BeanModelLookup get() {
if (instance == null) {
instance = GWT.create(BeanModelLookup.class);
}
return instance;
}

After (2.1.1):
public static BeanModelLookup get() {
if (instance == null) {
if (GWT.isClient()) {
instance = GWT.create(BeanModelLookup.class);
}
}
return instance;
}

Those GXT guys prefer to return null than let GWT a chance to create
an instance...
So obviously injection of your GXTBridge has no chance to help!

Do you know another solution which can work?

I don't think there is something to do to workaround this, but I
prefer post here before reporting the bug on GXT forum because I think
it will not be easy to show them my point of view.

Thanks,
Arnaud.

> (all the changes are avalaible here:http://code.google.com/p/mvp4g/source/browse/#svn/trunk/examples/Empl...)

Pierre

unread,
May 28, 2010, 8:01:47 AM5/28/10
to Mvp4g
I haven't tried to update the example yet so I didn't look at this
issue. If you find a solution for this, could you let me know?

Thanks,
Pierre
> > Pierre- Hide quoted text -

Arnaud

unread,
Jun 3, 2010, 5:52:42 AM6/3/10
to Mvp4g
In fact the solution is quite simple we just have to return true in
GXTBridge.isClient()!

Unit tests are working fine again, I don't know if side effects can
happen.

BTW, because GWT.setBridge() method has default visibility (in GWT
2.0.3 anyway) I can suggest you to move GWTMockUtilities class in the
same package than GWT (com.google.gwt.core.client) and directly call
setBridge method. It's also a hack but I find this a little better
than using reflection.

Arnaud.

Pierre

unread,
Jun 7, 2010, 6:22:22 PM6/7/10
to Mvp4g
Thanks for the feedback, I will bring this modification when I update
the EmployeeAdmin example.
> > > - Show quoted text -- Hide quoted text -
Reply all
Reply to author
Forward
0 new messages