I am beginner in gwtp and I want to build an application that displays a list of products, and by clicking I displays the details of the selected product...
My question is how to refresh the page to allow page product Detail to refresh while respecting security measures, obviously I do not want to pass the id of the product in the request.
I thought about storing the id in the session but I do not know if it will impact the application's performance given the high response times of RPC.
Any help or clarification on this would be appreciated.
You might consider using GWT's Cookie Support. Properly implemented, you'd always know exactly what they were doing last and getting them back to there becomes easy. Cookies are obviously client-side, so it's always going to be faster than RPC.
I have some advice but be aware I'm fairly new to GWTP as well....
Security
Communication should take place over SSL/HTTPS. I put it across my entire app using the servlet container (web.xml) so that it integrates seemlessly with non-GWT parts of my app.
I don't see a problem with putting an 'id' in a url. You can always prevent it from showing in the address bar with PlaceManager.revealPlace(PlaceRequest, boolean).
Composed View
I have a view with a list of entities on the left and the edit form on the right. The list is always shown and is placed in a 'slot' explicitly by a parent presenter:
public class Users extends Presenter<Users.View, Users.Proxy> {
#ContentSlot
public static final GwtEvent.Type<RevealContentHandler<?>> LIST_SLOT = new GwtEvent.Type<RevealContentHandler<?>>();
#ContentSlot
public static final GwtEvent.Type<RevealContentHandler<?>> FORM_SLOT = new GwtEvent.Type<RevealContentHandler<?>>();
#Inject
private UserList userList;
#Inject
public Users(EventBus eventBus, View view, Proxy proxy) {
super(eventBus, view, proxy, Configuration.SLOT);
}
#Override
protected void onReveal() {
super.onReveal();
setInSlot(LIST_SLOT, userList);
}
...
My app has an 'empty form' presenter which is shown by default when no list item is selected. This prevents the list and parent presenters from being a 'place' (requiring a token). Only the leaf presenters in the presenter hierarchy should be a 'place'.
Related
I am trying to do a transition to GWTP with my already existing application. However, I am having some troubles figuring out how to have a secured and an unsecured area for it.
The case is simple: I am having a landing page and an admin page which both have entirely different skeletons (menu, side nav, main content, etc.).
To make it simpler, these are my current module implementations:
What I am actually having in my ApplicationView UiBinder is this:
which is a public static UI field of ApplicationView:
public static #UiField SimplePanel mainContentWrapper;
What I do now is setting the actual content inside of the onReveal() method of the two presenter AdminToolPresenter and LangingPresenter like this:
#Inject
AdminToolPresenter(EventBus eventBus, MyView view, MyProxy proxy) {
super(eventBus, view, proxy, ApplicationPresenter.SLOT_AdminMainContent);
}
#Override
protected void onReveal() {
super.onReveal();
ApplicationView.mainContentWrapper.setWidget(this.getView());
}
and
#Inject
LandingPresenter(EventBus eventBus, MyView view, MyProxy proxy) {
super(eventBus, view, proxy, ApplicationPresenter.SLOT_LandingMainContent);
}
#Override
protected void onReveal() {
super.onReveal();
ApplicationView.mainContentWrapper.setWidget(this.getView());
}
Please note how I bind ApplicationPresenter.SLOT_AdminMainContent and ApplicationPresenter.SLOT_LandingMainContent respectively.
Since GWTP is actually a pretty smart tool I want to make sure that I am using it correctly.
Therefore I'd like to know if I do this the way it is intended to be or if GWTP actually provides a smarter solution to this problem?
This question rises especially since I am not sure yet how navigation is actually going to be handled.
There is no need to call ApplicationView.mainContentWrapper.setWidget(this.getView()) in your onReveal method.
GWTP should take care of that either by using bindSlot(ApplicationPresenter.SLOT_LandingMainContent, mainContentWrapper) in your ApplicationView constrcutor or by overriding the setInSlot or addToSlot method in your ApplicationView. (see the SLOTS documentation)
You will also want to use a Gatekeeper to secure your AdminToolPresenter.
I want to create a GWT UI where I basically will have a single HTML page that loads a PanelA object. The user will then do their thing and eventually perform an action that will move them onto another view/screen.
I have simplified my existing views to contain just a single button which moves the user onto the next page etc. for simplicity I only have 2 views to start.
Here is my start up entry point.
public class StockWatcher implements EntryPoint
{
public void onModuleLoad()
{
final RootPanel rootPanel = RootPanel.get();
rootPanel.add( PanelA.getInstance() );
}
}
Here is the PanelA class
public class PanelA extends HTMLPanel
{
private static PanelA panel;
private PanelA()
{
super("Panel A");
final RootPanel rootPanel = RootPanel.get();
Button btnNewButton = new Button("Go to panel B");
btnNewButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event)
{
rootPanel.clear();
rootPanel.add( PanelB.getInstance() );
}
});
add(btnNewButton);
}
public static PanelA getInstance()
{
if (panel == null)
{
panel = new PanelA();
}
return panel;
}
}
My other PanelB class is pretty much the same as PanelA , ie button that brings me back to PanelA
My UI works as desired.
My Question is, Is this singleton type pattern a correct or proper way to do this? ie Have a stack of Singleton UI views that get popped on/off the main panel?
Also, what is the best way to handle hitory/breadcrumb trace through a GWT app, ie allowing a user to go back to the previous screen bearing in mind that they may navigate to PanelX from either of PanelA, PanelB or PanelC
I use "Activities and Places" to manage all of this, and it's been working quite well in production for a year or so.
https://developers.google.com/web-toolkit/doc/latest/DevGuideMvpActivitiesAndPlaces
I think it's fine to use a Singleton mechanism for your views, but you have to make sure to completely reset any state you store. For me, it was easier to just create new views every time the user navigates to a new spot, and then if I detected a problem with load times or something to retroactively cause the view to re-use its components. I'd advise you to get the navigation working first, and then worry about the singleton (or not) optimizations.
I recommend Activities and Places design pattern.
It covers all the issues you raise in your question, plus many more that you have not thought of (or did not ask about) yet, like native browser history management and ability to bookmark different places within the app, handling of page reloads, efficient memory management vs optimized DOM operations, scalability (building an app with dozens/hundreds of views with minimal code duplication), testability, and code-splitting (where to split large apps).
I suggest you refer "GWTP" framework to make a GWT Project, some of the features currently supported by GWTP:
Dependency injection through GIN and Guice;
Simple but powerful history management mechanism;
Support for nested presenters;
Lazy instantiation for presenter and view;
Effortless and efficient code splitting;
Integrated command pattern supporting undo/redo;
So your questions and query like Singleton UI views, best way to handle hitory/breadcrumb trace and singleton type pattern , will cover and one good framework will make easy project management.
Im using GWTP in my GWT app and have the following architecture:
LoginPresenter (Presenter)
DashboardPresenter (TabContainerPresenter)
TabbedPresenter1 (Presenter)
TabbedPresenter2 (Presenter)
TabbedPresenter3 (Presenter)
The first time I start my app, the onBind and addTab methods are called in the DashboardPresenter and when I navigate to a tab, the onbind method is called on that presenter.
If I create a PlaceRequest and navigate back to LoginPresenter via my PlaceManager (by pressing a logout button), I return to the login presenter.
The problem is that if I login again, then all the onBind methods are not called because they are still in memory. onReset and onReveal are called correctly, but I would very much like that each Presenter are reset and that the onBind will be called on each login.
I decide on the login event which tabs should be visible for user and restrict tabs in the addTab method of the DashboardPresenter if the user does not have sufficient rights to see those tabs. But as of now, the tabs are setup the first time a user logs in, but not the next time. This means that if a user with lesser rights logs in after an admin user, he can see the same tabs as the admin. Not good!
How should I deal with this? I would very much like to "reset" all presenters or the session when a user logsout (navigating to the login page). Is it the Ginjector that needs to be "reset", so that it does not return the same binded objects as before?
Just to clarify: we do have server side security which prohibits users with no rights to access sensitive data. But when the user logs in, the gwt app receives a list of features which the user can access. This is used for customizing the UI to fit the rights of the user. (E.g. customize the visible tabs based on users privileges).
I am not sure if this work:
But you could try to fire a LogoutEvent on the global EventBus , handle it in all Presenters that need to be "unloaded" (TabbedPresenter1, etc) and call onUnbind() on them.
Afterwards navigate back to the LoginPresenter
Alternatively you could make us of a custom TabData (subclass TabDataBasic and add a flag hasAccess).
Again you fire a LogoutEvent and when you handle it you can do something like that:
TabDataDynamic tabData = (TabDataDynamic)getProxy().getTabData();
tabData.setHasAccess(false);
getProxy().changeTab(tabData);
In yout TabPanel implementation you have to make sure that the Tab is hidden, when the flag is set to false.
I think that you should take a look to GateKeeper which can be easily used with presenters like:
#ProxyCodeSplit
#NameToken(NameTokens....)
#UseGatekeeper( Your1GateKeeper.class)
public interface MyProxy extends TabContentProxyPlace<YourPresenter> {
}
And you can inject a Dashboard presenter to GateKeeper to check is this tab available:
#Singleton
public class Your1GateKeeper implements Gatekeeper{
private DashboardPresenter presenter;
#Inject
public ReadOnlyGateKeeper(DashboardPresenter presenter) {
this.presenter = presenter;
}
#Override
public boolean canReveal() {
return presenter.isAvailable();
}
}
So using appropriate GateKeepers will allow you to reach required security.
I'm trying to adapt my GWT web application from my home-grown MVC to GWT Platform.
I've managed to port the my application views with presenters, and essentially able to access views via PlaceRequest. And with changing the URL (#).
However I am not sure how to deal with Models using this GWT platform, in the common MVP I know there is a go() method in the presenter which fetches data, say from server via RPC.
In the GWT platform presenter here are the methods automatically generated by the Eclipse plugin:
Constructor
revealInParent
onBind
onReset
Where should I put the RPC code that will fetch and update my model. Say in the presenter I have:
ProfilePresenter.java:
public class ProfilePresenter
extends
Presenter<ProfilePresenter.MyView, ProfilePresenter.MyProxy> {
public interface MyView extends View {
HasText getFullname();
HasText getLocation();
HasText getAboutme();
HasText getLastlogin();
}
private User user; // Model which represents the User information etc.
And when the View associated with the Presenter is shown I need to fetch the User model from the server and update the model and then subsequently update the view through the interfaces it expose.
Also, say I have some buttons in the view, which then can be accessed by the presenter through HasClickHandler where should I put the event handlers?
I would put the RPC call in the onReset method.
See the presenter lifecycle
Personally I deal with events using the reversed MVP pattern. But you can also call a handler this way:
getView().getSubmitButton().addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
}
});
with the following signature for getSubmitButton in your view interface:
HasClickHandlers getSubmitButton()
Sydney covered most of your questions.
In general onResetmethod is a good place to make backend calls.
Sometimes when the backend call takes longer and you want to display the view only after the data was loaded you can use manual reveal.
But for the profile page I don't think that is necessary.
I also agree with the reverse MVP pattern. It's way easer to test presenters using the reverse MVP Pattern than using the HasXXXHandlers interfaces.
I'm currently building a GWT login screen and I managed to get a basic version of the login structure working.
I created the following pieces:
An entry point class (actually it was given).
A main application screen composite.
A login-screen composite.
I have the following logic in my entry point method:
MyApplication mainWindow = null;
public void onModuleLoad() {
LoginScreen loginScreen = new LoginScreen() {
#Override
public String onLogin(String username, String password) {
boolean passwordWasOk = rpcCheckUsernamePassword(username,password); // mechanism not important for this question
if (passwordWasOk) {
RootPanel.get().remove(0);
mainWindow = new MyApplication();
// Using root layout panel as main window is a layout type composite
RootLayoutPanel.get().add(mainWindow);
return null;
} else {
return "password was incorrect";
}
}
};
RootPanel.get().add(loginScreen);
}
So, I created a method in the LoginScreen composite that is called when the user clicks the 'Sign-In' button. If the onLogin method fails its validation of the username and password, then a narrative can be returned to the login composite so that it can update the user. The login screen will stay on the screen until the user uses a correct username/password combination.
My question is, is this the correct way to use composites? Where should my login logic ideally reside. Is a better alternative to inject some sort of login handler object in the constructor of the composite or via a setter or is the method I used quite normal?
As I'm planning to write quite a lot of code, I want to get the coding style correct from the outset.
Any feedback is greatly appreciated.
Thanks,
For complex projects you'd want to use the Model-View-Presenter (MVP) design pattern. It allows you to separate the rendering logic (views) from the business logic. To get you started, the official documentation has two articles about it plus there's that presentation by Ray Ryan that started the whole GWT + MVP = <3 haze :) After that I'd recommend browsing through MVP related questions on SO and GWT's Google Group.