play 2.4+ easy way to convert state of widgets in a view to javascript array or Json object and pass back to a controller when user leaves page?

58 views
Skip to first unread message

Simon Kaufmann

unread,
Jul 8, 2016, 3:57:55 AM7/8/16
to play-framework
I have a scenario which is pretty common and I'm trying to figure out the best way to handle it within the play framework.
The user is presented with a list, and gets to select one of more items from the list (in my case it's a twitter bootstrap button group).
I want take the users selection when they hit a "done" button and pass it through to a controller for action.

In my scenario the list is a static model - a series of items with ids that also have description etc

My initial thought was to map the ids of each item to the button classes, and use a jquery script when the page is exited to create an array of strings (item ids) that represents the users selection.

I'm not exactly sure how to implement this in play's view templating system... at the moment I pass in the list of items, iterate over it create button group controls with ids that are equal to the item id, and have a jquery script that interrogates the state of the controls when the user exits the page - however atm it is passing back an empty array to the next controller.

How do I convert from something like this (where selected is the variable) back to a java/scala list of strings that the controller can be passed and act on? (I assume this needs to be a jsArray? I'm definitely a json/javascript novice though so I may be missing the obvious...)
  
  <script>
        $(window).unload(function() {
            $('.btn-group checkbox:selected').each(function() {
                    selected.push($(this).attr("id"));
                });
        });
  </script>

where "id" = my database object id

is there an easier, better way to handle this common scenario
(huge minus to anyone who says - "don't do it this way - handle it in the controller" without a tangible example - that would be dodging the question - because I need to get the user's selection from the widgets in the view and convert into a list of Strings et al - something a play controller can handle...)



Simon Kaufmann

unread,
Jul 8, 2016, 7:12:03 AM7/8/16
to play-framework

the other important constraint here is that whatever client side variable is declared, it needs to be able to be passed back into a route

e.g. with List<String> selected = new ArrayList<String>() passed in
 
@(selected: List[String])

@main("Test Scripts Page") {
    
    <div class="page-header page-heading">
        <h1 class="pull-left">Test Scripts Page</h1>
        @helper.form(action = helper.CSRF(routes.MyController.create(selected))) {
        <input type="submit" class="btn btn-primary pull-right" value="Done >"> }
        <div class="clearfix"></div>
        <p class="lead text-left">Choose Buttons</p>
    </div>
    
    @for(index <- 0 to 3) {
    <div class="btn-group" data-toggle="buttons">
        <label class="btn btn-primary"><input type="checkbox" name="options" id="option-@index" aria-pressed="false" autocomplete="off" checked>option @index</label>
    </div> <!-- buttons --> }

    <input type="text" id="buttonvalue"/>
    
    <script>
        $(window).unload(function() {
            var jsArray = []
            $('.btn-group checkbox:selected').each(function() {
                jsArray.push($(this).attr("id"));
            });
            selected = Json.fromJson(jsArray, String);    
            $("#buttonvalue").val(selected.text());
        });
    </script>
}


Greg Methvin

unread,
Jul 8, 2016, 8:42:35 PM7/8/16
to play-framework
Hi Simon,

I think I see what you're asking for but I'm not really sure what your JavaScript code is attempting to do. Are "selected" and "Json.fromJson" defined somewhere else?

Are you trying to submit the form and trigger a new page load, or are you trying to submit everything via JS? If you want to do a normal form submit then you should place the checkboxes inside your form. You need to give each option a unique value property and parse your body using the "formUrlEncoded" body parser. Then you could do something like:

val selectedOptions = request.body.get("options").getOrElse(Seq.empty)

And those will be the value properties of your options.

You could also submit via ajax with jQuery but before we get into that I would want to have a better idea of what your goal is here.

Greg

--
You received this message because you are subscribed to the Google Groups "play-framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to play-framewor...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/71b545d4-6d99-4755-bede-9d91395e9a64%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
Greg Methvin
Senior Software Engineer

Simon Kaufmann

unread,
Jul 9, 2016, 1:17:47 AM7/9/16
to play-framework
I'm trying to setup a page where the user can select multiple items (in this case using a button group) and then pass that selection as a scala list on to another controller that acts on the users choice. I'm trying use a standard play route and pass the selection into the route when the user click on the done button. 

routes.MyController.create(selected)

so in this case selected is a var passed into the template (e.g. a List<String>) and I need to use javascript (or jQuery) to map the users selections into the var "selected" before invoking the controller.

The idea I had was to pass in a list of items from my model, write each item's description to a button as text, and the item's object id as an id attribute on the button.
Then use a script to review the state of the button group and write back the selected item ids into a List<String> and pass back to my controller for action.

I haven't seen any example of handling this in the play framework, despite it being a fairly common use case. As yet my application doesn't do any body parsing and I'm quite new to Json, javascript, jquery etc, so I might be missing something obvious and there may be a much better approach. 

The Json.fromJson method is from the play scala api - and I was trying to use it to map the javascript array jsArray back to scala List of strings so the controller can handle them directly as (in my case) Java object, (no body parsing in the controller as yet - hoping to do it this way instead).

Simon Kaufmann

unread,
Jul 13, 2016, 8:54:01 PM7/13/16
to play-framework
Ok. I have a solution for this.

My view template looks like this, with the Bootstrap button-grp tagged with "myButtons" (note: if you insert anything between the btn-group div and the label class buttons, JQuery doesn't seem to pick up the checked options), and an script with ajax call bound to the submitButton.

<div class="page-header page-heading">
        <h1 class="pull-left">Test Scripts Page</h1>
        <input type="submit" id="submitButton" class="btn btn-primary pull-right" value="Done >">
        <div class="clearfix"></div>
        <p class="lead text-left">Choose Buttons</p>
    </div>
        
     <div id="myButtons" class="btn-group" data-toggle="buttons">
        <label class="btn btn-primary active">
            <input type="checkbox" name="options" id="option1" autocomplete="off" checked> Box 1 (preselected)
        </label>
        <label class="btn btn-primary">
            <input type="checkbox" name="options" id="option2" autocomplete="off"> Box 2
        </label>
        <label class="btn btn-primary">
            <input type="checkbox" name="options" id="option3" autocomplete="off"> Box 3
        </label>
    </div>
        

    <script>
        $('#submitButton').click(function(e) {
            var selected = $( "#myButtons :checked" ).map(function() {
                return this.id;
            }).get().join();
            var selections = JSON.stringify({ 'selected' : selected });
            $.ajax({
                type :  "POST",
                dataType: 'json',
                data: selections,
                contentType: "application/json; charset=utf-8",
                url  :  "@routes.Application.getSelection()",
                success: function(data) {
                    console.log(data);
                }
            });
            return false;
        });
    </script>

my controller then picks up the selection via the AJAX post thus:

public static Result getSelection() 
    {
        JsonNode json = request().body().asJson();
    
        if( json == null)
        {
            Logger.info("Expecting JSON!");
            return badRequest("Expecting Json data!");
        }
        else
        {
            String selection = json.findPath("selected").textValue();
            if (selection == null)
            {
                Logger.info("Missing Parameter: selected");
                return badRequest("Missing Parameter: selected");
            }
            else
            {
                Logger.info("selection = " + selection);
                String[] ids = selection.split(",");
            }
        }
        return ok(modaltest.render());
    }

in my application code, those selections map to object ids in a model stored in my database, and then can be re-used in a subsequent request.

The only issue now is that the final return Request of my getSelection() controller (i.e. another GET to render a new page with a form) doesn't get invoked. No idea why yet. Probably something to do with the Ajax POST.

Greg Methvin

unread,
Jul 13, 2016, 9:31:08 PM7/13/16
to play-framework
Hi Simon,

What is modaltest.render doing? This should represent the data that is sent back in response to your ajax request. You would either need to send back some data to render on the page with JavaScript, or do a redirect in JavaScript (window.location = ...) to some other page when you get the response.

I think you could also achieve the same thing just using a regular form and no JavaScript at all, something like this: http://www.w3schools.com/tags/tryit.asp?filename=tryhtml_form_checkbox. Then get it from the body using request().body().asFormUrlEncoded().get("options").

--
You received this message because you are subscribed to the Google Groups "play-framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to play-framewor...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Simon Kaufmann

unread,
Jul 14, 2016, 3:54:43 AM7/14/16
to play-framework
Thanks Greg.

redirect(modaltest.render()) is just to move along to the next page in my application flow - after collecting the POST response with the user's selected options from my testscripts view. 

I've been testing approaches to this in a sandbox so "modaltest" be replaced with something else  - that said, I don't seem to be able to have ANY redirect work after submitting an AJAX post from a button in my view. Not sure why, but I don't know much about Ajax or how it should work with play. I will check out the other approach you have suggested - thanks for your help.

Greg Methvin

unread,
Jul 14, 2016, 5:52:21 AM7/14/16
to play-framework
Simon,

Is modaltest a view or something (not sure why you're calling a render method)? Normally you would redirect to a route, e.g. redirect(controllers.routes.Application.hello("Bob"))

You don't need to render something on a redirect response. The action you're redirecting to should do that.

If you're making the response via ajax, the redirect is just going to be sent back to the ajax client in JavaScript. It's not going to change the state of the page the ajax request is being made in. That's not a Play thing, it's just how ajax requests work. Your choices are to do redirection in JavaScript in your "success" function (e.g. window.location = foo) or to a normal form submit like I described.

Greg

--
You received this message because you are subscribed to the Google Groups "play-framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to play-framewor...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages