Adrian
Adrian

Reputation: 5681

binding Scala to HTML in Lift

I've started playing with Lift. I want to create a basic application which takes input from user via input field, performs a db search on that input, and takes the results of that db search and creates a List of objects which then it binds to some html code.

I need help only with the binding part, but some background info first.

I have a List[Node]:
case class Node(aname:String, adata:String) {..}

I have this html to bind to:

<div class="lift:helloWorld.displayNodes">
    <div>
            <div><li><B>Node:</B></li></div>
                <div>Name: <node:name></node:name></div>
                <div>Data: <node:data></node:data></div>
                <BR>
        </div>
</div>

The problem is that this code calls my method displayNodes from the snippet and passes itself in. That way I can bind to it because I have the html code. So I bind like this:

//this method is called from the index.html and 
def displayNodes(html : NodeSeq) : NodeSeq = {
    nodeList.flatMap {node => Helpers.bind("node", html, "name"->node.name, "data"->node.data) }
}

Now the Question:
How do I bind without specifying in the html which method I want the html code to call. I mean I want to remove this line: <div class="lift:helloWorld.displayNodes"> because I don't want to have it called until the list is being populated. So basically instead of the html "calling" the scala code I want the scala code to call the html and bind to it, but only after it has the data. How can this be done?

PS - I looked at cookbook.liftweb.net, but they don't have an example of what I need.

Upvotes: 0

Views: 292

Answers (2)

VasiliNovikov
VasiliNovikov

Reputation: 10256

I'd suggest to use SHtml.idMemoize http://lift.la/blog/shtmlidmemoize-simple-ajax-updating This way you can initially have a page with an input and no results. After the user inputs his data and the server get's the DB data, you update the results by quering outer.setHtml

It's cheaper and easier than to set up a CometActor, but you'll get the results only when the user asks something. So, this way server won't be able to push some updates to the browser by itself.

Upvotes: 1

jcern
jcern

Reputation: 7848

I am not sure how you have your system set up, but typically for server push you'd want to use a Comet actor. The code below should get you started on that path, as it will perform an initial render and then allow you to send partial updates to the page based on message events. So, in this case it will respond to the event pushList which should be sent when the list is loaded. You can read more about Actors here.

HTML File

<div data-lift="comet?type=HelloWorldActor">
   <span id="nodes"></span> 
</div>

Actor

case class HelloWorldActor() extends CometActor {
  //Handle any messages received
  override def lowPriority: PartialFunction[Any, Unit] = {
    case "pushList" => 
      partialUpdate {
        JsCmds.SetHtml("nodes", displayNodes.apply(listTemplate))
      }
  }

  //Look up the template from templates-hidden/list.html or if that does not exist
  //use the default NodeSeq specified
  def listTemplate = Templates.apply("templates-hidden" :: "list" :: Nil) openOr
    <div>
       <div><li><B>Node:</B></li></div>
       <div>Name: <span name="name"></span></div>
       <div>Data: <span name="data"></span></div>
       <BR>
    </div>

  //Load the list, and then on completion send the actor the message to push the list
  //This could also happen from outside the actor, you'd send the message in a similar 
  //fashion from wherever it happens
  def loadList() = {
    //load the data in your list and on complete
    this ! "pushList"
  }

  //The CSS Selector to output the node
  def displayNodes = {
    "*" #> nodeList.map { node =>
      "@name" #> node.name &
      "@data" #> node.data
    }
  }

  //Initial CSS Transformation
  override def render = {
    //Your initial CSS transformation
  }
}

You will see JsCmds.SetHtml is what actually sets the HTML on the page. It is just a JavaScript command that looks for a given id on an html element, and then replaces it with the NodeSeq specified. If you are not looking to use Comet, you could use that command as the response from any Lift component that returns a JsCmd.

I also pulled out the HTML that outputs the node to make it easier to illustrate. The Templates object contains a mechanism for using designer friendly templates that can be looked up and re-used so you won't loose anything doing that. However, if you wanted to continue with the HTML all in one file, you could just capture the elements in your initial render like: "#nodes" #> { ns => listTemplate = ns; PassThru } (after changing listTemplate to a var of course).

Upvotes: 1

Related Questions