Example for changing text of a p (or span) html element in a snippet from a ajax callback

66 views
Skip to first unread message

Philippe Derome

unread,
Nov 24, 2015, 9:51:27 PM11/24/15
to Lift
I am working with a function such as def process(): JsCmd =???
that is invoked from SHtml.hidden(process) following example of AjaxSubmit (http://simply.liftweb.net/index-4.8.html).

The normal non-erroneous code being executed in that example is  
case Full(a) => {          
RedirectTo(whence, () => {
  S.notice("Name: "+name)
  S.notice("Age: "+a)
})
}

Let us say I don't want to change page and be redirected and want to update the page seamlessly from within process function/call-back, targeting the html elements of the underlying snippet but not sending a message via the S object. The goal is to avoid browser page jitter when refreshing, so I want the same effect as S.notice or S.error without a call to RedirectTo for a  smooth visual effect (using S.notice here without the call to RedirectTo is a smooth browser experience). I have yet to look at using Comet or Actor within Liftweb and am not sure whether it's required to achieve this.

Thanks

Antonio Salazar Cardozo

unread,
Nov 25, 2015, 12:01:03 AM11/25/15
to Lift
You can go about this quite a number of ways. One is to have an element bound
on the page that uses `idMemoize`. At a very high level, that might look like this:

HTML:
<ul id="notices">
  <li class="notice">This is a notice!</li>
</ul>

Scala:
var notices = List[String]()

val noticesRenderer = SHtml.idMemoize { _ =>
  ".notice *" #> notices
}

def process(): JsCmd = {
  ...
    case Full(a) =>
      notices = List("Uh-oh, you messed up!")

      a.setHtml // returns JS that will rebind the notices
  ...
}

"#notices" #> noticesRenderer & // binds notices initially
"..." #> process _

You can also roll your own thing on the client, and simply send down a
function call or whatever your chosen mode of invoking the client-side
code is.
Thanks,
Antonio

Philippe Derome

unread,
Nov 25, 2015, 7:28:28 PM11/25/15
to Lift
yes, it worked, just I had to change "a.setHtml" to noticesRenderer.setHtml since method setHtml (not SetHtml) is defined on IdMemoizeTransform (the a object from simply liftweb 4.8 Ajax is of a different type). So, for people reading this issue, you may want to read up on idMemoize and IdMemoizeTransform. I now see that this liftweb group had lots of postings on idMemoize in past few years; however Lift in Action and Lift Cookbook don't mention this technique.

Thanks a lot Antonio, you responded quite fast.

On Tuesday, November 24, 2015 at 9:51:27 PM UTC-5, Philippe Derome wrote:

Philippe Derome

unread,
Nov 25, 2015, 9:02:58 PM11/25/15
to Lift
That was an initial inquiry. What I was in fact most interested in was to force a refresh for a whole snippet (or at least a div) after the ajaxSubmit callback has gathered its data to provide some of that data back to browser via snippet. It looks like one way to achieve this is to simply follow lift's blog here ( http://lift.la/blog/shtmlidmemoize-simple-ajax-updating ) In fact, just like on that blog, I'd also like two buttons to have differentiated refresh actions for the snippet.

I'll start playing with that blog example and presumably that'll do it.


On Tuesday, November 24, 2015 at 9:51:27 PM UTC-5, Philippe Derome wrote:

Antonio Salazar Cardozo

unread,
Nov 25, 2015, 10:00:34 PM11/25/15
to Lift
Glad to hear it helped! Yeah good catch on the `noticesRenderer.setHtml`—the price of
replying too fast, sorry :)

Regarding refreshing the whole snippet or whatever—my example was definitely a little
more limited in scope. You can always wrap the idMemoize around the whole rendering
block, since the parameter passed into the idMemoize function is the same thing that is
returned from it, so you can also use it to update the HTML from transforms inside the
block.

Let us know how things go!
Thanks,
Antonio

Philippe Derome

unread,
Nov 28, 2015, 9:04:37 PM11/28/15
to Lift
I was actually more interested in coordinating immediate data exchange between 2-3 snippets, so the original sample code I looked at seems a bit unsuitable now:
  • A displays data it obtains from B immediately when B obtains it
  • B executes a user form and obtains data (asynchronously in database or on web service), publishing to A
  • optionally C is a button+form that clears the data on A

So I guess I need Comet from B to A so that A renders with most up to date data since memoization will retrieve cached data (as it should, sigh) and won't be up to date.

Regards,

Phil

Brett Grace

unread,
Nov 29, 2015, 4:18:41 PM11/29/15
to Lift
You could render A here with a CometActor, and send messages to it from B and C. One thing to be aware of with CometActors is that be default there is only a single one, shared across all tabs and windows for a given session. You can give each CometActor a "name" which will force a new one to be created, but then you have to do some bookkeeping in your session to make sure that snippets B and C are talking to "their" CometActor A.

You could also render A as a lazy snippet (http://seventhings.liftweb.net/lazy). SetHtml's payload is evaluated as a template, so snippets are expanded. So B or C could simply use SetHtml to pass a new snippet to A's element and render it in parallel (arguments could be passed using either snippet parameters or RequestVars). Example:

In your template:
<div id="MyAsyncContents">
 
<div data-lift="LazyLoad">
 
<div data-lift="AsyncSnippet?parallel=true">
 stuff
 
</div>
 
</div>
</div>
<div data-lift="Trigger">
 
<button>Drink Me</button>
</div>

In your snippets package:

object AsyncSnippet {

 
def render = {
   
Thread.sleep(3 seconds) // simulate some long running process

   
"*" #> s"Your score at life is now ${System.currentTimeMillis}"
  }
}

object Trigger {

 
def render = {

   
"button" #> SHtml.ajaxButton("Clicky", () => SetHtml("MyAsyncContents", <div data-lift="LazyLoad"><div data-lift="AsyncSnippet">Can you hear me now?</div></div>))

 
}

}

Another avenue that might be worth exploring is Lift Wiring, see http://simply.liftweb.net/index-Chapter-6.html with a (maybe?) similar use discussed here in the news group. I'm not familiar enough with the capabilities and limitations of the wiring API to say for sure that it can do what you want, though, especially with regard to the async bit.

Philippe Derome

unread,
Nov 29, 2015, 9:01:40 PM11/29/15
to Lift
Thank you Brett. That's very helpful. I'll try your first suggestion on Comet (https://github.com/lift/seventhings/blob/master/src/main/webapp/comet.html), DPP's example seems to be a close fit. The wiring looks like something I should look at as well, but possibly a little later on. I take your notice on your warning as I see at http://simply.liftweb.net/index-2.3.html#prev that ChatServer is a singleton. I need to create another Actor singleton that is similar but holding different content than a vector[String].

As for the SetHtml example you provide, I can never see the "Can you hear me now?" in browser but instead a never ending spin even though a trace on Trigger.render shows expected NodeSeq transformation. 

Brett Grace

unread,
Nov 29, 2015, 9:17:26 PM11/29/15
to Lift
Hmm. You never should see "Can you hear me now" because the LazyLoad snippet doesn't render the interior snippet until it returns, by which time the contents have been replaced by the snippet. However the spinner should not be never-ending, it should resolve after 3 seconds to "Your score at life is now..." It was a quick example but I did have it working on my computer.

Philippe Derome

unread,
Nov 29, 2015, 9:58:58 PM11/29/15
to Lift
I tried again rigorously copying what you submitted (save for editing to 3000 parameter to Thread.sleep) and still spinning.

Good news is that what I proposed by extending Actor with an analog of ChatServer is working fine. Some code to clean up and I think I'll be very happy with what this Comet CometActor feature gives.

Philippe Derome

unread,
Dec 5, 2015, 9:31:14 PM12/5/15
to Lift
Using Comet has proven very useful. I broke down a moderately complex snippet with several elements into many smaller objects (snippets or comet actors) so that each form contains a single button ( I used to try to manage two buttons within a snippet) and find the design much cleaner and more flexible as a result. Perhaps that confirms D. Pollak's view of using many small independent items in a dynamic page. I want to read T.Perrett's Rock+Paper+Scissors comet sample and the wiring reference Brett gave me as well. This leads to more components and all of them very small, I like that. I'll have to read on Screens, Wiring, and Wizards though.
Reply all
Reply to author
Forward
0 new messages