I have a login page and the main page. After login, I have
public void onSuccess(String result) {
// go to the next page
SDM_Mailer page = new SDM_Mailer();
RootPanel.get().remove(0);
RootPanel.get().add(page);
}
However RootPanel does not accept an EntryPoint object! add() only accepts a Widget! Ok, so I extend Widget
public class SDM_Mailer extends Widget implements EntryPoint {
But now when I try to edit SDM_Mailer in the GWT Designer, it gives this error:
So exactly how do you create multiple pages, that are not all in the same giant class file or using tab? I know I've done this before but don't remember and with an older version.
You can adopt Activities and Places.
As mentioned to the official gwt project site:
The Activities and Places framework allows you to create bookmarkable URLs within your application, thus allowing the browser's back button and bookmarks to work as users expect.
Activity
An activity simply represents something the user is doing. An Activity contains no Widgets or UI code.
Place
A place is a Java object representing a particular state of the UI. A Place can be converted to and from a URL history token
Source: http://www.gwtproject.org/doc/latest/DevGuideMvpActivitiesAndPlaces.html
You usually only have one EntryPoint from there you add different widgets (which can represent your different pages) and manage them.
A very simple way to do this is for a small app can be to use .setVisible(true) and setVisible(false) on different widgets that represent pages, but this is not a good method in the long run.
You could also have a container widget in which you add whatever widget you want to display and then when you want to put a new widget in it you clear it.
container.clear();
container.add(widget)
The suggestions above are ok for small apps but aren't great when your app has a lot of pages (views). A popular way to manage pages (views) is to use MVP Activities and Places. It's a lot of overhead but is scalable and works well.
Extending Widget doesn't magically make something representable in HTML. Widgets provide graphical representations of something in HTML, that is, a way to render themselves into HTML, this is ussualy achieved by implementing UIBinders. I strongly suggest you take the MVP approach and use Activities, Places and Views. check the official documentation on how to do it, it's simpler than you may think.
If you are working with Eclipse, the GWT plugin does most of the boilerplate. You can watch this video on how to use it.
This is what I ended up doing:
package com.example.client;
import java.util.logging.Logger;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.shared.GWT;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.ui.RootPanel;
public class Controller implements EntryPoint {
private static Controller instance;
private static final Logger log = Logger.getLogger(Controller.class.getName());
// I have a feeling GWT does not respect private constructors, or else it uses some other voodoo.
private Controller(){}
public static Controller getInstance() {
if (instance == null) instance = new Controller();
return instance;
}
#Override
public void onModuleLoad() {
String token = History.getToken();
log.info("****************************** token:"+token);
History.addValueChangeHandler(new ValueChangeHandler<String>() {
#Override
public void onValueChange(ValueChangeEvent<String> event) {
navigate(event.getValue());
} // onValueChange
});
if (token == null || token.length() == 0) History.newItem(Login.TOKEN); // no token
else navigate(token); // restore app state
}
private static void navigate(String token) {
RootPanel rootPanel = RootPanel.get("gwtApp");
if (rootPanel.getWidgetCount() > 0) rootPanel.remove(0); // clear the page
if (Login.TOKEN.equals(token)) {
Login page = Login.getInstance();
page.onModuleLoad();
} else if (MainApp.TOKEN.equals(token)) {
MainApp page = MainApp.getInstance();
page.onModuleLoad(); // display the page
// page.setAuthenticated(true);
// page.setUsername(email);
}
}
} // Controller
In your *.gwt.xml file:
<entry-point class='com.example.client.Controller' />
Now when you want to go to a new page:
History.newItem(Login.TOKEN);
This seems quite familiar and is probably what I came up with a few years ago.
Related
Well, I will try to explain the problem as well as possible.
I'm using vaadin 8 and I'm using java. I'm doing an app like Youtube.
First Of All I have two important class for the problem. 1º user_header. 2º main_user_page.
On the first class (1º). I have some code integrated with components vaadin that creates a header for youtube with An image and nickname.
public class Zona_de_busqueda_UR extends Zona_de_busqueda_UR_ventana {
public Zona_de_busqueda_UR() {
imagenCabeceraURZ.setSource(new ExternalResource(parametros.getMiniaturaUsuarioNavega()));
apodoCabeceraURZ.setCaption(parametros.getApodoUsuarioNavega());
}
The two atributtes are components vaadin, and I'm setting the text to show on the app.
On the second class (2º). is one page, where (after I initiate session) I can see a normal page with videos and the header of the class (1º). THIS CLASS IS WHAT I RUN AND THIS CLASS CALL THE OTHER WHERE THE ATRIBUTTES ARE EJECUTED.
public class Pagina_inicio_UR extends Pagina_inicio_UR_ventana implements View {
Zona_de_busqueda_UR cabeceraZUR = new Zona_de_busqueda_UR();
public Pagina_inicio_UR() {
horizontalLayout.addComponent(cabeceraZUR);
}
In this class, only call the other class what you can see.
The problem is that the value of the components is showing where I refresh the app, but this would be after I initiate session. I show an example with photos.
First Of all, I initiate session.
![a busy cat(https://raw.githubusercontent.com/jmr779/images/master/primero.PNG)
And then, The header is without information...
![a busy cat(https://raw.githubusercontent.com/jmr779/images/master/segundo.PNG)
Now, I click F5 on my browser or refresh the page and I can see the information but I want see the information without having to click F5 or refresh.
![a busy cat(https://raw.githubusercontent.com/jmr779/images/master/tercero.PNG)
Thanks for the help.
As a little example: Take a website where you land on a login page and after a successful login getting directed to the main page where on the left side you'd have a menu and on the right side a content are where everything gets loaded when clicking a link in the menu. In the menu I'd have multiple stores where each store would have its own settings. Those settings would be initialized with an id to determine which content the view should display.
For the history mapper I guess one would have something like this:
/#LoginPlace:login
/#MainPlace:home
/#MainPlace:storeSettings?id=<id>
/#MainPlace:userSettings
etc..
In [2] it says
"In order to be accessible via a URL, an Activity needs a corresponding Place."
Which sounds to me like I should have a LoginActivity and a MainActiviy since after all LoginActivity is the first Place I arrive when I come to my website and MainActivity is the place I go when I am successfully logged in.
I'd have someting like this:
public class LoginActivity extends AbstractActivity implements LoginView.Presenter {
// ..
private void onLoginSuccess() {
this.clientFactory.getPlaceController().goTo(new MainPlace());
}
}
public class MainActivity extends AbstractActivity implements MainView.Presenter {
// ..
}
and of course have respective view LoginView and MainView.
This is how AppActivityMapper.getActivity() would look like:
#Override
public Activity getActivity(Place place) {
if (place instanceof LoginPlace) {
return new LoginActivity((LoginPlace) place, clientFactory);
} else if (place instanceof MainPlace) {
return new MainActivity((MainPlace) place, clientFactory);
}
return null;
}
So far so good but how would I implement the MenuView and the MainContentView?
I want to be able to click on menu items in the MenuView and update MainContentView and generate place tokens accordingly like:
/#MainPlace:home
/#MainPlace:storeSettings?id=<id>
/#MainPlace:userSettings
But I have no idea how to do that and how I would for example initialize my activity StoreSettingsActivity with the given id. I believe that I would need another MainActivityMapper extends ActivityMapper that now should control the place changes initiated by clicking on menu links but I just can't figure out how that could work.
I hope my question is clear, please let me know if you need some more information.
I think I once did something similar, it was not perfect because I had to deal with an existing architecture but it worked.
Here is what I did:
When a link from the menu is click execute placeController.goTo(new MainPlace(id));
In your ActivityMapper, return your MainActivity in which you set the id of the MainPlace.
In your MainActivity, in the start method call a method that will initiate your MainContentView depending on the id of MainPlace you set earlier in the MainActivity.
I am pretty sure there is a better way of doing this, but it works and it can be a start that might help you to find a better solution.
I have a JavaFX control that is basically an amalgamation of several other JavaFX controls.
I want it such that the .jar file can be imported into Scene Builder so that it can be used like any other control. The closest analogy I can think of is when you make a custom control in C# and use it several times throughout several projects.
When I try to import the FXML file, it doesn't work. The control isn't treated as a single entity, and instead is basically just all of it's parts strung out in the FXML file.
What do I need to do with the FXML file, or the controller.java file so that the Scene Builder will be able to import the .jar, see the control(s), and allow me to import and use each custom control as a single entity? I've looked several places and even asked on Stack Overflow once before (though the answer I got was not the one for which I was looking, and have received no responses since), but nothing I've seen comes close to handling my issue.
The closest I've come has to do with this line in the FXML file:
<?scenebuilder-classpath-element /path/to/something?>
but I don't know what goes in /path/to/something
I know I can, in the initialization, simply add the control to the scene, but that is sub-optimal and something which I am desperately trying to avoid.
I was finally able to resolve the issue. After much trial and error and following the sample code that came from here, I discovered my problem was that I needed 2 classes for each FXML control group.
One to be the actual controller of the group, and another to be the object that would house the controller. I followed the code in the Unlock example and it was a godsend for helping me.
Basically it comes down to two files:
The object (which extends the type of the root node, in my case):
public class <InsertObjectClassNameHere> extends <RootContainerTypeHere>{
}
After that you need the controller class. This is with what I am most familiar, however I was still doing it wrong. This is what needs to implement initializable:
public class <InsertControllerClassNameHere> implements Initializable{
}
So for me the Object class looks like this:
public class DGCSDefiner extends GridPane {
private final DGCSDefinerController Controller;
public DGCSDefiner(){
this.Controller = this.Load();
}
private DGCSDefinerController Load(){
final FXMLLoader loader = new FXMLLoader();
loader.setRoot(this);
loader.setClassLoader(this.getClass().getClassLoader());
loader.setLocation(this.getClass().getResource("DGCSDefiner.fxml"));
try{
final Object root = loader.load();
assert root == this;
} catch(IOException ex){
throw new IllegalStateException(ex);
}
final DGCSDefinerController cntrlr = loader.getController();
assert cntrlr != null;
return cntrlr;
}
/**
* Get the settings defined by the controller.
* #return controller defined settings.
*/
public ColorSettings getColorSettings(){
return this.Controller.getColorSettings();
}
/**
* Set the controllers color settings.
* #param CS Defined color settings.
*/
public void setColorSettings(ColorSettings CS){
this.Controller.setColorSettings(CS);
}
}
and then there is the actual Controller class.
So for a straight-forward answer,
you need to have a class that will be loading your controller, and you need to pass down from your controller to that class that with which you will be working (Or, you can simply keep the controller public and access it directly).
Coming from a non-Java background, I find myself writing a lot of View classes with extensive functionality (in an effort to be portable), that don't necessarily fit nicely into the Android FW setup as far as maintenance - for example, I might have a widget that does something on a interval that I want to stop and clean up when an Activity is paused/stopped/destroyed. Generally I can manage this by just calling a public method on the widget from the containing Activity, but A) sometimes this gets pretty deep, and having to create a public method to access a child in every parent can get ugly, and B) requires extra (uneeded?) attention.
I'm considering using an approach like a global delegate to manage this kind of thing, but have read a lot of warnings about this sort of approach - would something like the class that follows have any inherent flaws that I might be missing?
import java.util.HashMap;
import java.util.HashSet;
public class ActiveRegistry {
private static final ActiveRegistry instance = new ActiveRegistry();
public static ActiveRegistry getInstance(){
return instance;
}
private HashMap<String, HashSet<Runnable>> registry = new HashMap<String, HashSet<Runnable>>();
private ActiveRegistry(){
}
public void register(String key, Runnable runnable){
if(!registry.containsKey(key)){
HashSet<Runnable> list = new HashSet<Runnable>();
registry.put(key, list);
}
HashSet<Runnable> list = registry.get(key);
list.add(runnable);
}
public void execute(String key){
if(registry.containsKey(key)){
HashSet<Runnable> list = registry.get(key);
for(Runnable runnable : list){
runnable.run();
}
}
}
}
Use might be something like...
A View has something that needs to be cleaned up. On instantiation, register it... ActiveRegistry.getInstance().register("paused", someRunnableThatCleansUpStuff)
Extend Activity so that onPause calls ActiveRegistry.getInstance().execute("paused");
You are doing way more work than you need to. Using Fragments (from the support package, if you want to ensure backwards compatibility with older versions of android), will make your life a whole lot easier. Each fragment is embedded in an activity and has a lifecycle that is directly linked with its host activity's lifecycle. Using them should significantly reduce the complexity of your code, as most of what you are currently worrying about will be managed by the system instead.
I'm managing the History in my project via Places.
What I do is this:
implement PlaceRequestHandler on top level (for example AppController),
register it -> eventBus.addHandler(PlaceRequestEvent.getType(), this);
implement method "onPlaceRequest" ,where i do project navigation.
I'm using GWT presenter and every presenter in my project overrides the onPlaceRequest method.
Why do I need this, when every request is handled from the top level "onPlaceRequest" method?
I will give an example:
public class AppController implements Presenter, PlaceRequestHandler
...........
public void bind()
{
eventBus.addHandler(PlaceRequestEvent.getType(), this);
...
}
public void onPlaceRequest(PlaceRequestEvent event)
{
// here is the project navigation tree
}
and let's take one presenter
public class SomePresenter extends Presenter<SomePresenter.Display>
{
... here some methods are overriden and
#Override
protected void onPlaceRequest(PlaceRequest request)
{
// what should I do here?
}
}
What is the idea, and how I'm supposed to use it?
Instead of making all of your presenters extend PlaceRequestHandler and managing those events yourself, you can attach a PlaceHistoryHandler and a PlaceController to your event bus. Together, they manage the browser's history and your places for you. When you ask your PlaceController to goTo() a different place, it will stop your current activity and use a mapping of places to activities (your presenters) to choose which one to start next.
To use this technique, you need to have your presenters extend AbstractActivity. Try following through Google's tutorial about it in GWT's documentation called GWT Development with Activities and Places.