Search code examples
scalalift

Multiple Forms not rendering callback scripts


I'm attempting to write in Scala Lift a page that renders a list of items (proposals), and allows a button next to each to be clicked to take a verification action on that item. The approach I'm taking is to render a form for each item, and have it capable of taking an ajax action on that item. The page is rendering fine, except that the javascript scripts seem to not be rendering for each individual form.

This is my starting html:

<div class="lift:Verify.list" >
  <ul>
    <li class="proposal jsonForm" data-lift="Verify">
      <script class="jsonScript" data-lift="tail"></script>
      <div>
        <span class="name">Name</span> is <span class="verified">quantumly verified.</span><input type="submit" value="Verify"/><span class="result"/>
      </div>
    </li>
  </ul>
</div>

And this is my verification object:

object Verify extends Logger {

  val proposals:List[Proposal] = Proposal.findAll

  def list = ".proposal *" #>   proposals.map { p =>
      ".name" #> p.name &
      ".verified" #> { p.verified.get match {
          case true => "verified."
          case false => "UNVERIFIED."
        }}
  }

  def render = ".jsonForm" #> ((ns:NodeSeq) => jsonForm(VerificationServer, ns)) &
               ".jsonScript" #> Script(VerificationServer.jsCmd)


  object VerificationServer extends JsonHandler with Loggable {
    def apply(in: Any):JsCmd = in match {
      case JsonCmd("processForm", target, params: Map[String, String], all) => 
        SetHtml("result", Text("Verified"))
    }
  }
}

When I view source after the page renders (with three proposals being loaded up), I'm seeing this at the bottom:

<script src="/ajax_request/liftAjax.js" type="text/javascript"></script>

    <script class="jsonScript"></script>
    <script class="jsonScript"></script>
    <script class="jsonScript"></script>

<script type="text/javascript">
// <![CDATA[

var lift_page = "F1026676301564EIZULQ";
// ]]>
</script>

The empty <script class="jsonScript"></script> seems to be the problem: in a page with a single form, this contains something looking like this:

<script type="text/javascript" id="jsonScript">
// <![CDATA[
function F590130389017U5BRNC(obj) {liftAjax.lift_ajaxHandler('F590130389017U5BRNC='+ encodeURIComponent(JSON.stringify(obj)), null,null);}
// ]]>
</script>

I'm (obviously) very new to Lift, and may not be doing this the best way. If there is a better way to accomplish the goal, what is that? And if this is a reasonable path, why is it that those script tags are not being populated?


Solution

  • I'm not sure exactly the output you are looking for, but there seem to be a bunch of things that work in your example. For example, SetHtml requires and ID, but you are passing in a class. Also, your script tag is calling a method tail in the snippet which doesn't appear in your code.

    That said, something like this should allow you to do what I think you are looking to do:

    class TestSnippet {
      /** A definition of the Proposal class we will use *//
      case class Proposal(val id:Long, val name:String, var verified:Boolean)
    
      val proposals = //Define collection here
    
      def render = ".proposal" #> proposals.map { p =>
        "div [id]" #> s"prop-${p.id}" &
        ".name" #> p.name &
        ".verified" #> { 
          p.verified match {
            case true => "verified."
            case false => "UNVERIFIED."
          }
        } &
        ".submit" #> SHtml.ajaxButton("Verify", () => {
          p.verified = true
          JsCmds.Run("$('#prop-" + p.id + " .result').empty().html('" + p.verified + "')")
        })
      }
    }
    

    And apply it to the HTML

    <div>
      <ul data-lift="TestSnippet">
        <li class="proposal">
          <div>
            <span class="name">Name</span> is <span class="verified">quantumly verified.</span><input type="submit" class="submit" value="Verify"/><span class="result"/>
          </div>
        </li>
      </ul>
    </div>
    

    What this will do is iterate through your proposals collection and for each item, set the id attribute of the container div to a unique value (here I assume a Proposal has an id field) so jQuery can easily refer to it. Then, I bind an ajaxButton to your submit tag in the same CSSTransform that we output name and verified. When clicked, the button will set the verified field to true and then execute some JavaScript on the client - in this case it is a jQuery expression which looks for the div whose id we assigned and sets the text in the result span to the value of p.verified.

    So, the page would initially render like this:

    Nothing Verified

    And clicking on a button would yield this:

    enter image description here

    For more complicated updating, such as if you wanted to have "UNVERIFIED" change to "verified" lift also provides a few built-in Memoization functions that can help in re-rendering the CSS Snippet. You can see more about it here, here, and in bunch of discussions on the Lift mailing list.