How to update application URI on the fly so bookmarks are usable?

One of our users asked from me how to update Application URI when view changes? They would like to add e.g. “/EmployeeView?employeeId=15” to URI when an employee view is opened and instantiated with employee id 15.

This would allow users to bookmark application “navigation state” which is pretty useful feature in most applications (especially if you are using single sign-on and using bookmark does not require you to login).

I do not know how to do this, has anyone tried this?

You can do that with a parameter (or uri) handler, but please note that this will require a full page refresh. You can not change the url without reloading the page - with one exception: the hash. You could use # in the url to change it without requiring a page refresh.

http://localhost:8080/myapp#some-direct-link

I have not tried, though… :slight_smile:

Best Regards,
Marc

So, the actual question remains :slight_smile: How do I change URI in my Toolkit application? Is there an API for this in Toolkit or do I need to hack it myself?

here is an example

package test;

import com.itmill.toolkit.Application;
import com.itmill.toolkit.terminal.ExternalResource;
import com.itmill.toolkit.ui.Button;
import com.itmill.toolkit.ui.Window;
import com.itmill.toolkit.ui.Button.ClickEvent;

public class UriChangeTest extends Application {

	public void init() {
		final Window mainWin = new Window();
		setMainWindow(mainWin);
		mainWin.addComponent(new Button("Change URI", new Button.ClickListener() {

			public void buttonClick(ClickEvent event) {
				mainWin.open(new ExternalResource(getURL() + "#"+System.currentTimeMillis()));
			}}));
	}

}

Thanks for reply, but this does not work for bookmarks. There is no way to catch # typed URI back to Toolkit (not by implementing Application.URIHandler or Application.getWindow(String name)). Therefore this URI change is not usable in bookmarking I think.

But, we can add e.g. /EmployeeView/12341234 to URI with this code and then catch it back in Toolkit and bookmarking into Toolkit applications work! Side-effect is that using bookmarks does full page refresh but that’s the way how non Ajax application “links / bookmarks” just work…

So this works:

Add button below to some layout


// Note, ExampleApplication.getCurrent() uses ThreadLocal pattern and returns Toolkit Application instance
		ExampleApplication.getCurrent().getMainWindow().addComponent(
				new Button("Go to EmployeeView and set URI too", new Button.ClickListener() {
					public void buttonClick(ClickEvent event) {
						ExampleApplication.getCurrent().getMainWindow().open(
								new ExternalResource(getApplication().getURL()
										+ "EmployeeView"));
					}
				}));

And add this to your Application


@Override
	public Window getWindow(String name) {
		// See if client comes in through a bookmark
		if (name.equals("EmployeeView")) {
			// Set view to EmployeeView
			getMainLayout().setMainView(EmployeeView.class);
			return getMainWindow();
		} else {
			// do not forgot this, it handles Toolkit windows
			return super.getWindow(name);
		}
	}

You could catch URI changes on handleURI too but it’s cleaner to do in getWindow method. However with getWindow method you cannot as easily parse other additional information that you might want to pass for the view, e.g. which employee I should be editing right now.

Note, now you got to make sure that you do not create a Toolkit window named by “EmployeeView” because that window is never accessible because of your overridden Application.getWindow(String name) code…

I have to say that application developers should think of other solutions than this even though bookmarks are very familar to many end users these days. But if you look where Mozilla Prism project is going (or even Firefox 4), you see that many browser mechanisms are fading out because of AJAX applications.

Simplest solution is to provide your “bookmark / shortcut” menu directly under Toolkit application.

Yeah, you’re right about not being able to catch # on the server. This is something we should implement, though - it would be quite useful. I’ll go ahead and add a ticket for that.

I’ve understood that this has been made in the 5.3 release. Is there any documentation about this? Or could you possibly provide a quick example about it?

In my case this feature is really needed.

Thanks in advance.

Hi!

You are completely right. Feature is implemented with invisible Component called UriFragmentUtility.

Manual is till not updated to have a complete examples of the usage. In the mean time you could check for examples in:

http://dev.itmill.com/browser/trunk/src/com/itmill/toolkit/tests/tickets/Ticket34.java

or from incomplete next generation feature browser called Sampler where it was implemented just this week:

http://dev.itmill.com/browser/trunk/src/com/itmill/toolkit/demo/sampler

Basic idea is that you can update fragment part (#something) of uri from server side without reloading the page. With FragmentChangedListener you can read that value (like entering to the app with fragment or using back/forward buttons).

I hope this helps enough.

cheers,
matti

Thanks for the links! I got it working but I encountered one problem that’s pretty critical one for me.

It works great when I type the
#something
to URL by myself or use Link-component. But if I try to write the URL with a code like this (with a button press for example) mainWindow.open(new ExternalResource(layout.getApplication().getMainWindow().getURL() + "#something")); The application writes the
#something
to URL and the application freezes. The FragmentChangedListener doesn’t catch it at all. And then I need to refresh the page to make the application work again.

And with the link component I use exactly same
new ExternalResource(…);
. You happen to know what I’m doing wrong or have any tips how I could write the
#somethings
to URL? I need it because I have to be able to bookmark pages in the application.

Argh. Maybe I should really finally register so editing would be possible…

code in the previous post should be more like this:

mainWindow.open(new ExternalResource(mainWindow.getMainWindow().getURL() + "#something"));

Aaargh! One more try…

mainWindow.open(new ExternalResource(mainWindow.getURL() + "#something"));

Sorry.

Hi,

If I read this correctly, you should be able to achieve the same thing by saying uriFragmentUtility.setFragment("#something"); That will change the current url.

If that’s not what your looking for, could you explain a bit more - for instance what is your current url when doing window.open() and where do want to go… Just basically what is the situation and what do you want to achieve…

Best Regards,
Marc

Thanks for the quick reply! And that was just what I was looking for. I can’t uderstand how I’ve been able to miss that method :slight_smile:

Back with a new problem…

The problem comes up with IE (6&7), Safari and Chrome. With FF 2, FF 3 and Opera it works fine.

With the the first mentioned browsers writing
#something
to URL doesn’t seem to cause an event. So I can’t parse the information. In my case I always have some fragment in the URL and when I enter a new fragment and hit enter the new fragment disappears and the older fragment replaces it. And the FragmentChangedListener doesn’t notice anything. But then if I press the “back”-button, the new fragment I typed (that got replaced by the older one) is displayed in the URL and the listener catches it.

For some reason the self typed fragment gets ignored and replaced with the previous one. But it still gets to browhers history and when “backing” to it, it works like it’s supposed to. It’s just being jumped over.

And when I insert fragment to URL with
setFragment(“something”)
the text goes to URL normally and causes an event with all browsers.

If you start the application with a fragment in the URL, the listener catches it. The problem comes up later then. Actually I think I once was able to write couple of fragments to URL by hand that got catched with IE. Then it stopped working again. But I’m not sure about this anymore because I didn’t manage to make it happen again.

Hi!

I made a change to trunk that changes the behavior a bit. Now you ought to get the event even with IE and Chrome (Safari was like FF to me) via typing to location bar even if your UriFragmentUtility had a state already.

The issue is due that browsers handle the situation differently when user changes the fragment part of uri. IE reloads the page, FF just changes the fragment. So developers should never encourage users to select application state by typing something to location bar. It will be faster just to click a button in application.

cheers,
matti

Hi!

I encountered one small problem more.

When starting the application with some fragment in the URL, the application opens up the right page (I have a navigation that works with normal navigation buttons and URL fragments). And when I enter some other page (with navigation buttons that make setFragment), the URL fragments changes normally. But if I try to navigate back to the page (with buttons) that was opened at first, URL fragments aren’t updated. The correct page is loaded so setFragment is done correctly and the FragmentChangedListener catches it and opens up the page. But for some reason the fragment doesn’t go to URL.

If I start the application with no fragments in the URL, everything works just fine.

Hi!

Hard to say what happens there. Can you provide a test case? Just an uri to deployed application might be enough.

cheers,
matti

I don’t know if I understood you correctly, but the uri looks like this when running it locally.

If I start the app with an uri like this:


http://localhost:8080/testapp/#SimpleSearch

It opens the simple search -page like it’s supposed to.

And then if enter a page (with button navigation)
http://localhost:8080/testapp/#AdvancedSearch
, it also opens up normally.

And if I now navigate to the simple search again, the page is displayed normally (button makes
setFragment(“simpleSearch”)
and the FragmentChangedListener catches it) but the uri doesn’t update. So the
#AdvancedSearch
is still in the uri, even though the current page is simple search.

Hi!

Our incomplete Sampler demo application had some troubles with UriFragmentUtility too. There was a regression after last update to the client side component.

I fixed this in:
http://dev.itmill.com/changeset/6371

I hope you had the same issue.

Please try nightly when 20081230 becomes available:
http://itmill.com/download/nightly/Default.html

(don’t care about failed tests)

cheers,
matti