Hi Joonas,
Thanks for looking into this further. Personally, I think if it could be made to work by fixing the two issues you mentioned in the core framework without adverse impact to existing apps, that would be preferable.
I also tried a scaled down prototype to try to get this working. I was able to work around that issue by adding the UriFragmentUtility to the new Window.
As I’m not so familiar with all the internals, I tried setting the fragment at various points in the app such as
- getWindow
- setting the fragment in handleURI for the Window
- setting the fragment right away when a new Window is created.
Here’s what I got working:
Here’s what didn’t work:
Not sure if it may help, but here is the code, and the results of testing various approaches are commented throughout.
package com.example.vaadinhistorytest;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
import com.vaadin.Application;
import com.vaadin.terminal.DownloadStream;
import com.vaadin.terminal.ExternalResource;
import com.vaadin.ui.*;
import com.vaadin.ui.UriFragmentUtility.FragmentChangedEvent;
import com.vaadin.ui.UriFragmentUtility.FragmentChangedListener;
/*
* CONCLUSIONS:
* OK TESTED - FIRST TAB OF SESSION IS http://localhost:8080/VaadinHistoryTest AND CLICKED ALL LINKS
* OK TESTED - FIRST TAB OF SESSION IS http://localhost:8080/VaadinHistoryTest#screen2 AND CLICKED ALL LINKS
* OK TESTED - FIRST TAB OF SESSION IS http://localhost:8080/VaadinHistoryTest, then open new tab with http://localhost:8080/VaadinHistoryTest#screen2
* -> But click links in first tab, and header links disappear!
*
* NOT WORKING, FIRST TAB OF SESSION IS http://localhost:8080/VaadinHistoryTest#screen2, then open new tab http://localhost:8080/VaadinHistoryTest
* -> But click links in first tab, and header links disappear!
* -> new tab uri not changing in browser bar, content not set
*
*
*/
public class VaadinhistorytestApplication extends Application {
public static final String _mainScreen = "main";
public static final String _screen1 = "screen1";
public static final String _screen2 = "screen2";
public static final String _uriHash = "#";
protected Component header = null;
@Override
public void init() {
header = this.buildHeader();
Window w = new MyAppWindow();
setMainWindow(w);
System.out.println("SET MAIN WINDOW IN init()");
}
public static String buildUriToken(String frag){
return _uriHash+frag;
}
public Window getWindow(String windowName) {
System.out.println("getWindow("+windowName+")");
Window w = super.getWindow(windowName);
if (w!=null){
String token = ((MyAppWindow)w).getUriFragmentUtility().getFragment();
System.out.println("-> Found window, token "+token);
if (token == null){
/*
* Setting the fragment here does the following:
* +GOOD: http://localhost:8080/VaadinHistoryTest on second tab will resolve properly to http://localhost:8080/VaadinHistoryTest#main and set content appropriately
* > NOTE: Without it, http://localhost:8080/VaadinHistoryTest on second tab doesn't get url as http://localhost:8080/VaadinHistoryTest#main and content is empty (assuming default content is not set with window creation)
* -BAD: http://localhost:8080/VaadinHistoryTest#screen2 on second tab will first show main view, then replace it with screen2
*/
//((MyAppWindow)w).getUriFragmentUtility().setFragment(buildUriToken(_mainScreen));
//System.out.println("--> Found window, Forced uriToken to = "+((MyAppWindow)w).getUriFragmentUtility());
}
}
else {
w = new MyAppWindow();
w.setName(windowName);
System.out.println("-> Created new window "+w.getName());
addWindow(w);
}
return w;
}
private Component buildHeader(){
final HorizontalLayout hLayout = new HorizontalLayout();
hLayout.setSpacing(true);
URL url = null;
try {
url = new URL(getURL().toString().substring(0, getURL().toString().length()-1));
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
url = getURL();
}
Link homeLink = new Link("Home",new ExternalResource(url+"#"+_mainScreen));
Link screen1Link = new Link("Screen1",new ExternalResource(url+"#"+_screen1));
Link screen2Link = new Link("Screen2",new ExternalResource(url+"#"+_screen2));
hLayout.addComponent(homeLink);
hLayout.addComponent(screen1Link);
hLayout.addComponent(screen2Link);
return hLayout;
}
private Component buildMainScreen(){
VerticalLayout vLayout = new VerticalLayout();
Label l = new Label("This is the MAIN content");
vLayout.addComponent(l);
return vLayout;
}
private Component buildScreen1(){
VerticalLayout vLayout = new VerticalLayout();
Label l = new Label("This is the SCREEN 1 content");
vLayout.addComponent(l);
return vLayout;
}
private Component buildScreen2(){
VerticalLayout vLayout = new VerticalLayout();
Label l = new Label("This is the SCREEN 2 content");
vLayout.addComponent(l);
return vLayout;
}
class MyAppWindow extends Window {
VerticalLayout mainView = new VerticalLayout();
UriFragmentUtility uriUtil = new UriFragmentUtility();
MyAppWindow(){
this.addComponent(header);
this.addComponent(mainView);
/*
* Setting content here causes problems:
* -BAD: http://localhost:8080/VaadinHistoryTest#screen2 initially renders with Main content and then replaces it with Screen 1 content.
* It only happens with new tabs, not when http://localhost:8080/VaadinHistoryTest#screen2 is the first tab of the session
*/
//setMainContent(buildMainScreen());
uriUtil.addListener(new FragmentChangedListener() {
public void fragmentChanged(FragmentChangedEvent source) {
String frag = source.getUriFragmentUtility().getFragment();
System.out.println("*** In Window-level UriFragmentUtility, fragmentChanged to "+frag);
// Actually manipulate the contents of the Window here
if (frag!=null && !"".equals(frag.trim())){
String[] params = frag.split("/");
String currentScreen = params[0]
;
removeSubwindows();
if (_screen1.equals(currentScreen)){
setMainContent(buildScreen1());
}
else if (_screen2.equals(currentScreen)){
setMainContent(buildScreen2());
}
else{
setMainContent(buildMainScreen());
}
}
else {
/*
* Setting the fragment here will do the following for either first tab of session or second tab:
* +GOOD: Changes uri frag in browser bar from http://localhost:8080/VaadinHistoryTest to http://localhost:8080/VaadinHistoryTest#%23main
*/
uriUtil.setFragment(buildUriToken(_mainScreen));
/*
* This is needed so that: (Tested with and without setFragment on getWindow)
* +GOOD: First tab of session with http://localhost:8080/VaadinHistoryTest will resolve properly to http://localhost:8080/VaadinHistoryTest#main and set content appropriately
*/
setMainContent(buildMainScreen());
}
}
});
/*
* Setting the fragment here causes problems:
* -BAD: First tab of session, http://localhost:8080/VaadinHistoryTest#screen2 initially renders with main content then gets replaced with Screen2 content
* -BAD: IF setFragment IS NOT DONE IN getWindow, http://localhost:8080/VaadinHistoryTest#screen2 as second tab also initially renders with main content then gets replaced with Screen2 content
*
* Setting the fragment here resolves a problem:
* +GOOD: IF setFragment IS NOT DONE IN getWindow, http://localhost:8080/VaadinHistoryTest on second tab will resolve properly to http://localhost:8080/VaadinHistoryTest#main and set content appropriately
*
*
*/
//System.out.println("--- Newly created window uriFrag is "+uriUtil.getFragment());
//uriUtil.setFragment(buildUriToken(_mainScreen));
//System.out.println("--- Created window with uri fragment auto-set to main");
this.addComponent(uriUtil);
System.out.println("CREATED A NEW WINDOW");
}
public UriFragmentUtility getUriFragmentUtility(){
return this.uriUtil;
}
public void setMainContent(Component mainComponent){
this.mainView.removeAllComponents();
this.mainView.addComponent(mainComponent);
}
public void removeSubwindows() {
Collection<Window> wins = getChildWindows();
if (null != wins) {
for (Window w : wins) {
removeWindow(w);
}
}
}
public DownloadStream handleURI(URL context, String relativeUri){
System.out.println("~~~ In Window handleURI, path = "+context.getPath()+", relativeUri = "+relativeUri);
String token = uriUtil.getFragment();
System.out.println("~~~ In Window handleURI, fragment = "+uriUtil.getFragment());
if (token == null || "".equals(token.trim())) {
/*
* This does the following:
* +GOOD: First tab of session http://localhost:8080/VaadinHistoryTest#screen2 avoids setting main content prior to rendering Screen2 content
* +GOOD: First tab of session http://localhost:8080/VaadinHistoryTest properly resolves to http://localhost:8080/VaadinHistoryTest#%23main
*/
uriUtil.setFragment(buildUriToken(_mainScreen), false);
System.out.println("~~~ Forced uriToken to = "+uriUtil.getFragment());
}
return super.handleURI(context, relativeUri);
}
}
}