4

Still a bit of a GWT noob here but making progress using Activities and Places as described by Google here.

I understand that a Place's "URL consists of the Place's simple class name (like "HelloPlace") followed by a colon (:) and the token returned by the PlaceTokenizer.

Can I somehow remove the colon when I don't have a token to send?

For example, I'm fine with a URL like this "#editPerson:2" when I need to work with PersonId=2. But what about when I just want to present a blank Person form? In that case I would prefer to use "#addPersonForm" rather than "#addPersonForm:"

Any suggestions (even better code suggestions) would be most appreciated!

dlamblin
  • 43,965
  • 20
  • 101
  • 140
Justin
  • 6,031
  • 11
  • 48
  • 82
  • This is from the doc: `Many Places in your app might not save any state to the URL, so they could just extend a BasicPlace which declares a PlaceTokenizer that returns a null token` Have you tried to create the PlaceTokenizer that retuns a null token for your Add Person form? – Strelok Dec 09 '11 at 02:56
  • thanks for your reply. if i return null from getToken(PlaceName place) then I end up with a URL like this: "#addPersonForm:null". Am I missing something (probably :)? – Justin Dec 09 '11 at 03:19

3 Answers3

3

To take full control of the URL hash (that is to generate your own tokens from Places and map these tokens back to Places) you can implement your own history mapper (a class implementing the PlaceHistoryMapper interface).

public class MyPlaceHistoryMapper implements PlaceHistoryMapper {

   @Override
   public Place getPlace(String token) {
        // parse tokens and create Places here  
   }

   @Override
   public String getToken(Place place) {
        // examine Places and compose tokens here
   }
}

In your entry point class you'd then replace the line:

AppPlaceHistoryMapper historyMapper = GWT.create(AppPlaceHistoryMapper.class);

with:

PlaceHistoryMapper appHistoryMapper = new MyPlaceHistoryMapper();

That's it. Your URL hashes no longer need to be class name-based or to use the : delimiter.

Boris Brudnoy
  • 2,405
  • 18
  • 28
  • many thanks for your reply. If I implement my own history mapper as you describe, does that mean I will no longer need the AppPlaceHistoryMapper interface at all? and the @WithTokenizers annotation? and a Tokenizer for each Place? thanks again! – Justin Dec 09 '11 at 20:17
  • Correct. In this solution you are not using the annotated interface extending PlaceHistoryMapper (which requires using GWT.create() to generate the actual PlaceHistoryMapper instance). Instead you're substituting your own implementation. You could still have a Tokenizer for each Place if you yourself intend to use them in your implementation. I've been doing without so far. – Boris Brudnoy Dec 09 '11 at 22:13
  • Boris_siroB, thanks for this info. i ended up going with Thomas Broyer's suggestion but I'll keep your solution in mind for the future. – Justin Dec 11 '11 at 00:25
3

You can provide your own PlaceHistoryMapper (without using the generator) as already suggested by Boris_siroB, or you can do it within a PlaceTokenizer with an empty prefix: with an empty prefix, there won't be a colon, and the tokenizer can do whatever you want. If you totally distinct places, make it a tokenizer of Place, so it's also the "catchall" for getToken. That way you can keep all the advantages of the generation with prefixes, PlaceTokenizers and WithTokenizers (if you want to take advantage of them)

Thomas Broyer
  • 64,353
  • 7
  • 91
  • 164
  • thanks for your reply. when you say "empty prefix", do you mean @Prefix("")? in that case don't i just get a url with a hashtag # and nothing else? it's not bookmarkable? likely i'm confused – Justin Dec 10 '11 at 02:28
  • I do mean `@Prefix("")`. With an empty prefix, the history token is then directly the value returned by the `PlaceTokenizer#getToken`. – Thomas Broyer Dec 10 '11 at 10:24
  • i get you now. that works fine when i use an empty prefix for one Place and still send it a history token. however, when i tried it on a second Place where I also don't need any state in the URL I get a Found duplicate place prefix "" error. Dang! – Justin Dec 10 '11 at 12:12
  • 1
    Yes, you can only have a single `PlaceTokenizer` for a given prefix. As I said, you'd have to use a _catchall_ `PlaceTokenizer`, for a class that is a parent of all the places you want to manage without the `prefix:` (that parent class can be the `Place` class itself). Of course, if you never want the `prefix:token` notation, then you should rather use a hand-made `PlaceHistoryMapper`. – Thomas Broyer Dec 10 '11 at 14:19
1

I'm using a PlaceHistoryMapper decorator named PlaceHistoryMapperWithoutColon.

Usage :

final PlaceHistoryMapper historyMapper0 = GWT
                .create(PlaceHistoryMapperImpl.class);

final PlaceHistoryMapper historyMapper = new PlaceHistoryMapperWithoutColon(historyMapper0);

Decorator source :

public class PlaceHistoryMapperWithoutColon implements PlaceHistoryMapper {

    private static final String COLON = ":";

    private PlaceHistoryMapper placeHistoryMapper;

    public PlaceHistoryMapperWithoutColon(PlaceHistoryMapper placeHistoryMapper) {
        this.placeHistoryMapper = placeHistoryMapper;
    }

    @Override
    public Place getPlace(String token) {
        if (token != null && !token.endsWith(COLON)) {
            token = token.concat(COLON);
        }
        return placeHistoryMapper.getPlace(token);
    }

    @Override
    public String getToken(Place place) {
        String token = placeHistoryMapper.getToken(place);
        if (token != null && token.endsWith(COLON)) {
            token = token.substring(0, token.length() - 1);
        }
        return token;
    }

}

Decorated source example :

@WithTokenizers({ FirstPlace.Tokenizer.class, SecondPlace.Tokenizer.class })
public interface PlaceHistoryMapperImpl extends PlaceHistoryMapper {

}

Place source example :

public final class FirstPlace extends Place {

    @Prefix("first")
    public static class Tokenizer implements PlaceTokenizer<FirstPlace> {

        @Override
        public NetworkInfosPlace getPlace(String token) {
            return new FirstPlace ();
        }

        @Override
        public String getToken(FirstPlace place) {
            return "";
        }

    }
}
Stéphane B.
  • 3,260
  • 2
  • 30
  • 35
  • Thanks, I used this. But I needed to remove the logic to add the colon, to get the places with token to work again. Is this method needed anytime? – jan Apr 29 '14 at 07:53
  • @jan sorry, I don't understand your question. If you don't need my decorator, you should just use "final PlaceHistoryMapper historyMapper = GWT.create(PlaceHistoryMapperImpl.class);" – Stéphane B. Apr 29 '14 at 14:09