play framework - bind enum in routes
Asked Answered
A

2

5

I am building an app in java on play 2.2.

I have a java enum as a parameter in a function that I use in routes.

This is my enum class. I searched around and found out I need to implements QueryStringBindable to use it in routes.

public enum Something implements QueryStringBindable<Something> {
    a,
    b,
    c;

    @Override
    public F.Option<ClientStatus> bind(String key, Map<String, String[]> params) {
        String[] arr = params.get(key);
        if (arr == null || arr.length == 0) {
            return F.Option.None();
        } else {
            Something status = Something.valueOf(arr[0]);
            return F.Option.Some(status);
        }
    }

    @Override
    public String unbind(String key) {
        return null;
    }

    @Override
    public String javascriptUnbind() {
        return null;
    }
}

Yet I tried in my routes:

GET    /someurl     controllers.Application.function(status: util.enums.Something)

But it returns bad request with error message as:

For request 'GET /someurl' [util.enums.Something]

I googled and didn't find any answer working in my case. Did I miss something or play doesn't support binding enums?

Aquaplane answered 30/1, 2014 at 22:10 Comment(0)
P
9

I had the same problem and I finally found out that it is not solvable as is.

By reading the documentation for PathBindable and QueryStringBindable I found that play framework requires the Bindable to provide a No Argument public constructor. Which by definition is no possible with enum in Java.

So I had to wrap my enum to solve this. In your example we would have something like:

public enum Something {
    a,
    b,
    c;

    public static class Bound implements QueryStringBindable<Bound>{
       private Something value;

        @Override
        public F.Option<ClientStatus> bind(String key, Map<String, String[]> params) {
            String[] arr = params.get(key);
            if (arr != null && arr.lenght > 0) {
                this.value = Something.valueOf(arr[0]);
                return F.Option.Some(this);
            } else {
                return F.Option.None();
            }
        }

        @Override
        public String unbind(String key) {
            return this.value.name();
        }

        @Override
       public String javascriptUnbind() {
            return this.value.name();
       }

       public Something value(){
           return this.value;
       }
    }
}

and then you have to use the type some.package.Something.Bound as a type in your routes file.

EDIT: using that in a template is slightly more tricky. And you have to know a bit of scala. To follow @Aleksei's comment

<a href="@routes.MyController.showStuff(myEnumVar)">link</a>

should become

<a href="@{
   routes.MyController.showStuff(new MyEnumVarWrapper(myEnumVar)).url
}">link</a>
Photoreceptor answered 14/2, 2014 at 2:58 Comment(3)
i gave a thought about the same solution, but it sounds too hacky to me. so i ended up binding the field to a string and parse the string in my controller to get a enum. This hacky solution is the closest for play 2, though.Aquaplane
This way seems more work but at least you play framework takes care of returning the right http error code for you if someone gets the spelling wrong.Photoreceptor
Your solution is not quite clear when one need to call action from template. I mean the code like this: <a href="@routes.MyController.showStuff(myEnumVar)">link</a> What I need to place instead of myEnumVar?Leprose
C
1

I want to offer a small correction to the answer: (The return type isn't ClientStatus, and the unbind function should use the key parameter, it's for the url generatiion)

public enum Something {
a,
b,
c;

public static class Bound implements QueryStringBindable<Bound>{
   private Something value;

    @Override
    public F.Option<Bound> bind(String key, Map<String, String[]> params) {
        String[] arr = params.get(key);
        if (arr != null && arr.lenght > 0) {
            this.value = Something.valueOf(arr[0]);
            return F.Option.Some(this);
        } else {
            return F.Option.None();
        }
    }

    @Override
    public String unbind(String key) {
        return key + "=" + this.value.name();
    }

    @Override
   public String javascriptUnbind() {
        return this.value.name();
   }

   public Something value(){
       return this.value;
   }
}

}

Cowitch answered 8/5, 2015 at 18:2 Comment(1)
Welcome to Stack Overflow! This is really a comment to the existing answer. With a bit more rep, you will be able to post comments. If you want to provide this as a different answer please explain why you think this is right or better compared to the existing answer.Bogor

© 2022 - 2024 — McMap. All rights reserved.