angelokh
angelokh

Reputation: 9428

play 2.0 template the right way to reuse footer template

I have a footer template which has a input and submit for email subscription. The footer is used by every page. When subscription succeeds, it will redirect back to current page. However, I found out I am passing a string value to indicate what current page is. What is the best way to have a footer template for Play 2.0 application?

footer.scala.html

@(page: String)
<div id="footer">
    <div class="input-append">
        <form action="@routes.ApplicationController.saveSubscription(page)"
                                method="post">
        <input type="text" name="emailAddress" placeholder="Your Email" />
        <input class="btn" type="submit" value="Subscribe" />
        </form>
    </div> <!-- /input-append -->  
</div> <!-- /footer -->

ApplicationController.java

public class ApplicationController extends Controller {
    public static Result saveSubscription(String page) {
        ..........
        flash("success", "success message");
        if (page.equals("page1")) {
            return redirect(routes.ApplicationController.page1());
        } else if (page.equals("page2")) {
            return redirect(routes.ApplicationController.page2());
        } 
    }
}

page1.scala.html

@main("Page 1") {
    <div>
    <p>page 1</p>
    </div>
    @footer("page1")
}

page2.scala.html

@main("Page 2") {
    <div>
    <p>page 2</p>
    </div>
    @footer("page2")
}

EDIT 1

I follow @virtualeyes but it seems the subscribe.js is never been called. Here is the New setup.

main.scala.html

<html>
    <head>
        <script type="text/javascript" src="@routes.Assets.at("javascripts/jquery.min.js")"></script>
    <script type="text/javascript" src="@routes.Assets.at("javascripts/vendor/jquery.validate.min.js")"></script>
    <script src="@routes.Assets.at("javascripts/main.js")" type="text/javascript"></script>
    <script src="@routes.Assets.at("javascripts/subscribe.js")" type="text/javascript"></script>

    </head>
    <body>
       @footer()
    </body>
</html>

footer.scala.html

<div id="footer">
    <div class="input-append">                  
      <form id="_form" action="@routes.ApplicationController.simpleSubscription()">
        <input type="text" name="emailAddress" placeholder="Your Email" />
        <input id="_process" class="btn" type="submit" value="Subscribe" />
      </form>
    </div> <!-- /input-append -->  
</div> <!-- /footer -->

routes

POST    /subscribe  controllers.ApplicationController.simpleSubscription()

Now I got his error: Action not found For request 'GET /subscribe?emailAddress=fdsaf%40rte.com'

I am not sure if it is because method="post" is removed. If I put it back, then the result will return but will redirect to /subscribe page. I also set a breakpoint at subscribe.js but it doesn't seem to be called at all.

EDIT 2 - Working

after I changed a little bit for subscribe.coffee and get rid of main.coffee, now it's working.

subscribe.coffee

$('#_process').click (e) ->
  e.preventDefault()

  isValid = $('#_form').validate().form()
  if isValid
    $('#_process').spin()
    $.ajax
      type: "POST"
      url:  $('#_form').attr('action')
      data: $('#_form').serialize()
      success: (data) ->
        $('#_status > div').removeClass('alert-error').addClass('alert-success')
        $('#_status > div').html( data )
        $('#_status').fadeIn()
        fade = () -> $('#_status').fadeOut('slow')
        setTimeout fade, 2000
        $('#_process').spin('stop')

      error: (data) ->
        $('#_status > div').removeClass('alert-success').addClass('alert-error')
        $('#_status > div').html( data.responseText )
        $('#_status').fadeIn()
        fade = () -> $('#_status').fadeOut('slow')
        setTimeout fade, 2000
        $('#_process').spin('stop')

      complete: () -> $('#_process').spin('stop')

footer.scala.html

 <div id="_status">
    <div class="alert alert-error"></div>
 </div>
 <div class="input-append">
    <form id="_form" action="@routes.ApplicationController.simpleSubscription">
       <input type="text" name="emailAddress" placeholder="Your Email" />
       <input id="_process" class="btn" type="submit" value="Subscribe" />
    </form>
    <div id="_spin"></div>
  </div> <!-- /input-append -->

The spin() function is from https://github.com/pshizzle/spin.coffee

Upvotes: 1

Views: 578

Answers (2)

virtualeyes
virtualeyes

Reputation: 11237

AJAX is the way™

//footer.scala.html
<div id="footer">
  <div class="input-append">
    <form id="_form" action="@routes.ApplicationController.saveSubscription">
      <input type="text" name="emailAddress" placeholder="Your Email" />
      <input id="_process" class="btn" type="submit" value="Subscribe" />
      <img id="spinner" src="/assets/img/loader.gif" alt="loading..." />
    </form>
  </div>
</div>

<div id="status">
  <div class="alert alert-error"></div>
</div>
<style type="text/css">
  #spinner, #status { display: none; }
</style>

//ApplicationController.java
public class ApplicationController extends Controller {
  public static Result saveSubscription() {
    // save subscription
    ...
    // pseudo code
    if(success) 
      Ok( i18n("subscription success").toJson() );
    else
      Conflict( i18n("subscription fail").toJson() );
  }
}

//main.coffee
jQuery ->
  $.ajaxSetup
    type:     "POST"
    cache:    false
    dataType: "json"

  # prevent form submit on keypress
  $('form').find('input').keypress (e) -> e.preventDefault() if(e.which == 13)

  jParse = (data) -> 
    try jQuery.parseJSON(data)
    catch e
      data

  jText = (data) -> jParse(data.responseText)

  toSuccess = (msg) -> 
    $('#status > div').removeClass('alert-error').addClass('alert-success')
    $('#status > div').html( jParse(msg) )
    $('#status').fadeIn()

  toFail = (data) -> 
    $('#status > div').html( jText(data) )
    $('#status').fadeIn()

//subscribe.coffee
jQuery ->
  $('#_process').click (e) ->
    e.preventDefault()

    isValid = $('#_form').validate().form() // assumes jQuery validation plugin
    if isValid
      $('#spinner').show()
      $.ajax
        data: $('#_form').serialize()
        success: (msg) ->
          toSuccess(msg)
          fade = () -> $('#status').fadeOut('slow')
          setTimeout fade, 2000

        error: (data) -> toFail(data)

        complete: () -> $('#spinner').hide()

Upvotes: 2

biesior
biesior

Reputation: 55798

How about passing ready to use redirect address in hidden field?

footer

@(returnPath: String)
<form action="@routes.ApplicationController.saveSubscription()" method="post">
    ...
    <input type="hidden" name="returnPath" value="@returnPath" />
</form>

page1

@main("Page 1") {
    ...
    <!-- it's important to use toString at the end -->
    @footer(routes.ApplicationController.page1.toString)
}

or even better: giving the path from the current request()

@main("Page 1") {
    ...
    @footer(request.path)
}

controller

public static Result saveSubscription() {
    ...
    flash("success", "success message");
    return redirect(form().bindFromRequest().get("returnPath"));
}

BTW: maybe it would be just easier and nicer to use AJAX form? in such case you are avoiding redundant reloads at all (and finding objects and rendering whole templates again and again, etc etc.)

Upvotes: 0

Related Questions