[Blog] Comet actors, Chat app and knockout.js

211 views
Skip to first unread message

Diego Medina

unread,
Mar 2, 2013, 12:01:04 AM3/2/13
to Lift
Hi,

I just published two post covering 3 different ways to write comet
applications using Lift, they are based on the chat app found on
Simply Lift.

I hope you enjoy them:

Part I: goo.gl/YvsU8
Part II: goo.gl/0J4wg

Thanks

Diego
--
Diego Medina
Lift/Scala Developer
di...@fmpwizard.com
http://fmpwizard.telegr.am

Torsten Uhlmann

unread,
Mar 2, 2013, 2:17:23 AM3/2/13
to lif...@googlegroups.com
So cool, thank you!

-- 
AGYNAMIX(R). Passionate Software.
Inh. Torsten Uhlmann | Buchenweg 5 | 09380 Thalheim
Phone:       +49 3721 273445
Fax:             +49 3721 273446
Mobile:       +49 151 12412427
Web:           http://www.agynamix.de

--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code

---
You received this message because you are subscribed to the Google Groups "Lift" group.
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.



Torsten Uhlmann

unread,
Mar 2, 2013, 5:10:09 AM3/2/13
to lif...@googlegroups.com
I just read through the articles- very helpful and thorough, thanks!

I have a question on your best practices about decoupling server to client JavaScript communication.
Please don't see this as criticism, that's a field I need to learn about, so I'm asking what people use as best practice…

In your article you decouple server from client by sending events to the client and act on them with client side JavaScript.
I would have gone one step further and just call some JavaScript function in the server side lowPriority method. That JavaScript function would be defined in some JS file or object depending on the complexity of your API.

That function then in turn would trigger an event or send the data directly to a 3rd party lib.

The pro I see is an even better decoupling between server and client as you do not assume how the client would handle the data.
The con is possibly one more layer of indirection.

As JavaScript frameworks and integrating them into Lift apps is not my biggest strength, so I'm asking you (the list) about your best practices.

And thanks again for that article, Diego, it cleared up some of the questions I had about integrating JS frameworks.

Thanks,
Torsten.

Am 02.03.2013 um 06:01 schrieb Diego Medina <di...@fmpwizard.com>:

Diego Medina

unread,
Mar 2, 2013, 8:12:07 AM3/2/13
to Lift

Hi Torsten,

Thanks for reading it. I'm not sure I understand your question.

The way the technique 2 and 3 work is that Lift just sends the raw data, and the client side decides what to do with it and how to add it to the html.

Thanks!

Diego
Sent from my android cell

Torsten Uhlmann

unread,
Mar 2, 2013, 8:46:17 AM3/2/13
to lif...@googlegroups.com
Hi Diego,

the example sends the data to the client in form of an event:

$(document).trigger('new-ko-chat', %s)
That means, the server assumes there is some listener which listens at $(document) for this event. If you decide to put the listener at some other DOM element you need to change the Javascript call.

I was asking to compare it to instead calling a Javascript method which is defined in some JS file:

JE.JsRaw("""newKoChat(/* data as json */)""")
That method would in turn either trigger an event or give it directly to Knockout.

I'm not sure which way is better or more often used. So it's basically a question...

Torsten.

Aditya Vishwakarma

unread,
Mar 2, 2013, 8:46:39 AM3/2/13
to lif...@googlegroups.com
@Torsten: The problem with directly calling a Js function(calling it Comet JsFunc below) is that it adds a dependency on that function in js code. If someone has to call a bunch of functions on a comet push, he has to write the dispatch code in the Comet JsFunc. 
Also, any time a new function is the be executed, the Comet JsFunc has to be changed. This is a direct dependency which can be removed through events.
Another thing I can think of is if the event is being used to drive a UI, say when "toggleUserState" event is thrown -> Change permission of user. This event can be fired by either the user or as a consequence of some other event chain. The server can also participate  by directly firing events, which decouples  frontend devs from not having to worry about adding additional dispatch code in their Comet JsFunc.

It is not a hard choice obviously. We use both the methods depending upon the context. If something is exclusively server side, calling functions make sense. Otherwise we fire events when we feel we can reuse an existing pipeline build in frontend
Aditya

Aditya Vishwakarma

Chris Gaudreau

unread,
Mar 2, 2013, 8:56:31 AM3/2/13
to lif...@googlegroups.com
Hi Diego,

Thanks for the post, but I have a question: how do you decide when a message is clearable?

Peter Petersson

unread,
Mar 2, 2013, 9:14:01 AM3/2/13
to lif...@googlegroups.com
Great stuff Diego, really enjoined reading it !

Have you seen that FoBo v0.9.3-SNAPSHOT [1] has support for knockout.js
v2.2.1 in addition to prev versions 2.0 and 2.1 and the support is now
in it's own fobo sub project fobo-knockout, so it can be used and
initiated by it's own or as before initiated from within the FoBo meta
module.

I would love to see / find out some general code patters that could be
incorporated as FoBo/KO API comet functions, assuming very little of the
client stuff, showing intended couplings in the api docs and so on ...
that would IMHO be a awesome decoupling of responsibilities and a great
addition to the FoBo stack :)

I will certainly try to extract something, if anything can be found, the
next time I dive into KO stuff.
As always anyone is welcome to contribute! :)

[1] https://github.com/karma4u101/FoBo

best regards Peter Petersson

Diego Medina

unread,
Mar 2, 2013, 10:24:42 AM3/2/13
to Lift
@Torsten:
to add to what Aditya wrote, we also don't have many top level
javascript functions, as in, we try to add namespacing to kind of
encapsulate our functions. So listening for specific events so far has
worked well for us.

@Chris: from the knockout example html file, I set all the inner li
elements as clearable except one of them, because we need ko to know
which elements to "duplicate" on the for each call. IF I set all the
li elements as clearable, then ko would not have anything to work
with.

@Peter:

I forgot that FOBO now includes ko, I'll make sure to be using it in
future. And if I come up with any patterns I'll make sure to bring
them up and see if they can somehow be integrated.


Thanks everyone

Diego

Antonio Salazar Cardozo

unread,
Mar 2, 2013, 11:55:42 AM3/2/13
to lif...@googlegroups.com, adi.vis...@gmail.com
This is a great summary of why I lean event-based. I'll add one more: flexibility in handlers. Which is to say, say a user logs in via ajax. The response sends down an event 'logged-in' that is triggered on the document. Several independent components on the client can listen for the same event and take the actions appropriate to them based on that event. That's something that's doable, but more difficult and nastier if you're simply invoking a function.
Thanks,
Antonio

Torsten Uhlmann

unread,
Mar 2, 2013, 1:38:18 PM3/2/13
to lif...@googlegroups.com
Thanks for the great responses!

@Antonio: Are you usually sending the event to $(document) or are there cases when you send events more targeted?

Thanks,
Torsten.


-- 
AGYNAMIX(R). Passionate Software.
Inh. Torsten Uhlmann | Buchenweg 5 | 09380 Thalheim
Phone:       +49 3721 273445
Fax:             +49 3721 273446
Mobile:       +49 151 12412427
Web:           http://www.agynamix.de

Tyler Weir

unread,
Mar 2, 2013, 2:07:40 PM3/2/13
to lif...@googlegroups.com
Well done!

Antonio Salazar Cardozo

unread,
Mar 2, 2013, 3:59:06 PM3/2/13
to lif...@googlegroups.com
Typically $(document), but that's because I haven't found a clear need to date to target them to specific elements when doing high-level app communication. I think it's perfectly kosher if the situation calls for it, though. The document element is just a nice anchor for those.
Thanks,
Antonio

Dario Oddenino

unread,
Mar 4, 2013, 2:04:26 PM3/4/13
to lif...@googlegroups.com
Great! I'll read it after dinner :)
I was just asking myself if there was any need for something like knockout with lift, and I hope that this will help me out!

Dario Oddenino

unread,
Mar 9, 2013, 4:04:56 AM3/9/13
to lif...@googlegroups.com
Thank you for the article, very intresting.

I only have a doubt (I'm looking at the knockout example, but it's more general as a question):

when you're sending the message to the server in ChatIn snippet, is it possible to add to the message additional information collected with javascript?
I'm thinking about something like

ChatServer ! s + myFunctionThatReadsSomething(something)


case class myFunctionThatReadsSomething(something: String) extends JsExp {
 
def to JsCmd = // js stuff
}

Thanks!

Il giorno sabato 2 marzo 2013 06:01:04 UTC+1, fmpwizard ha scritto:

Diego Medina

unread,
Mar 9, 2013, 3:52:02 PM3/9/13
to Lift
Hi Dario,

You could change the ChatIn snippet, and have the render method work
with an ajax form, and then you can submit multiple fields if you
want, or you could use something like jsonCall , and read values from
the current page using plain jQuery, and send that information down
to the Lift server.

There are a few posts on the list about how to use jsonCall, and maybe
one of these links will help too
http://blog.fmpwizard.com/back-button-and-bookmark-meet-lift-comet-revi
https://fmpwizard.telegr.am/blog/adding-fields-dynamically-lift

And if you still need more help, feel free to post your questions here
and we'll try to help you.

Thanks

Diego
> --
> --
> Lift, the simply functional web framework: http://liftweb.net
> Code: http://github.com/lift
> Discussion: http://groups.google.com/group/liftweb
> Stuck? Help us help you:
> https://www.assembla.com/wiki/show/liftweb/Posting_example_code
>
> ---
> You received this message because you are subscribed to the Google Groups
> "Lift" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to liftweb+u...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>



Diego Medina

unread,
Mar 10, 2013, 1:58:55 AM3/10/13
to Lift
Hi Dario,

See this commit

https://github.com/fmpwizard/lift_starter_2.4/commit/55a7eaf20a7d4947a29c35872fd6a7cac1b9bee0

That shows you how to use jsonCall to read the text from the input
field, you can then expand the GetDataFromPage JsExp to take more
data, and format it as json

I'll be adding a few more commits to the branch
https://github.com/fmpwizard/lift_starter_2.4/tree/lift-knockout-js

over the next few days (and hopefully turn it into another blog post)

Thanks

Diego

Dario Oddenino

unread,
Mar 10, 2013, 2:58:58 PM3/10/13
to lif...@googlegroups.com
Wow, thanks a lot!

I'm having a bit of troube with extracting json objects.
For example, in your SubmitMessage function you have:
implicit val format = DefaultFormats
val msg
= j.extractOpt[String]

Now, I'm trying to extract something like:
JObject(List(JField(par1, JDouble(1234)), JField(par2, JDouble(5678))))

I'm looking both at Formats and at extract, but I'm not very sure on how to procede.
Should I create a custom format, use the right target for extract, create a case class that is suitable for this json object or all of this together?

Diego Medina

unread,
Mar 10, 2013, 3:05:16 PM3/10/13
to Lift

Hi,

First, I find this link super useful
https://github.com/lift/framework/tree/master/core/json

In your case, you don't need a custom format, either a case class that has those two fields, or the \ operator should do

Hope that helps

Diego
Sent from my android cell

Dario Oddenino

unread,
Mar 11, 2013, 3:08:05 PM3/11/13
to lif...@googlegroups.com
Thank you very much, the solution was amazingly simple.

Diego Medina

unread,
Mar 11, 2013, 3:08:35 PM3/11/13
to Lift
On Mon, Mar 11, 2013 at 3:08 PM, Dario Oddenino <bran...@gmail.com> wrote:
> Thank you very much, the solution was amazingly simple.

The power of Lift :)

Dario Oddenino

unread,
Mar 16, 2013, 6:50:18 PM3/16/13
to lif...@googlegroups.com
I actually have another problem!
Following your knockout example, my comet client fires a js event that updates a ko.observable with the last object saved to the db.
Everything works fine, except that when I hit refresh the js event is never fired and so all my ko bindings remain empty.

Diego Medina

unread,
Mar 17, 2013, 12:46:27 AM3/17/13
to Lift
Hi Dario,

See the latest commit on this branch
https://github.com/fmpwizard/lift_starter_2.4/tree/compare-chat-apps-comet-lift

now you can refresh the page and the messages will stay there.

We basically now have two messages, one for the initial render (which
I think is horrible that I pass the whole Vector btw, the next time I
have a few minutes I'll fix that), and the other message is the one we
call on initial render.

You will see that each of those "messages" result in different events
being trigger on the client side.

On the client side I now have a listener for the event
"initial-chat-messages", which checks if the page already has
messages, if not, it will add the ones we just got, otherwise it will
ignore them.

Thanks

Diego

Dario Oddenino

unread,
Mar 17, 2013, 6:43:34 AM3/17/13
to lif...@googlegroups.com
Hi Diego,
it works, but I'm failing to see how this is happening.

My lowPriority method

  override def lowPriority = {
   
case p: MyClass =>
      lastMessage
= p
      partialUpdate
(NewMessage(lastMessage))
   
case InitialRender =>
      partialUpdate
(NewMessage(lastMessage))
 
}

I don't understand who or what is sending this InitialRender message.

  def render = {
   
this ! InitialRender
   
ClearClearable
 
}

The "this ! InitialRender" is returning a Boolean? What for?

Thank you very much

Christian Thomas

unread,
Mar 17, 2013, 7:24:19 AM3/17/13
to lif...@googlegroups.com

Hi Dario,

Am 17.03.2013 11:43 schrieb "Dario Oddenino" <bran...@gmail.com>:
>
> Hi Diego,
> it works, but I'm failing to see how this is happening.
>
> My lowPriority method
>
>   override def lowPriority = {
>     case p: MyClass =>
>       lastMessage = p
>       partialUpdate(NewMessage(lastMessage))
>     case InitialRender =>
>       partialUpdate(NewMessage(lastMessage))
>   }
>
> I don't understand who or what is sending this InitialRender message.
>
>   def render = {
>     this ! InitialRender
>     ClearClearable
>   }
>
> The "this ! InitialRender" is returning a Boolean? What for?

No, as you can see in https://github.com/fmpwizard/lift_starter_2.4/blob/compare-chat-apps-comet-lift/src/main/scala/code/comet/ChatKnockOutJs.scala

the class itself is an actor. Initially on request the render method is called. "this ! InitialRender" sends a message InitialRender to itself, the actor instance. So you will find the case InitialRender =>
      partialUpdate(InitialMessages( msgs ))
  }

that will do a partialUpdate sending a js cmd including all existing msgs. Inside the js cmd an event will be triggered so knockout can do its work.

Hope that help s (have not read the whole thread)

Christian

> You received this message because you are subscribed to a topic in the Google Groups "Lift" group.
> To unsubscribe from this topic, visit https://groups.google.com/d/topic/liftweb/mJkBoin4SQU/unsubscribe?hl=en-US.
> To unsubscribe from this group and all its topics, send an email to liftweb+u...@googlegroups.com.

Dario Oddenino

unread,
Mar 17, 2013, 7:57:56 AM3/17/13
to lif...@googlegroups.com
Ops, I feel very stupid now. It was so obvious...
I think I should stop coding on Sunday mornings.


Thanks!
Reply all
Reply to author
Forward
0 new messages