alt83 / mvp4g

Automatically exported from code.google.com/p/mvp4g
0 stars 0 forks source link

Fix mvp4g to work UiBinder #17

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
gwt 2.0 comes with UiBinder

Currently mvp conflicts with UiBinder when UiBinder xml file includes other 
controls also defined in UiBinder

For example:

Main Mail application view consists from Inbox Panel, Navigation Panel and 
Top Command Panel. If all views are defined in UiBinder xml files, and also 
defined as views in mvp configuration, both mechanism will instantiate each 
view class.

Original issue reported on code.google.com by alexei....@gmail.com on 29 Nov 2009 at 1:49

GoogleCodeExporter commented 9 years ago
Alexei, can you please explain in more details and provide examples of the 
issue? We didn't notice yet problems 
with UiBinder.

Original comment by alexande...@gmail.com on 2 Dec 2009 at 2:39

GoogleCodeExporter commented 9 years ago
The problem begins when view/widget defined in UiBinder xml format contains 
another 
views/widgets which are also defined in UiBinder xml format. In this case 
UiBinder code 
will instantiate all contained views by itself and mvp4g will also instantiate 
another 
instances of the same views, thus every contained view will have two instances 
and only 
one of them will be connected to presenter (and it will be one that is not 
displayed)

Original comment by alexei....@gmail.com on 3 Dec 2009 at 9:37

GoogleCodeExporter commented 9 years ago
Could you attach the project where you have this error so that I can easily 
reproduce it? Thanks.

Original comment by plcoir...@gmail.com on 3 Dec 2009 at 5:02

GoogleCodeExporter commented 9 years ago
Probably I have the same problem with UiBinder and nested widgets.
Please take a look at the attached project.
Thanks

Original comment by ceppelli on 13 Jan 2010 at 7:10

Attachments:

GoogleCodeExporter commented 9 years ago
Thanks, I will look at your project and see where the problem comes from.

Original comment by plcoir...@gmail.com on 15 Jan 2010 at 12:42

GoogleCodeExporter commented 9 years ago
I imported your project into eclipse and it worked fine. What problem do you 
have?

Original comment by plcoir...@gmail.com on 16 Jan 2010 at 4:04

GoogleCodeExporter commented 9 years ago
The application contains 2 widgets:

1. MyViewUiBinder.ui.xml 
2. MyLabelUiBinder.ui.xml

The MyViewUiBinder.ui.xml instantiates the MyLabelUiBinder.ui.xml.

I wrote some line in the code which print on the standard output some 
information during the startup of the 
application.

[MyLabelUiBinder] MyLabelUiBinder() 0
[MyViewUiBinder] MyViewUiBinder()
[MyViewUiBinder] createView()
[MyLabelUiBinder] MyLabelUiBinder() 1
[MyViewPresenter] onInit()
[MyLabelUiBinder] createView() 0
[MyLabelPresenter] onInit()

As you could see the "MyLabelUiBinder()" is instantiated 2 times ( this is 
wrong). In the MyLabelPresenter.java 
the onInit() and onUpdateLabel(String) event wont be applied to the displayed 
MyLabelUiBinder.ui.xml widget 
but to the second one (probably) which is instantiated by the mvp4j framework.

I hope my description was clear.

Thank in advance

Original comment by ceppelli on 18 Jan 2010 at 2:26

GoogleCodeExporter commented 9 years ago
Thanks for your description. I now understand the problem.

However I'm not sure if this is a bug. My interpretation of MVP + event bus 
pattern 
is that each component should be independant. You can remove any of them, it 
won't 
impact the rest of the application. I think that having a view of a presenter 
defined by the view of another presenter (which is the case here) doesn't 
respect 
this rule. If you ever want to remove label presenter, you need to also modify 
MyViewUiBinder.ui.xml and remove the label.

I would follow a different strategy. I would remove label binder from view 
binder 
and create a new event changeWidget. This event would be thrown by 
LabelPresenter 
when application starts to tell the application to set its view. This event 
would be 
handle by ViewPresenter that, when triggered for this event, will add the 
widget. 
You would then have no link between the two views.

You will find attach the project you sent implementing this strategy.

What do you think?

Original comment by plcoir...@gmail.com on 19 Jan 2010 at 12:21

Attachments:

GoogleCodeExporter commented 9 years ago
Thanks you for you answer.

I see you point and also I took a look at your code, I'm not really an expert 
in pattern and architectures, so when I'm reading you explanation, I only could 
agree with you 
that this is not a bug.

The new features UiBinder  introduced in GWT 2.0 is really great from my point 
of view, it is like the mxml in the Adobe Flex framework. Now it really easy to 
create a 
complex visual component, deal with the css and the most important thing it is 
possible in an easy way  to make a composition of the widgets already created 
without 
writing new code (just xml).

With your approach the creation of the widgets is always driven by the mvp4g 
framework (this is fine) but the composition of it in the application is 
hardcoded in the 
code:

@Event(handlers={MyViewPresenter.class})
    public void onChangeWidget(Widget w){
        view.getPanel().add(w);
    }

What do you think? Maybe It would be possible instantiate the presenter in the 
constructor of the UiBinder widget and register it the EventBus? (As I said I'm 
not an 
expert, maybe I make a mistake)

At the moment I'm dealing with a medium GWT application I wrote. After some 
mouths is not more possible to make new development (From the beginning I 
didn't 
spend to much time in architecture designing, MY FAULT). Now I'm thinking to 
rewrite it from scratch and I'm making some experiments.

What I see until now about mvp4g was great (It will resolve my "spaghetti code 
issue" and will improve the effectiveness of the unit test). But I still have 
some problem 
how to use it in some situations, maybe you have some suggestions.

1. As explained before the composition of widgets (especially the new UiBinder 
version). 
2. The application should show/use multiple widgets of the same kind, but the 
amount of them is not know at the beginning, which approach should I use in 
order to 
create new presenter/view pair on demand?

Thanks

Original comment by ceppelli on 19 Jan 2010 at 3:13

GoogleCodeExporter commented 9 years ago
I can see why you would like to do this if you're used to flex. I guess it may 
be 
possible to integrate this to Mvp4g thanks to @UiField(provided=true). Maybe 
for a 
future version?

Just to make sure we talk about the same thing. For me a view is made of 
widgets, 
and each view is managed by one presenter.

So Mvp4g let you manage composition of widgets inside a view.

Now for composition of views inside a view, on this point, I do follow a 
different 
approach, more like "no link, only event". If a presenter has a view that can 
contains other views, it just tells the rest of the application about it 
(thanks to 
events) without knowing the other views. This way your application can really 
be 
dynamic.

In case you have views inside a view, for example, if you have:

<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder>
    <g:FlowPanel>
        <v:MyMenu></v:MyMenu>
        <v:MyMainPage></v:MyMainPage>
    </g:FlowPanel>
</ui:UiBinder>
How would you change your content of your main page? What would you do if you 
decide 
to make your menu dynamic (eg: you need to load a different menu according to 
your 
user rights)?

In the case the menu never changes, you can also add a menu widget to MainView 
and 
have only one presenter to manage these two elements.

Concerning your second question, you can look at this discusion that talks 
about the 
same problem and let me know what you think about this solution:
http://groups.google.com/group/mvp4g/browse_thread/thread/50ff74971d8c0006/3dad9
c44c9
3f1485?lnk=gst&q=portlet#3dad9c44c93f1485

Thanks

Original comment by plcoir...@gmail.com on 19 Jan 2010 at 11:59

GoogleCodeExporter commented 9 years ago
"I can see why you would like to do this if you're used to flex. I guess it may 
be 
possible to integrate this to Mvp4g thanks to @UiField(provided=true). Maybe 
for a 
future version?"

Just adding my vote to this - I spent much time trying to use the 
@UiField(provided=true) to integrate nested UIBinder modules and MVP but was 
unsuccessful.  If anyone has any success, or if we can get these working nicely 
please post your example here.  This functionality, imho, makes or breaks this 
design 
pattern as I cannot code GWT without MVP, and I cannot code GWT without 
breaking my 
views up into separate, nested UIBinder modules.

Thanks.

Original comment by NirmalPS...@gmail.com on 6 Feb 2010 at 4:12

GoogleCodeExporter commented 9 years ago
I just transformed the GWT Mail example using Mvp4g. You can look at it by 
downloading the last example of the 1.1.0 example. It gives you an example of 
how to 
use UiBinder with MVP. For now, it's not possible to directly inject view 
inside 
other views, you need to do it throught events.

But, after having transformed the GWT Mail example, I realized how practical it 
would be to inject view inside other views when using UiBinder. I will work on 
it 
for the 1.2.0 release.

Original comment by plcoir...@gmail.com on 6 Feb 2010 at 2:46

GoogleCodeExporter commented 9 years ago
GIN has been integrated with Mvp4g so you can use GIN injection features to 
inject 
view inside other views.

You can inject a view directly into another view this way: 
-declare the injected view as a singleton 
-insert this view to your other view thanks to GIN @Inject 
=> this way the same instance will be injected into your view and inside the 
presenter (you don't need to use events) 

//Injected view 
@Singleton 
public class MailListView extends ResizeComposite implements 
MailListPresenter.IMailListView {...} 

//Other View 
public class MailView extends Composite implements IMailView { 

        interface Binder extends UiBinder<DockLayoutPanel, MailView> { 
        } 

        private static final Binder binder = GWT.create( Binder.class ); 

        @UiField( provided = true ) 
        MailListView mailList; 

        @Inject 
        public MailView( MailDetailView mailDetail ) { 
                this.mailList = mailList; 
                initWidget( binder.createAndBindUi( this ) ); 
        } 

However you need to make sure that the presenter that manages the injected view 
binds its view before (or right after) it is injected into the other view, 
otherwise 
this view will do nothing.

You can download the last examples of the 1.2.0 snapshot and look at 
MailWithMvp4g 
to see a working example.

Original comment by plcoir...@gmail.com on 25 Apr 2010 at 10:45

GoogleCodeExporter commented 9 years ago
Just another thought on this, what would you do if you want to inject two 
different
instances of that view?

I believe the option here is to use Gin's @Named annotation in conjuntion with 
the
above, so that you name your singletons... Can't think of a better way... Maybe 
that
helps someone. Maybe there is a better way?

Original comment by christop...@gmail.com on 18 May 2010 at 9:41

GoogleCodeExporter commented 9 years ago
Attached is an example where you have the same menu at the bottom and at the 
top of 
the screen. Both of these menus are displayed using GIN injection.

The workaround to be able to have 2 menus is to have 2 presenter classes and 2 
view 
classes. 

You would have something like that:
@Presenter(view=MainMenuView.class)
public class MainMenuPresenter extends 
BasePresenter<MainMenuPresenter.IMenuView, 
MainEventBus> {
//here you define all your code
...
}

@Presenter( view = MainBottomMenuView.class )
public class MainBottomMenuPresenter extends MainMenuPresenter {
//this class is empty, it's only use to define a second instance of the menu
}

public class MainPageView extends Composite implements IMainPageView {

        @Inject
    public MainPageView( MainMenuView topMenu, MainBottomMenuView bottomMenu ) {

Original comment by plcoir...@gmail.com on 19 May 2010 at 2:36

Attachments:

GoogleCodeExporter commented 9 years ago
plcoirier,

This Gin injection in the view is perfect! But doesn't seem to work when using 
multi-modules. I want to inject presenters from sub-modules in my root view. 
Imagine in the MailView injecting MailDetailView and ShortcutsView from a 
child-module (extends Mvp4gModule). If I try as you mention, I get a 
compilation error on my main view not having an empty constructor.

Then if I try to annotate the handlers for the start event with the presenters 
from the sub-module it complains that it is a different event-bus.

Do you know of a way to do this?

Original comment by javier.a...@gmail.com on 24 Aug 2010 at 7:39

GoogleCodeExporter commented 9 years ago
Each module has its own Ginjector so you can't inject a view from one module to 
another one.

The only way to inject a view from another module is to use events. You could 
have 2 events setMailDetailView & setShortcutsView that the child module can 
forward to the parent module with the needed views. This is the strategy 
followed in the Mvp4gModule example.

Original comment by plcoir...@gmail.com on 25 Aug 2010 at 10:17