Wednesday, July 20, 2011

On Reusable Components in Lift

This is my first post on this new blog but I wanted to jump straight into Lift. I currently am using Lift regularly for a large scale project.

I am writing this post because of the scarcity of resources on how to elegantly architecture large blocks of similar reusable code into a coherent module type structure. This is quite interesting since right now Lift has support for modules but it seems rather difficult to package together both HTML and scala code to make a reusable software component.

If this sounds very esoteric, I know, so I will give a concrete example.

Let's say you have a message list. A message list is just a chat window where you can send a message and other messages will be received with a timestamp and a picture of the user who sent it. Basically a small persistence dynamic realtime chat. 

The only thing which is different is getting the data for the message and then saving new data. 

Obviously this will be a snippet, but then how do we package up the 20 lines of the template HTML? 
This sounds like an obvious case for lift:embed, and we will get there.

Let use call these messages, GroupMessage and UserMessage, where the group messages all are local to a group and every user has his own personal message wall. 

I will discuss the various ways to create a component and then the pros/cons of each.

1. Create a trait which encapsulates the logic (including create the whole game message xml).

This would look something like this:


trait MessageList(func: save, retreive: func)
{

  def messageList(): NodeSeq
}


class Foo extends MessageList(save,func)
{

def render = 
"messageList" #> messageList
}

class Foo{

object GroupMessageList extends MessageList(save,func)

}

Pros: 

-allows for easy composition into other snippets
- allows one to dynamically create an save/and other func (very dynamic, don't see any use cases, perhaps prefer different post checks based on the user ??)

Cons:

Not as much customization available. The designer cannot see the HTML which is being emitted. If one wants to be able to embed html into places in the template not possible. And as you will see the other ways to do this seem tighter
No use of Lift's sweet templating system.


2.
First off you want all of the message view code in a embeddable template called _messageList.html. This would include both HTML and possible necessary Javascript etc...

Create an abstract class called AbstractMessageList and implement the methods of MessageList which handle retrieval and saving of the messages. The abstractMessageList automatically binds the messages with values retrieved from those functions.

The code would look like....

abstract class AbstractMessageList
{
   def  func
   def save

def render = #bind the everything
}

class GroupMessage
{

def func = #Get Data Function
def save = #GroupMessage Function
}

class UserMessage
{

def func = #Get Data Function
def save = #Save Function

}

In the group.html you then just do the following


<div class="lift:GroupMessage">
    <lift:embed what ="_message"></lift:embed>
</div>

This makes it very easy for the designer to change the _message template and the coder ( or if the same person even easier) to change the render function of the abstractMessageList

What if I want to Style the Message Lists differently?

I would change the code for the embed to something like this:

<div class="lift:GroupMessage">
<div id="group-message">
    <lift:embed what ="_message"></lift:embed>
</div>
</div>

We then can create styles which are just

#group-message message
{
}
#group-message li
{
}

etc...


What If I Want Use Different Html for each type of MessageList?

This is easy and will come up a lot if you do not want the exact same component but a customized and is one of the best parts about lift.

There are two ways to handle this. One is to use lift:bind's. Whereever you think there could be a hook in the _message code you add a 

<lift:bind name="possibleHook1"></lift:bind>
<lift:bind name="possibleHook2"></lift:bind>

<div class="lift:GroupMessage">
    <lift:surround with ="_message">
         <lift:bind at="possibleHook1">
                 <!-- bind any html here -->
         </lift:bind>
   </lift:surround>
</div>


If you want the possible hooks to in turn include dynamic code one could do the following.

class GroupMessage
{
        override def render = super.render & "possibleHook1Sel" #> //Stuff
}

Perhaps I am missing a more obvious solution of packaging of 
The power is limitless. The extendability and ease of use to package together similar functionally in simple to use Lift blocks is very easy. I wrote all of this down to get my own head around best practices when making reusable components for a website.