Exploring Avatar

Introduction

Avatar is an end-to-end web application framework based on the thin-server architecture (TSA) designed to build modern HTML5 applications. Avatar supports both view components (client) as well as service components (server). However, unlike other frameworks, view and service components are not tightly coupled so developers can opt to use them à la carte.

Service components are written in JavaScript and are executed on top of a Java EE 7 server extended with the Avatar Scripting Container (Avatar.js). View components are developed using HTML5 plus a rich set of widgets and data binding with minimal JavaScript code. In this tutorial, you'll learn about Avatar by examining code fragments and running sample applications that demonstrate several key concepts.

Getting Started

Avatar introduces several new concepts. The first one is modules of which there are two types. View modules, which are specific to clients and provide the UI components that are downloaded to the browser, and service modules, which run on the server and provide supporting services to the view components. By having view and service modules in a single application, Avatar provides an end-to-end framework that contains the client and server code in a single deployable archive. An Avatar application consists of at least one module, either a view module or a service module. As we shall see, view modules are often implicitly defined by the inclusion of one or more HTML pages in your application.

Clock

This first example of a very basic Avatar application is a clock that displays the time retrieved from the server through a REST API. It shows the most fundamental concepts of Avatar starting with the service layer.

The REST Service

The server part of this application is contained in one single JavaScript file that is evaluated by the Avatar.js container when the application is deployed. The first step in the implementation of a service module is to get a reference to the avatar object, which then is used to register the service.

var avatar = require("org/glassfish/avatar");

avatar.registerRestService({
    url: "data/time",
    methods: "GET"}, 
    function() {
        this.$onGet = function(request, response) {
            response.$send({time: (new Date()).toISOString()});
        };
    }
);

The first argument to the registerRestService is a meta data object that declares the relative URL of the REST resource to be data/time, and that it only accepts GET requests. The second argument is the constructor function that defines the implementation of the service. The $onGet method is invoked for each GET request that matches the service's URL, passing a request object and a response object. The $send method on the response object is used to return the requested resource to the client, in this case simply an object containing the current time.

View Module

View modules make up the presentation layer of an application, defining the user interface and the logic for interacting with the server. The view module for this application comprises a single HTML file shown next:

<html>
  <head>...</head>
    <body>
         <script data-model="rest">
             var Server = function() { this.time = ''; };
         </script>
        <script data-type="Server" data-instance="server" data-url="data/time"></script>
        <span id="output">Time is #{server.time}</span>
        <button onclick="#{server.$get()}" id="refresh">Refresh</button> 
    </body>
</html>

The body of this HTML page consists of an Avatar model definition and a model instance defined using script tags and a UI defined using standard HTML elements with a few extensions. A model, in this context, is defined using a JavaScript constructor function that combines application data, in the form of properties, together with application logic, in the form of methods that operate on the data. The special data-model attribute of script is used to indicate the model type and the data-instance attribute is used to define instances of a model.

This UI consists of a single output field that displays the time retrieved from the server followed by a button to refresh the time.

The span element that displays the time gets the actual data from the model using the Expression Language (EL). This data binding results in the text being updated every time the data model value is changed. The use of EL expressions eliminates the need for writing controller logic in JavaScript in order to make the page dynamic.

The onclick event handler on the refresh button is another EL expression, which tells the data model to fetch new data from the server. The method $get() on the rest model comes from the model's prototype object which is part of the Avatar framework and is inherited by all rest models you define in your application. It will send a GET request to the URL of the model and populate the resulting data as properties on the model object. As a result of doing that, because of the data binding to the span element, the output will automatically be updated with the new time as soon as the response has been received.

Updating a REST Resource

The data model defined here is a rest model that uses the declared URL to get a resource from a REST API. But a rest model can also modify REST resources using the other HTTP methods (PUT, POST, and DELETE). To demonstrate this let's add a Set button that can be used to change the time on the server. This requires changing the service as well as it so far only handles GET. To do so we implement the $onPut method in the service that parses the received time string and calculates a delta.

var avatar = require("org/glassfish/avatar");

var delta = 0;

avatar.registerRestService({
    url: "data/time",
    methods: ["GET", "PUT"]}, 
    function() {
        this.$onGet = function(request, response) {
            var current = new Date().getTime();
            return response.$send({time: (new Date(current - delta)).toISOString()});
        };
        this.$onPut = function(request, response) {
            var newTime = Date.parse(request.data.time);
            var current = (new Date()).getTime();
            delta = current - newTime;
            return response.$send(null);
        };
    }
);

The $onGet method is also changed to take the delta into consideration when returning the time. Note that the service now carries a state, the delta, which will be kept in memory as a JavaScript variable global to this application module. Later in this tutorial we will show how such state can be persisted and distributed in Avatar.

On the client side we add an input field where the time string can be modified, and a Set button telling the rest model to update the resource on the server.

...
    <body>
         <script data-model="rest">
             var Server = function() { this.time = ''; };
         </script>
        <script data-type="Server" data-instance="server" data-url="data/time"></script>
        <span id="output">Time is #{server.time}</span>
        <button onclick="#{server.$get()}" id="refresh">Refresh</button><br>
        <input id="input" data-value="#{server.time}"/>
        <button onclick="#{server.$put()}" id="set">Set</button> 
    </body>
...

The input field contains the same EL expression as the span element, which means that when the model value is changed the input field will also change, but more importantly, when the input field changes due to user input, the data model will automatically change. This is called a two-way data binding. The Set button uses a similar EL as the Refresh button but instead of invoking a GET request it tells the model to send a PUT instead with the value of the data model in the body.

The Push Service (SSE)

In addition to REST, Avatar also has built in support for Server Sent Events, consisting of

push services on the server side and push models on the client side. To show how to use these we will add an automatic update feature to the clock where the server continuously pushes the new time to the client every second. We start by adding a push service:

...
var getTime = function() {
    var current = new Date().getTime();
    return {time: (new Date(current - delta)).toISOString()};
};

...

avatar.registerPushService({
    url: "push/time"},
    function() {
        this.$onOpen = this.$onTimeout = function(context) {
            context.$setTimeout(1000);
            return context.$sendMessage(getTime());
        };
    }
);
`

The push service is registered in a similar way as the rest service; a meta data object containing the only mandatory property "url", and a constructor function. The $onOpen method is called when a new SSE connection is established, passing in a context object. This context object can be used to send data to the client, setting timers, etc. When a timer fires, the $onTimeout method is called passing the same context object. In this example we want to do the same thing in $onOpen and $onTimeout: schedule a new timer and send the time to the client. To reuse code we also break out the code for subtracting the delta into a function.

To communicate with this new push service, the client side needs a push model. The following modifications add such a model and binds it to the span element that prints the time. (We remove the Set button as it's not needed anymore.)

...
    <body>
         <script data-model="rest">
             var Server = function() { this.time = ''; };
         </script>
         <script data-model="push">
             var Updates = function() { this.time = ''; };
         </script>
        <script data-type="Server" data-instance="server" data-url="data/time"></script>
        <script data-type="Updates" data-instance="updates" data-url="push/time"></script>
        <span id="output">Time is #{updates.time}</span>
        <input id="input" data-value="#{server.time}"/>
        <button onclick="#{server.$put()}" id="set">Set</button> 
    </body>
...

Web Store

Using the Web Store sample application, you can store and then retrieve a collection of key/value pairs. You can perform any REST operation on the keys; PUT to add or update key/value pairs in the collection; GET to retrieve a key's value, and DELETE to delete a key/value pair from the collection. When you update the keys and values, the collection gets updated and because of data binding, all the data is synchronized. This sample can be found in the examples directory that is part of the distribution.

The Web Store application defines two REST services. The first service returns a single collection resource corresponding to all registered key/value pairs. The second service is defined for each individual key/value pair and is used to create, retrieve, update and delete (a.k.a. CRUD) key/value pairs. The collection service is shown first:

avatar.registerRestService({url: "data/items/"}, 
    function() {
        this.$onGet = function(request, response) {
            return itemsProvider.$getCollection()
            .then(function(result){
                var items = { "items" : result.data };
                return response.$send(items);
            });
        };
    }
);

The code snippet above registers a REST service on the relative URL "data/items/". It overrides a single $onGet method from the prototype that uses a data provider to retrieve and return an object containing all the items. A data provider is akin to an associative array in which values can be stored and retrieved using keys. However, providers can be implemented on different forms of data sources such as text files, distributed caches, databases, etc.

The service shown above calls the method $getCollection in order to retrieve the entire item collection. Since Avatar is based on a Node programming model, all method calls are asynchronous and either return promises, as in the example above, or accept callbacks. By default, all method calls return promises if no callback is supplied. Since, in this case, we want to return the entire collection, the method then on the promise is immediately invoked to wait for the result and return the response back to the client. Using promises or callbacks is mostly a matter of personal preference; however, promises tend to make the code more readable by avoiding the so-called Pyramid of Doom problem. When using promises it's important to terminate the promise chain in order to not loose any errors that may happen in any of the handler functions. This can be done by registering an error handler at the end of the chain, but a better way is to simply return the promise and let the Avatar framework terminate the chain. This ensures that a proper error response is sent back to the client if an error occurs. Note in the example above that the handler returns the promise returned by the $send() method. When a handler returns a promise (called inner promise) the outer promise becomes the inner promise. This ensures that any error that occurs in the $send() method is also caught.

Let us now look at the implementation of the item service:

avatar.registerRestService({url: "data/items/{item}"},
    function() {
        this.$onGet = function(request, response) {
            itemsProvider.$get(this.item, function(error, itemValue) {
                if (!itemValue) {
                    itemValue = {};
                }
                response.$send(itemValue);
            });
        };

        this.$onPut = function(request, response) {
            itemsProvider.$put(this.item, request.data, function(result) {
                response.$send(result);
            });
        };

        this.$onDelete = function(request, response) {
            itemsProvider.$delete(this.item, function(result) {
                response.$send(result);
            });
        };
    }
);

The methods $onGet, $onPut and $onDelete are overridden in order to interface with the provider. These method implementations show the alternative, callback style whereby the last parameter passed is a function to be called when the result becomes available. These callback functions, in turn, call the method $send on response to send the result to the client.

Data Providers

Finally, this example uses a data provider to store the key/value pairs. As stated previously, providers can be implemented on different forms of data sources. Let us explore the creation of itemsProvider using a file data source and a database data source based on the Java Persistence API (JPA).

The file data source is defined by providing a path to a file and a property name to be used as an object's key:

var itemsProvider = new avatar.FileDataProvider(
        { filename: "rest-sample.txt", key: "key" });

No additional configuration is required in the case of a file data provider. A database provider requires some additional parameters and configuration, namely, the name of the persistence unit, which defines the connection to the database, and the entity type, which defines the object structure and relational mapping:

var itemsProvider = new avatar.JPADataProvider(
        { persistenceUnit: "rest", createTables: "true", entityType: "Item" });

JPA uses an ORM file (object-relational mapping) that must be bundled with the Avatar application in the META-INF folder. The ORM file for this sample is shown next:

<entity-mappings ...>
    <entity class="rest.Item" access="VIRTUAL">
        <attributes>
            <id name="key" attribute-type="String">
                <column name="akey"/>       <!-- KEY is a reserved word in SQL -->
            </id>
            <basic name="value" attribute-type="String">
            </basic>
        </attributes>
    </entity>
</entity-mappings>

This XML file defines an entity comprising two attributes, key and value, with the former being the entity's ID and the latter a simple string value. For more information on JPA, see this Java Persistence API page.

View Module

The view module for this application comprises a single HTML file shown next:

<html>
  <head>...</head>
  <body>
    <script data-model="rest" id="ItemModel">
      var ItemModel = function(collection) {
        this.key = "key1";
        this.value = "";

        this.clear = function() {
          this.key = this.value = "";
        };
        this.$onPutDone = function() {
          collection.$get();
        };
        this.$onDeleteDone = function() {
          collection.$get();
        };
      };
    </script>

    <script data-model="rest" id="ItemCollectionModel">
      var ItemCollectionModel = function() {
        this.displayItems = function() {
          if (typeof this.items == 'undefined') {
            return "No Items. Add a key and value and click PUT.";
          }
          var result = "";
          for (var i in this.items) {
            var item = this.items[i];
            result += item.key + ":" + item.value + "</br>";
          }
          return result;
        };
      };
    </script>

    <script data-type="ItemCollectionModel" data-instance="itemCollection"
            data-url="data/items/"></script>

    <script data-type="ItemModel" data-instance="item" data-url="data/items/#{this.key}"
            data-props="dependsOn:'itemCollection', progressIndicator:'true'"></script>

    <h2>Item Collection</h2>
    <div>
      <button onclick="#{itemCollection.$get()}">GET ALL</button>
    </div>
    <br>
    <span id="output">#{itemCollection.displayItems()}</span>

    <h2>Item</h2>
    <div>
      <button onclick="#{item.$get()}">GET</button>
      <button onclick="#{item.$put()}" id="put">PUT</button>
      <button onclick="#{item.$delete()}" id="delete">DELETE</button>
      <button onclick="#{item.clear()}">Clear</button>
    </div>
    <br>
    <form>
      <label for="ik">Item Key: </label>
      <input id="ik" data-value="#{item.key}"><br>
      <label for="iv">Item Value: </label>
      <input id="iv" data-value="#{item.value}">
    </form>
  </body>
</html>

As seen in these declarations, the model type is rest. In JavaScript, an prototype provides a default behavior for an object, somewhat like a base class in Java. In Avatar, you can use the object's default behavior, override it with application code, or use a combination of the two as we've done in this application. It is worth pointing out that services that interact with Avatar applications need not be written exclusively in JavaScript; an EE service written in Java can serve data to a client component given that client and service components in Avatar use standard protocols.

Avatar uses the dollar sign ($) to denote methods with default behaviors that are defined in the Avatar framework. In this application, $get, $put, and $delete methods retain their default behaviors, whereas the methods $onPutDone and $onDeleteDone are overriden by the ItemModel constructor. The reason for doing this is that, by default, REST communications are asynchronous and, in some cases, you want to intercept certain events to update data models. In this application, we've overridden the $onPutDone and $onDeleteDone methods to allow us to intercept the PUT and DELETE operations and update the associated collection model whenever they are called.

In the code that defines the item instance, there's a dependency via a dependsOn attribute (an attribute foo can be defined either as data-foo or as part of data-props as in this case):

<script data-type="ItemModel" data-instance="item" data-url="data/items/#{this.key}"
        data-props="dependsOn:'itemCollection', progressIndicator:'true'"></script>

This indicates that the item instance depends on the itemCollection instance. Here's how it works. Each instance of the item model holds an individual key/value pair, whereas an item collection instance holds all the key/value pairs. When you update the item model (a single key/value pair), it correspondingly updates the data in the collection of key/value pairs. In other words, the methods $onPutDone and $onDeleteDone force a refresh of the collection object, and the collection object, in turn, provides the updated data to the UI.

In a rest model instance, you define a URL with which the application interacts and persists information on the server. Each instance definition contains a URL, one for each item and one for the collection. Avatar uses the URL to identify each resource in the system, each item resource and the collection resource. Notice that the item resource appends the key value (using #{this.key}) to the end of the URL so the URL changes as the model changes.

Finally, the UI portion defines an HTML form where a user can type in a key and a value. These properties are data bound to model properties using the Expression Language (EL) and the special attribute data-value. For example, property key is bound to the input element ik and property value is bound to the input element iv. Because of the nature of these HTML elements, as input elements, the type of binding in these cases is two-way: the model is updated when the input fields are updated and vice-versa.

Stock Tickers

The Stock Tickers application uses HTML5 and Server Sent Events (SSE) to push data from the server to the client asynchronously. The server is notified of the data change (for example, incoming stock quotes) and then pushes the data to the browser. This sample can be found in the examples directory that is part of the distribution.

Service Module

Just like the REST services we explored before, push services are registered on the avatar object. The difference, in this case, is in the prototype object assigned to the service instance.

The push service has three methods $onOpen, $OnClose and $onTimeOut. In this example, when a new connection is established, the $onOpen method is called and a wake-up timeout of 1 second (1000 milliseconds) is set. When the timeout expires the $onTimeOut method is invoked which updates the data (in the method updateData, omitted for brevity), sends the data to the client and resets the timer. In this manner, the data is updated and periodically sent to the client every second.

avatar.registerPushService({
    url: "push/stocks"
},
function() {
    this.data = {
        tickers: ["ACME", "EACM", "MEAC", "CMEA"],
        values: [25.0, 50.0, 75.0, 100.0]
    };

    this.$onOpen = function(context) {
        avatar.logFine("$onOpen called")
        context.$setTimeout(1000);      // start timer
    };

    this.$onClose = function(context) {
        avatar.logFine("$onClose called");
    };

    this.$onTimeout = function(context) {
        avatar.logFine("$onTimeout called")
        this.updateData();
        var promise = context.$sendMessage(this.data);
        promise.then(function() {
              avatar.logFine("Message sent (promise)");
          }, function(err) {
              avatar.log("Error sending message (promise): " + err);
          });
        context.$setTimeout(1000);      // re-start timer
    };

    this.updateData = function() { ... };
});

In the method $onTimeout, the data is sent by calling $sentMessage on the context object. Method then on the promise returned accepts two callback functions to handle success and error cases. This example also shows the use of Avatar's logging facility.

View Module

Stock Tickers is a purely declarative sample application; it is data binding that causes the UI to be updated when the model is refreshed from the server. Here is the code from the application's view module.

<html>
    <head>...</head>
    <body>
        <script data-model="push">
            var TickersModel = function() {
                this.tickers = ["ACME", "EACM", "MEAC", "CMEA"];
                this.values = [0.0, 0.0, 0.0, 0.0];
            };
        </script>
        <script data-type="TickersModel" data-instance="tk"
                data-url="push/stocks"></script>
        <h2>Stock Tickers SSE Sample</h2>
        <hr><br>
        <table>
            <tr><th>#{tk.tickers[0]}</th>
                <th>#{tk.tickers[1]}</th>
                <th>#{tk.tickers[2]}</th>
                <th>#{tk.tickers[3]}</th></tr>
            <tr><td>#{tk.values[0]}</td>
                <td>#{tk.values[1]}</td>
                <td>#{tk.values[2]}</td>
                <td>#{tk.values[3]}</td></tr>
        </table>
    </body>
</html>

Note the declaration of the model instance tk whose type is defined as push. The push model demonstrates a one-way data communication model, from the server to the client. The server sends data to the client which updates the model. When the model gets updated, data binding ensures that the UI gets updated as well. There is no procedural code required to accomplish this behavior; data from the server is received without ever having to request it.

Chat

The Chat application uses HTML5 and Web Sockets (WS) to handle asynchronous bidirectional data. For example, you can get or send messages whenever they become available, as in a typical chat system. This sample can be found in the examples directory that is part of the distribution.

Service Module

A socket service can override methods to handle events for opening and closing connections as well as receiving messages. Messages can be sent to a specific peer or to all the peers connected to the same socket endpoint, identified in this application by the "/websockets/chat" path.

avatar.registerSocketService({
    url: "/websockets/chat"
},
function() {
    this.data = {
        transcript : ""
    };

    this.$onMessage = function(peer, message) {
        avatar.log("$onMessage called");
        this.data.transcript += message;
        this.data.transcript += "\n";

        peer.$getContext().$sendAll(this.data, function() {
            avatar.log("Message sent");
          }, function(err) {
            avatar.log("Error sending message");
          });
    };
});

In this example, a new message received from a peer is concatenated to the transcript and then broadcasted to all peers connected to the endpoint, in other words all the participants in the chat room. Callback functions are passed to the context method $sendAll in order to log the outcome of the asynchronous send operation.

View Module

You log in the Chat application by typing your name and clicking Login. In the Chat Room, you type text in the Message field and click Send. In the Transcript output text box, you see that you are logged in and your message appears alongside your login name.

Let us start by exploring the socket model defined in the application's view module.

<script data-model="socket">
    var ChatModel = function() {
        this.transcript = "";
        this.message = "";
        this.user = "";

        this.login = function() {
            this.$send(this.user + " logged in");
            avatar.navigateTo('#chat');
        };
        this.send = function() {
            this.$send(this.user + ":" + this.message);
            this.message = "";
        };
    };
</script>

In this case the model's type is socket. The model type does not override any of the prototype's method; instead, it defines two new methods for logging in the chat room and for sending messages, both of which call the prototype's $send method.

The view module consists of two views, one used for logging into the chat room and the other for displaying the transcript and typing messages.

<html>
    <head>...</head>
    <body>
        <script data-model="socket">...</script>
        <script data-type="ChatModel" data-instance="ch" data-url="/websockets/chat">
        </script>

        <div data-widget="view" id="login" data-title="Chat WebSocket Sample"
             data-main="true">
            <h2>Login Page</h2>
            <label for="user">Username: </label>
            <input id="user" data-value="#{ch.user}"><br>
            <button onclick="#{ch.login()}" type="submit">Login</button>
        </div>

        <div data-widget="view" id="chat" data-title="Chat WebSocket Sample">
            <h2>Chat Room</h2>
            <textarea rows="5" cols="30" label="Transcript"
                      data-value="#{ch.transcript}"></textarea>
            <br>
            <label for="msg">Message: </label>
            <input id="msg" data-value="#{ch.message}"><br>
            <button type="text" onclick="#{ch.send()}">Send</button>
        </div>
    </body>
</html>

The login button on the main view invokes the login method on the model, which in turn, sends a message over the Web Socket connection (adding a user to the chat) and then navigates to the chat view. Once in the chat room, messages can be typed and sent over the connection; every time the transcript is updated, it is sent back to all clients causing their corresponding UI's to be updated.

JMS Chat

The JMS Chat sample shows integration with the Java Message Service API (JMS). This is a variation of the Chat example whereby messages are also delivered to and received from a JMS message queue, effectively implementing a gateway between a Web Socket connection and a JMS connection.

Service Module

Socket services in Avatar can be declaratively configured to interact with a JMS destination. The special jms property is an object that includes the name of the JMS connection factory, a destination name as well as a message selector and message properties. All this data is used internally by the socket implementation (more precisely, the socket's prototype object) to establish a connection with a JMS destination for the purpose of relaying messages to and from a JMS service.

Additionally, this sample creates a file data provider in order to store each chat room's transcript in external storage. The code for the service is shown next:

var chatStorage = new avatar.FileDataProvider({
    filename: "jmschat.txt",
    key: "id"
});

avatar.registerSocketService({
    url: "/websockets/jmschat/{chatroom}",
    jms: {
        connectionFactoryName: "jms/AvatarConnectionFactory",
        destinationName: "jms/AvatarTopic",
        messageSelector: "chatroom='#{this.chatroom}'",
        messageProperties: {
            chatroom: "#{this.chatroom}"
        }
    }
},
function() {
    var superOnMessage = this.$onMessage;
    var that = this;
    this.$onMessage = function(ctx, msg) {
        avatar.log("Received websocket message: "+msg);
        chatStorage.$get(this.chatroom)
            .then(function(transcript) {
                if (!transcript) {
                    transcript = {
                        messages: []
                    };
                }
                transcript.messages.push(msg);
                return chatStorage.$put(that.chatroom, transcript);
            });
        superOnMessage.call(this, ctx, msg);
    };
});

The socket service in this application overrides the method $onMessage from the socket prototype in order to update and store the transcript in an external file. Note that the method being overridden is also invoked to ensure delivery of the message to the JMS queue, in accordance with the declarative configuration provided.

As in other samples shown before, all calls to a provider are asynchronous and return promises. Only when a promise is fulfilled, in this case only after the transcript of the chat room has been read from storage, can certain actions like pushing a new message be carried out.

Given that the JMS configuration is purely declarative, the code needed to establish the JMS connection and forward messages back and forth is part of the socket prototype implementation. Note that Avatar also supports the creation of JMS destinations imperatively as well as an API to send and receive messages directly.

View Module

The view component of this application is similar to Chat. In addition to messages and transcripts, the view also supports the notion of a chat room that is selectable at login time. The chat room's name is part of the URL used to establish the Web Socket connection for the chat. As a result, two different models are defined: one local and one socket.

Let us start by describing the views that are part of this module:

<div data-widget="view" id="login" data-title="JMS &amp; WebSocket Chat" data-main="true">
    <h2>Login Page</h2>
    <form>
        <label for="user">Username: </label>
        <input id="user" data-value="#{um.user}"><br>
        <label for="room">Chatroom: </label>
        <input id="room" data-value="#{um.room}"><br>
        <button id="loginButton" onclick="#{avatar.navigateTo('#chat')}">Login</button>
    </form>
</div>

<div data-widget="view" id="chat" data-title="JMS &amp; WebSocket Chat">
    <script data-type="ChatModel" data-instance="ch" data-dependsOn="um"
            data-url="/websockets/jmschat/#{um.room}"></script>
    <h2>Chat Room #{um.room}</h2>
    <form>
        <textarea rows="5" cols="30" label="Transcript" data-value="#{ch.transcript}"></textarea>
        <br>
        <label for="msg">Message: </label>
        <input id="msg" data-value="#{ch.message}"><br>
        <button id="sendButton" onclick="#{ch.send()}">Send</button>
    </form>
</div>

The login view uses the model um, a local model, and the chat view uses the model ch, a socket model defined locally on that view and that depends (see dependsOn attribute) on the um model. The former is used to collect the user and chat room names, while the latter is used to exchange messages over the Web Socket connection. Note that the model ch becomes active only when its enclosing view is shown; this is required given that its URL is initialized using the expression #{um.room}. The following are the definitions for these models:

<script data-model="local" data-instance="um">
    var UserModel = function() {
        this.user = "";
        this.room = "";
    };
</script>

<script data-model="socket">
    var ChatModel = function(um) {
        this.transcript = "";
        this.message = "";

        this.$onOpen = function() {
            this.$send({user: um.user, message: "logged in"});
        };
        this.send = function() {
            this.$send({user: um.user, message: this.message});
            this.message = "";
        };

        var superUpdate = this.$update;
        this.$update = function(source) {
            this.transcript += source.user + ": " + source.message + "\n";
            superUpdate.call(this, this);       // Trigger data binding
        };
    };
</script>

The ChatModel type overrides the method $update which is invoked every time the model is updated as a result of the deserialization of new data received on the socket channel. In this sample, the internal transcript property is updated and the original method invoked to resume normal processing.

More Samples

You will find additional sample applications in the examples directory of your distribution. These include applications illustrating the use of some of the off-the-shelf widgets that Avatar supports including forms, trees, tables, menus, and various pane containers. Avatar supports a pluggable model for widget libraries. The widget library used by an Avatar application is controlled by a property in a file that is part of the application artifact.

Conclusion

In this tutorial, we have learned about the main Avatar concepts by examining a few samples: primarily the notion of view and service modules as well as models and data binding. Additional sample applications demonstrate more advanced widgets, other types of service providers and HTML5 client storage. The purpose of this tutorial has been to demonstrate how Avatar delivers an end-to-end framework for building modern web applications that are fast, lightweight, and responsive, exactly what is needed for today's desktop and mobile clients.