GWT MVP architecture advantages - gwt

I am learning GWT and i have read at multiple places that using MVP architecture is best suitable to develop a GWT Application
I have also read that its easy to do testing using the MVP ARCH.Can somebody explain me why its easy to do testing using the MVP architecture.
Also i am working on a project using MVP and i find it very tedious to make the view connect to the data base.I mean i have to update my presenter,service,serviceAsync,servicImpl,Facades in order to make connection to database.
So can somebody provide me the essence of MVP for GWT?i would appreciate a couple of examples.

Separation between the presenter (which contains logic) and view (a dumb wrapper around UI controls) allows you to:
write unit tests for the presenters that can run without needing the corresponding environment (desktop, browser, GWT widgets)
reuse front-end logic without being tied to a particular set of widgets/UI framework
The latter use case is rare, so let's focus on the MVP model's suitability for automated, programmatic testing. With a team of developers this often takes the form of a continuous build/test cycle using Hudson (or similar) on a headless server, where it's not practical to open a web browser, create controls, etc. every time a test is run.
Typical usage of MVP+GWT is that views implement an interface provided by the presenter, and often this interface is defined in terms of other generic interfaces. Here's a very simple presenter that increments a numeric label when a button is clicked - note that instead of exposing the TextBox and Button directly, the view returns generic HasText and HasClickHandlers instances:
public class ButtonClickPresenter {
public interface View {
HasText currentValue();
HasClickHandlers incrementButton();
}
private final View myView;
private int currentValue = 0;
public ButtonClickPresenter(View myView) {
this.myView = myView;
this.myView.currentValue().setText("0");
this.bind(); // for the sake of demonstration
}
public void bind() {
this.myView.incrementButton.addClickHandler(
#Override
new ClickHandler() {
void onClick(ClickEvent event) {
currentValue ++;
myView.currentValue().setText(
Integer.toString(currentValue));
}
});
}
}
The "real" view returns UI widgets (created via UiBinder in this example):
public class ButtonClickView implements ButtonClickPresenter.View {
// ... skipped UiBinder initialisation ...
#UiField Label currentValueLabel;
#UiField Button incrementButton;
#Override
public HasText currentValue() {
return currentValueLabel;
}
#Override
public HasClickHandlers incrementButton() {
return incrementButton;
}
// ... etc ...
}
whereas unit tests create a dummy implementation (or use Mockito, EasyMock, etc.) and thus don't require any UI components:
public class ButtonClickPresenterTest {
ButtonClickPresenter presenter;
ClickHandler currentHandler;
String currentText;
#Before
public void setUp() {
presenter = new ButtonClickPresenter(
// dummy view - just stores label text in a String and
// keeps track of the Presenter's click handler
new ButtonClickPresenter.View() {
#Override
public HasText currentValue() {
return new HasText() {
#Override public String getText() { return currentText; }
#Override public void setText(String text) { currentText = text; }
};
}
#Override
public HasClickHandlers incrementButton() {
return new HasClickHandlers() {
#Override
public HandlerRegistration addClickHandler(ClickHandler handler) {
currentHandler = handler;
}
};
}
});
}
#Test
public void testIncrement() {
// initial value
assertEquals("0", currentText);
// clicking the button should increment the number
currentHandler.onClick(null);
assertEquals("1", currentText);
}
}
As for your next paragraph: your views shouldn't be connecting to the database at all! The presenter should request data via Service/ServiceAsync (or an abstraction such as gwt-dispatch or gwt-platform), then call methods on the view to populate the UI.
By the way, those last two links (along with gwt-presenter) are a good start if you're looking for GWT MVP code samples - combined with Google GIN they provide frameworks for tying all this stuff together.
Having said all that, I agree - the combination of GWT+MVP+Java can be hard work and extremely verbose (I'm glad I don't have to work with it much these days). The alternative, though, is even less attractive: an untestable, unmaintainable ball of spaghetti...

You might like to have a look at the sample gwt-platform application blogged about here-> http://uptick.com.au/content/serendipity-working-gwt-platform-and-smartgwt.
Cheers
Mark

Related

how to smooth scroll listview like ios in android?

I am use data from web using XML parser and setting data using custom adapter for this use asyncTask .
My problem is that some devices like Samsang duos,gallaxy work perfectly but on micromax devices it will not work properly.
My adapter is
public class HallBookingAdapter extends ArrayAdapter<MyHall> {
private Context context;
private ArrayList<MCCIAHall> halls;
private int resource;
MyHall objHall;
public int count;
View view;
public static Boolean isScrollingHalls=true;
LayoutInflater inflater;
static class HallBookingHolder
{
public TextView txtTitle,txtLocation,txtCapacity,txtCapacityTitle;
public ImageView imgHall;
public LinearLayout hallBookingLayout;
}
public HallBookingAdapter(Context context, int resource, ArrayList<MyHall> halls) {
super(context, resource, halls);
this.context=context;
this.halls=halls;
this.resource=resource;
inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
count=halls.size();
return halls.size();
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
view=convertView;
objHall=halls.get(position);
HallBookingHolder holder=new HallBookingHolder();
if (convertView==null) {
view = inflater.inflate(resource, null);
holder.txtTitle=(TextView)view.findViewById(R.id.txtListHallTitle);
holder.txtLocation=(TextView)view.findViewById(R.id.txtListHallLocation);
holder.txtCapacity=(TextView)view.findViewById(R.id.txtListHallCapacity);
holder.txtCapacityTitle=(TextView)view.findViewById(R.id.txtListHallCapacityHeadding);
holder.imgHall=(ImageView)view.findViewById(R.id.imgListHall);
view.setTag(holder);
}
else
{
holder = (HallBookingHolder)convertView.getTag();
}
//Creating the Font to the text
Typeface tfLight = Typeface.createFromAsset(context.getAssets(),"OpenSans-Light.ttf");
Typeface tfRegular = Typeface.createFromAsset(context.getAssets(),"OpenSans-Regular.ttf");
Typeface tfsemiBold = Typeface.createFromAsset(context.getAssets(),"OpenSans-Semibold.ttf");
//Setting the font
holder.txtTitle.setTypeface(tfRegular);
holder.txtLocation.setTypeface(tfLight);
holder.txtCapacity.setTypeface(tfsemiBold);
holder.txtCapacityTitle.setTypeface(tfLight);
//Setting data to textview and image
holder.txtTitle.setText(objHall.hallName);
holder.txtLocation.setText(objHall.location);
holder.txtCapacity.setText(objHall.capacity);
//Using Guild Library Image Load using image web url
String imgurl=objHall.getImageUrl();
Glide.load(imgurl).centerCrop().into(holder.imgHall);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent=new Intent(context, HallDetailsActivity.class);
intent.putExtra("position", position);
context.startActivity(intent);
}
});
return view;
}
}
read it listview smooth-scrolling
Using a background thread ("worker thread") removes strain from the
main thread so it can focus on drawing the UI. In many cases, using
AsyncTask provides a simple way to perform your work outside the main
thread. AsyncTask automatically queues up all the execute() requests
and performs them serially. This behavior is global to a particular
process and means you don’t need to worry about creating your own
thread pool.
check this too http://www.itworld.com/development/380008/how-make-smooth-scrolling-listviews-android
Put all your font styles in the if (convertView==null) {} block to set them only once. Now you are setting them every time a new row is created.
Here is a list of quick tips to help you.
Reduce the number of conditions used in the getView of your adapter.
Check and reduce the number of garbage collection warnings that you get in the logs
If you're loading images while scrolling, get rid of them.
Set scrollingCache and animateCache to false (more on this later)
Simplify the hierarchy of the list view row layout
Use the view holder pattern
Here is a link to help you implement these tips. Link

create new instance of class that is managed by gin

Im using GWTP, working on their tab panel example. My issue is i need to demonstrate taking a search term and adding a new tab to the tab panel with the search results. So if i search 5 times, i have 5 tabs. easy enough, so i thought.
Gin is used extensively in GWTP. So my method to add a new tab, which should be something as simple as
tabPanel.addTab(new SearchDataGridView(), NameTokens.searchPage + 1);
gets confusing because of the constructor for the SearchDataGridView class
#Inject
SearchDataGridView(Binder uiBinder) {
employeeDataProvider = new ListDataProvider<Employee>();
initSearchGrid();
initWidget(uiBinder.createAndBindUi(this));
}
yea, i know im not passing the search term yet, im still trying to get the tab to open.
My gin config is this
bindPresenter(
SearchDataGridPresenter.class,
SearchDataGridPresenter.MyView.class,
SearchDataGridView.class,
SearchDataGridPresenter.MyProxy.class);
The gwtp gin config
#Override
protected void configure() {
// RestDispatchAsyncModule.Builder dispatchBuilder = new RestDispatchAsyncModule.Builder();
// install(dispatchBuilder.build());
// install(new RestDispatchAsyncModule.Builder().build());
install(new DefaultModule(DefaultPlaceManager.class));
install(new ApplicationModule());
bind(CurrentUser.class).in(Singleton.class);
bind(IsAdminGatekeeper.class).in(Singleton.class);
// DefaultPlaceManager Constants
bindConstant().annotatedWith(DefaultPlace.class).to(NameTokens.homeNewsPage);
bindConstant().annotatedWith(ErrorPlace.class).to(NameTokens.homeNewsPage);
bindConstant().annotatedWith(UnauthorizedPlace.class).to(NameTokens.homeNewsPage);
bindConstant().annotatedWith(Names.named("rest")).to("http://localhost/services");
// Google Analytics
// bindConstant().annotatedWith(GaAccount.class).to("UA-8319339-6");
// Load and inject CSS resources
bind(ResourceLoader.class).asEagerSingleton();
}
How do i pull this off?
thanks
Using comments below, sorta got it working. The problem is i can't get the contents of the tab to display. I added debugging code to the setInSlot method of my SearchContainer class and realized that whenever i click the search tab, it fires this setInSlot method, but its fired with my default page presenter listed as content.
#Override
public void setInSlot(Object slot, IsWidget content) {
Window.alert("fired setInSlot: " + slot.toString());
Window.alert("fired setInSlot: " + content.toString());
if (slot == ApplicationPresenter.TYPE_SetTabContent) {
tabPanel.setPanelContent(content);
} else {
super.setInSlot(slot, content);
}
}
Thats the method im using to get my info. Its weird that the tab appears properly, the jsonRPC calls that are built into the view are executed properly, it just doesn't display.
My main container presenter has its view and proxy identified by this
public class ApplicationPresenter
extends
TabContainerPresenter<ApplicationPresenter.MyView, ApplicationPresenter.MyProxy> implements
CurrentUserChangedHandler, AsyncCallStartHandler, AsyncCallFailHandler, AsyncCallSucceedHandler,
ApplicationUiHandler {
/**
* {#link ApplicationPresenter}'s proxy.
*/
#ProxyStandard
public interface MyProxy extends Proxy<ApplicationPresenter> {
}
/**
* {#link ApplicationPresenter}'s view.
*/
public interface MyView extends TabView, HasUiHandlers<ApplicationUiHandler> {
void refreshTabs();
void setTopMessage(String string);
}
Could my issue be with my content type? Here is what i have defined for all my types
/**
* This will be the event sent to our "unknown" child presenters, in order for them to register their tabs.
*/
#RequestTabs
public static final Type<RequestTabsHandler> TYPE_RequestTabs = new Type<RequestTabsHandler>();
/**
* Fired by child proxie's when their tab content is changed.
*/
#ChangeTab
public static final Type<ChangeTabHandler> TYPE_ChangeTab = new Type<ChangeTabHandler>();
/**
* Use this in leaf presenters, inside their {#link #revealInParent} method.
*/
#ContentSlot
public static final Type<RevealContentHandler<?>> TYPE_SetTabContent = new Type<RevealContentHandler<?>>();
The proper way to do this is to use a PresenterWidget and a Provider.
In your ClientModule you define this:
bindPresenterWidget(SearchDataGridPresenter.class, SearchDataGridPresenter.MyView.class, SearchDataGridView.class);
In your Presenter that adds the SearchDataGridView you inject a Provider:
#Inject
public SeachContainerPresenter(final Provider<SearchDataGridPresenter> seachDataGridProvider) {
}
For each search you call addToSlot(TYPE_SetSearchDataGridContent,seachDataGridProvider.get()) in your SearchContainerPresenter.
In the SearchContainerView you override the addToSlot() method:
#Override
public void addToSlot(Object slot,Widget content) {
if (slot == SeachContainerPresenter.TYPE_SetSearchDataGridContent) {
tabPanel.addTab(content, NameTokens.searchPage + 1);
}
else {
super.addToSlot(slot,content);
}
}
Look in binding everything together. I think that you have to add the AsyncProvider of your presenter to yuor Ginjector.
AsyncProvider<SearchDataGridPresenter> getSearchDataGridPresenter();
You can put a SearchDataGridView getSearchDataGridView() method in the GIN Injector and call it in order to obtain the SearchDataGridView instance.

ActivityMapper with Gin and AsyncProvider

I've just started a project built with the new GWT archetype.
ActivityMapper looks like:
public interface Factory {
HomeActivity homeActivity();
GreetingActivity greetingActivity(String user);
}
private final Factory factory;
#Inject
MainActivityMapper(Factory factory) {
this.factory = factory;
}
#Override
public Activity getActivity(Place place) {
if (place instanceof HomePlace) {
return factory.homeActivity();
}
if (place instanceof GreetingPlace) {
GreetingPlace greetingPlace = (GreetingPlace) place;
return factory.greetingActivity(greetingPlace.getUser());
}
logger.severe("Unhandled place type: " + place.getClass().getName());
return null;
}
I'm now trying to implement code split with AsyncProvider based on this example, but I can't get it working.
When using ActivityAsyncProxy, what should I do? return the ActivityAsyncProxy from getActivity(Place place)? but then, how can I create the ActivityAsyncProxy from the factory?
How would you suggest to make the activity mapper play nicely with code split?
AFAICT, you cannot use AsyncProvider with assisted inject (that would be a great addition to GIN). That means you cannot benefit from generated runAsync calls, you'd have to do them yourself.
Have a look at http://code.google.com/p/google-web-toolkit/issues/detail?id=5129 for discussion of this issue, with several proposals.
Also have a look at https://groups.google.com/d/msg/google-web-toolkit-contributors/bUFYWEFskBI/ja2aJ0tBgdwJ for my own take on it (also available at https://gist.github.com/3038878).

ClickHandler doesn't fire in jquery-mobile header & footer

after hours of searching and trying, I decided to ask here.
JqmHeader.java
public class JqmHeader extends ComplexPanel {
public JqmHeader() {
setElement(DOM.createDiv());
getElement().setAttribute("data-role", "header");
}
public void add(Widget widget) {
super.add(widget, getElement());
}
}
JqmPage.java
public class JqmPage extends ComplexPanel {
...
public JqmPage(String id) {
setElement(Document.get().createDivElement());
getElement().setAttribute("data-role", "page");
getElement().setAttribute("data-url", id);
RootPanel.get().add(page);
render(page.getId());
}
private native void render(String id) /*-{
$wnd.$("#" + id).page();
}-*/;
...
}
MyPage.java extends JqmPage.java
...
JqmHeader header = new JqmHeader();
Button b = new Button("TestButton");
b.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
Window.alert("TestButton clicked");
}
});
header.add(b);
this.add(header);
...
My Problem
So, my Problem is, that the ClickHandler from the Button in the header bar doesn't fire. If I add the Button not to the header, but to the "RootPage", like
this.add(b)
, everything works.
I think it must lie at the jquery-mobile header implementation. Are there any workarounds /ideas?
Thanks from Berlin,
Alex
JQuery Mobile swallows the events on headers so they are not propagated to GWT. What I did in solving this for jqm4gwt (https://github.com/sksamuel/jqm4gwt) was to have a general listener on the page level and then compare event source. If it was a button in the header then I fire that event on the button manually.
Take a look at bindHeaderEvents in https://github.com/sksamuel/jqm4gwt/blob/master/src/main/java/com/sksamuel/jqm4gwt/JQMPage.java
Also, the jqm4gwt project might be a good solution for you to save you having to invent all these widgets yourself.
If you want to take a look at a pure GWT solution for building mobile apps you could take a look at http://www.m-gwt.com

Drag and Drop in GWT using gwt dnd

I have been really struggling to get Drag and Drop working in GWT. Last 3 days, I was trying to create a basic drag and drop application and failed. Currently I can drag it around, but I am unable to drop to any location.
How can we solve it? Do we need to modify onDragEnd - I am under the impression that unless I specifically have to do something, I dont have to? I am quite confused.
Also, how do I limit the drop to any single area? I do understand that we can do it using DropController. But I have defined the panels using UiBinder, so how do I get that panel back to link in the DropController? i.e. RootPanel.get() gives me the basic root panel and not the actual panel I want. I tried RootPanel.get("field-id"), but that is showing null even if that id is available. What am I doing wrong?
The code I have written is as follows:
public class TestPanel extends Composite implements
DragHandler, HasMouseDownHandlers, HasMouseUpHandlers, HasMouseMoveHandlers, HasMouseOutHandlers {
interface Binder extends UiBinder<Widget, TestPanel> { }
private static final Binder binder = GWT.create(Binder.class);
#UiField AbsolutePanel absolutePanel;
private PickupDragController TestDragController;
private Image img = new Image("./testicon.png");
public TestPanel(){
initWidget(binder.createAndBindUi(this));
absolutePanel.add(img);
TestDragController = new PickupDragController(RootPanel.get(), false);
AbsolutePositionDropController dropController = new AbsolutePositionDropController(
RootPanel.get());
TestDragController.registerDropController(dropController);
TestDragController.addDragHandler(this);
TestDragController.makeDraggable(this, getDragHandle());
}
private Widget getDragHandle() {
return img;
}
#Override
public void onDragEnd(DragEndEvent event) { }
#Override
public void onDragStart(DragStartEvent event) { }
#Override
public void onPreviewDragEnd(DragEndEvent event) throws VetoDragException { }
#Override
public void onPreviewDragStart(DragStartEvent event) throws VetoDragException { }
#Override
public HandlerRegistration addMouseDownHandler(MouseDownHandler handler) {
return addDomHandler(handler, MouseDownEvent.getType());
}
#Override
public HandlerRegistration addMouseUpHandler(MouseUpHandler handler) {
return addDomHandler(handler, MouseUpEvent.getType());
}
#Override
public HandlerRegistration addMouseMoveHandler(MouseMoveHandler handler) {
return addDomHandler(handler, MouseMoveEvent.getType());
}
#Override
public HandlerRegistration addMouseOutHandler(MouseOutHandler handler) {
return addDomHandler(handler, MouseOutEvent.getType());
}
}
and the testpanel uibinder looks like the following:
<g:AbsolutePanel ui:field="absolutePanel" styleName="{style.panel}">
</g:AbsolutePanel>
If somebody can help me out, I will be very much obliged.
K
P.S: To explain more: I was able to solve the first question by updating onDragEnd as the following:
#Override
public void onDragEnd(DragEndEvent event) {
DragContext context = event.getContext();
RootPanel.get().add(context.draggable, context.desiredDraggableX, context.desiredDraggableY);
}
but, I am not sure whether this is the correct solution - since I think I should not be doing the positioning myself.
If you're new to GWT dnd, why don't you try the working demo ?
There is a lot of examples and all the source code is available.
(And no, you're not supposed to do the positionning yourself)
You have to add a DragOverHandler on the drop target(s): even if it does nothing, it defines the component as a drop target.
Of course, you still need to define the DropHandler too on this component (and optionally, DragEnterHandler and DragLeaveHandler for visual feedback, in general).
The DragEndHandler is called even if the target isn't reached (drag abandoned in a non-drop area), it is used to change the state of the dragged object, you might need to set a way for the DropHandler to communicate success on dropping to the DragEndHandler (shared variable, EventBus, etc.).