Rich, "Ajax-style" web sites and applications offer tremendous advantages over both static web sites and desktop applications. But they can be difficult to develop, maintain, and expand. Many of the difficulties on the client side are alleviated by Ajax libraries such as jQuery, EXT, and Dojo, and by development environments such as Aptana Studio. Jaxer, the Aptana server, completes the picture by offering a powerful "Ajax server." With Jaxer, you can develop your entire site or application using only standards-based DHTML, JavaScript, and CSS. You can populate the HTML DOM on the server before the page is served using the same techniques and libraries you would use on the client; call server-side JavaScript functions directly from client-side JavaScript; validate your data with the same code on both the server and the client; and do it all in a completely standards-based, flexible, open-source environment.
Let's take a closer look at what it takes to put together a modern web site or application.
Client-side paradigms
The key to developing modern, richly interactive web sites and applications is to start with the desired user experience and interface. Web application developers need to determine how information will be presented to the user and how will the user interact with it. Once a developer sketches out the basic information flow, several other high-level decisions become apparent:
After defining the user interface, a developer can create a mockup to walk through the design. This becomes imperative for web applications with rich interfaces, which require dynamic HTML mockups with working controls and realistic data. Increasingly, a developer will use one or more "Ajax libraries" to create a rich UI, styled with CSS. As a developer refines these client-side mock-ups, the web page that the user will see takes shape.
Server-side development paradigms
Developing a client-side web page is only part of the work involved in developing a rich internet application, however. Developers encounter a new set of questions as they complete the development of their application:
Solving these issues requires a whole new set of technologies: the server side. For example, a server using PHP, Java, or Ruby, and often a templating framework, emits the HTML and the JavaScript needed for the client as one or more documents. The data needed by the client is then packaged into the page, usually by the templating framework writing JavaScript-format text. For rich web controls such as grids or calendars, the server-side templates cannot always emit exactly the HTML needed by the client-side Ajax library, so often the server will only emit an empty HTML container, and the client-side Ajax library must return to the server to fetch the data it needs to create the HTML for the control.
Client communication back to the server presents more issues. Ajax libraries simplify sending the data from the client, but then the server side must be set up to accept a large variety of requests and return the data in some format that the client can then read. Frequently, a templating framework writes (and escapes) PHP data into XML or JSON for transmission back to the client. Data validation on both the client and the server present even more issues, for example, how do you keep them in sync?
Eliminating the gap between client and server
Jaxer eliminates the complexity of using different technologies for client and server. With Jaxer, developers can use a single set of technologies—the ones closest to their users: HTML, JavaScript, and CSS.
As always, the page the user will see starts on the server, but now this page can just be the same static page that was mocked up in the design phase. As a developer, you can designate some JavaScript to run on the server before the page is sent to the client, such as code that accesses a database to fill up a grid. You can also designate some of the JavaScript to remain on the server but be accessible from the client, such as code to update the database with new data from the client, or retrieve database data for refreshing the client. In this case, when the user interacts with the page to fire events, these events trigger client-side JavaScript that can directly call the functions you designated to stay behind on the server.
Jaxer enables you to use Ajax-style development end-to-end. You can even use your favorite Ajax libraries for all of your development! This provides you with several key development benefits:
Jaxer is pre-configured for use as a plug-in to the Apache 2.x web server. Future versions will support more configurations. To provide world-class, standards-compliant JavaScript and DOM capabilities server-side, Jaxer is built on the Mozilla engine, which is the same engine used in the popular Firefox browser. Jaxer is layered into Apache as an input and output filter so that you can also use Jaxer to modify dynamic pages created by other languages, such as PHP or Ruby.
Jaxer Core vs. Framework
Jaxer itself is a combination of C/C++ "Core" code and a server-side JavaScript "Framework." The Core provides the JavaScript parser and runtime, HTML parser and DOM engine, and an event architecture that calls the Framework as the document is being processed on the server. The Framework provides the Jaxer logic itself, for example deciding which code to run on the server and which on the client, creating proxies on the client for callable server-side functions, serializing and deserializing data, and so on.
Because the Framework is written in JavaScript, it is easily extensible by Aptana or other developers. Jaxer is an open-source, free, and extensible platform for end-to-end Ajax development and for building rich presentation layers on top of existing back-end platforms.
Jaxer page lifecycle
Let's examine the lifecycle of a typical web page built with Jaxer:
On the server side, the developer's JavaScript environment is enhanced by the Jaxer Framework, which provides access to the database (MySQL only at this time), file system, network (coming soon), the HTTP Request and Response data, and in the future also external server-side platforms such as Java, PHP, and Ruby.
To define and/or execute any code server-side, add a runat attribute to a <script> block. This attribute has several possible values, and they determine where the code will execute (whenever this page is served) and what other actions will automatically take place:
| value | description |
|---|---|
| client |
The functions and code contained in the script block will run in the client browser only. This functions exactly as a regular script block. This is the default value of the runat attribute, so usually you'll omit it for script blocks intended for the client. Its main use is to override the runat attribute of a specific function within a server-side script block. Note: if a script block has runat = "client" (or no runat attribute), it will not run at all server-side, so you cannot override the runat behaviors of individual functions from within this block. |
| server |
The functions and code contained in the script will run on the server only. Any functions defined within the script block will be cached in association with this page. These functions are not directly callable from the client, but they can be called during callback processing by other server-side functions. These script blocks will not be presented to the client browser. |
| both |
The functions and code contained in the script will run on both the client and the server. Any functions defined within the script block will be cached in association with this page. The server-side functions are not directly callable from the client, but they can be called during callback processing by other server-side functions. |
Although most use cases will be covered by the basic attributes shown above, You can use the following runat values on <script> tags:
| value | description |
|---|---|
| server-proxy | Same as the basic 'server' target except ALL the functions will be proxied by default |
| server-nocache | Same as the basic 'server' target except NONE of the functions will be cached by default |
| both-proxy | Same as the basic 'both' target except ALL the functions will be proxied by default |
| both-nocache | Same as the basic 'both' target except NONE of the functions will be cached by default |
Jaxer is aware of some special function object properties that can be declared on individual function objects to control how they are managed. When these are specified the property value will override the containing script block runat setting for the individual function. This allows more granular control and prevents the need to break scripts out into separate files depending on their runat target.
| property | description |
|---|---|
| proxy |
Server-side functions can be declared to be proxied so they are callable from the client side. This is achieved by specifying a proxy property on the function object. The possible values for this property are true or false . This is only required for enabling the proxying of the function. By default, in a <script runat="server"> block, the functions are not proxied. Note that if a function is not proxied, it isn't just that proxies are not inserted into the client to facilitate calling it: it's actually marked as not callable on the server, so hacking the client to try to call the function on the server will not work. |
| runat |
Takes the same values as the <script> tag runat attributes. |
The following illustrates one simple way of using the runat and proxy options in a typical code scenario. We choose to group all the server-side code in one script block, and explicitly designate a subset of function to be proxied. Then all client-side code goes in a different script block (where there isn't even the option of programatically changing it by setting a different runat or proxy value). Of course you may choose a different way of organizing your code if that makes more sense. And for large amounts of code, it may also make sense to extract the code into (reusable) external JavaScript files.
The _login.js file referenced in the example above contains some functions that explicitly override the runat='server' directive specified on the script tag used to load the file.
Proxy example
In the following snippet, the function will proxied:
Client-side exampleIn the following snippet the function will run client side.
Jaxer provides a useful convenience object inside the Jaxer namespace to allow the proxy functions to be declared in a single group within your JavaScript code:
Jaxer.proxies = [myFunc1, myFunc2, "myFunction"]; // ... Jaxer.proxies = Aptana.proxies .push[myFunc3, "myFunction4"];
This code must be presented in such a way that it is executed by the server prior to DOM serialization. You can also use this to remove the proxied functions by setting the value to null.
Note: Jaxer.proxies is NOT a complete collection of the functions being proxied by the server it is just a convenient way to express the myFunc.proxy=true; syntax for multiple function references.
The runat attribute applies to everything within the <script> block. Individual functions within the <script> block can be changed to a different runat value by adding a runat / proxy property to them and setting it to the appropriate (string) value: for example, myFunction.runat = "both" . The one exception is for <script> blocks that don't have a runat attribute (or have runat="client" ): since such <script> blocks are not executed at all on the server, setting runat properties within those <script> blocks will not take place on the server, so the behavior of functions within them cannot be changed from within them.
This tutorial describes how to create a simple, one-page Ajax-style application to keep track of a list of tasks. The user can create new tasks, check off and delete existing tasks, and all the data should persist and be accessible from any browser. The functionality in this example is extremely simple to focus on the basics (e.g. no logging in step). Although you'll probably use one of the many popular Ajax libraries, such as jQuery, EXT, Dojo, or YUI, in building your own applications, this simple tutorial assumes no specific Ajax library use, and only uses the JavaScript built into any modern browser and keeps all CSS, JavaScript, and HTML in a single document.
Your Jaxer installation (whether as a standalone Jaxer Package or in Aptana Studio) includes this simple task application.
To access the tasks application from within Aptana Studio:
This section describes how to create your own tasks application from scratch.
To create the client-side:
This web page is starting to look nice, but it does not have any functionality yet. Next, you will delete the sample data and add some event handlers and a bit of JavaScript.
To add a simple client-side script to the page:
When the user types a description into the top textbox and clicks add, a new task line should be created at the top of the list.
Note: This addTask uses pure DOM manipulation. You could instead create the HTML as a string or copy a hidden HTML block (acting as a template) and convert it into the new HTML fragment. Usually you would use your favorite Ajax library to achieve this quickly.
You are now ready to add the last bit of functionality. When you check off a task as done, it should disappear from the list.
You can now add tasks, complete and delete them, and modify their descriptions; however you cannot save or retrieve data yet.
Note: to add the onclick event handler programatically to the checkbox, we used the Jaxer.setEvent(domElement, eventName, handler) function. There are multiple ways to add event handlers to a DOM element programatically. As you'll soon see, we'll need addTask to work both server-side and client-side. On the server we'll need to add the event handler to the DOM, changing the HTML that's then sent to the client. If we would have used checkbox.onclick = "completeTask(" + newId + ")" that would have added the event handler without changing the DOM, so the handler would not have made it to the client. The Jaxer.setEvent function "does the right thing" on both server and client, modifying the DOM on the former and directly setting the onclick property of the checkbox on the latter. The key lesson to remember: only DOM modifications make it from the server to the client.
This example uses MySQL to save and retrieve tasks data. (Make that sure you have adjusted config.js in your Jaxer installation directory before continuing. See the installation and configuration instructions.) The Jaxer Framework gives access to MySQL. The Framework is accessible within the Jaxer namespace (JavaScript global object) server-side, and a small part of it is also inserted into the Jaxer namespace client-side. The Framework, to minimize name collisions, defines no other global variables.
To add server-side functionality to save and retrieve your task data:
Note: When you call Jaxer.DB.execute(…) , you are using our default connection settings as defined in config.js, e.g. using the "demos" database specified in the default config.js that ships with Jaxer. If this database does not yet exist, it will be created automatically for you when Jaxer serves the first page.
Note: Usually addTask would use some 3rd-party Ajax library to create and add the DOM fragment for the task. Because both the DOM and JavaScript are on the server, you can use exactly the same addTask with the same Ajax library also on the server-side, and ensure that the HTML emitted from the server is exactly what the client needs. Just set addTask.runat="both" (or put it in a <script runat="both"> block).
Use the capability of DB.execute . For example, instead of embedding input values into the SQL itself, which could expose your code to SQL injection attacks. Use question marks and pass in an array of values to be substituted respectively for the question marks. This also eliminates the need to escape the input values.
This final step completes your application. You now have a simple Ajax tasks manager built entirely in one web page using only standard, web-native paradigms: HTML, JavaScript, and CSS with a bit of SQL on the server and some new JavaScript server-side functionality. The server modifies the HTML DOM based on some JavaScript code and the database, adds a few proxy functions and sends it to the client. The client allows the user to modify it based on new information using the same code, and seamlessly calls the server back to persist the changes to the database.
This page contains the entire code sample for the simple tasks application.
| Attachment | Size |
|---|---|
| iconPreviewSample.png | 927 bytes |
| iconImportSample.png | 619 bytes |
| toDo1.png | 4.7 KB |
| toDo2.png | 5.92 KB |
| taskcap.png | 6.19 KB |