|
|
|
To do list part III - Data and Dojo
This tutorial uses database Access and Dojo, introduces some more Zero concepts, and improves our to do list application. Let's start with database access. So far, we have been using the memory for storing our to do list. Let's see how Zero handles database access and add the functionality to our application.
The following concepts are covered in this tutorial:
- Dependency Resolution
- Database Configuration
- Database Access
- Zero Configuration
- Dojo Toolkit
Before being able to use a database, we'll have to enable our application for database access by adding the required entries to the list of dependencies.
Dependency resolution
Every Zero application maintains a list of dependencies. In order to enable database access, we need to add the appropriate dependencies to our project. The list of dependencies is stored in the ivy.xml file. This is part of Ivy, a dependency management system that a Zero application uses. You can get more details about it at the Ivy website. For this tutorial, we will use a MySql database, so we need to add the appropriate dependency to the project. Perform the following steps to add the required dependencies:
- Follow the steps in the previous To do list part II - REST and AJAX tutorial to stop the simpletodo2 application and create a new project named simpletodo3.
- Expand the config folder and double click ivy.xml file as shown on the following screen.
- Click the Zero Package Information tab as shown on the following screen.
- Under the Dependencies section, click the Add button as shown on the following screen.
- Select the following dependencies in the Dependency Selection panel:
- zero.data
- mysql-connector-java
- dojo
Note: You can select multiple items in the list by holding down the Control key.
If you did not see one one or more of the entries in the list, click the Source tab in the editor and add the appropriate line in the dependencies section, and click OK in the dialog box to resolve these dependencies remotely.
<dependency org="dojo" name="dojo" rev="0.4.3"/>
<dependency org="mysql" name="mysql-connector-java" rev="5.0.3"/>
<dependency org="zero" name="zero.data" rev="1.0.0+"/>
Zero manages a local repository of its dependencies and allows you to search and download dependencies from remote repositories. For more information on how to manage your repository, visit the Dependencies and Packaging section of the documentation.
- Click OK.
- Save the
ivy.xml file.
- If you see problems in the project, right click the project and select Zero Tools -> Resolve.
Note: You might have noticed that we also added the dependency for Dojo. For now, let's concentrate on the database access and we will work with Dojo later.
Database configuration
In this version of our todo list, we will introduce a priority for every item on the to do list, which is reflected in the table that we will create. As mentioned previously, we will use MySql for this tutorial as the database of choice. If you already have the MySql server installed, you can use that instance. Otherwise, install an instance by using the instructions at the MySql Website. After you have the database set up, use the following steps to create a database.
- Open the MySql Command Line Client as shown on the following screen.
- Login by entering your password as shown on the following screen.
- Run the following commands:
CREATE DATABASE simpletodo;
USE simpletodo;
DROP TABLE IF EXISTS todos;
CREATE TABLE todos (
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
owner VARCHAR(32) NOT NULL,
item VARCHAR(128) NOT NULL,
priority TINYINT DEFAULT 2,
status TINYINT DEFAULT 0
);
INSERT INTO todos (owner, item, priority, status)
VALUES ('johndoe', 'learn Groovy', 0, 0),
('johndoe', 'code example in Groovy', 1, 0),
('johndoe', 'learn Ajax', 2, 1),
('johndoe', 'enhance example with Ajax', 0, 1),
('johndoe', 'go to Dojo party', 1, 0);
Note: You can copy this entire block of text and paste it into the command line window to run it, as shown on the following screen.
- Run the following command to verify that we have created and populated the table correctly:
SELECT * FROM todos;
The following screen should display:
The database is ready.
Zero configuration
The next step is to configure our Zero application to use the database and table that we just created. Configuration information for a Zero application is stored the zero.config file under the config folder. You can find details about how to configure a Zero application for data access in the Configuring data access article. For more information about the format of the zero.config file, refer to the Configuration article. Use the following steps to edit the zero.config file:
- Add the following contents to the
zero.config file:
[/app/db/simpletodo/config]
class=com.mysql.jdbc.jdbc2.optional.MysqlDataSource
serverName=localhost
databaseName=simpletodo
port=3306
user=root
password=mysqlpass
- Replace the dummy password mysqlpass with your password.
- Correct the other values to match your environment.
- Save the
zero.config file.
Working with the zero.config file
Zero uses a data manager that we can configure using the configuration file, and obtain a reference to in our scripts in order to access the database. The data manager provides an API for performing various operations as we will see later in this tutorial. We have provided the needed information in the zero.config file. To learn more about how data access works in a Zero application see the Data Access article.
Database access
The database is ready, and we have configured our application to access the database. Next we'll access the database that we created and configured. In the previous tutorial, we created a logical resource under the app/resources folder named todos. We will create a similar resource and modify it to use the database instead of storing the to do list in memory.
- Select the
app/resources folder.
- Click File -> New -> File and name the new file
todos.groovy
- Add the following contents to the todos.groovy file:
/*
* ============================================================================
* Licensed Materials - Property of IBM
* Project Zero
*
* (C) Copyright IBM Corp. 2007 All Rights Reserved.
*
* US Government Users Restricted Rights - Use, duplication or disclosure
* restricted by GSA ADP Schedule Contract with IBM Corp.
* ============================================================================
*/
package todos;
def onList() {
logger.INFO {"onGET called"};
sendJSONData(zero.data.Manager.create('simpletodo'));
}
def onCreate() {
logger.INFO {"onPOST called"};
def item = zero.util.XMLEncoder.escapeXML(request.params.item[0].get());
def priority = zero.util.XMLEncoder.escapeXML(request.params.priority[0].get());
logger.INFO {"Item to create: "+item};
if (item != null && item.size() > 0) {
def dm = zero.data.groovy.Manager.create('simpletodo');
def id = dm.insert("INSERT INTO todos (owner, item, priority, status) VALUES('johndoe', $item, $priority, 0)", ['id']);
request.status = HttpURLConnection.HTTP_CREATED;
request.headers.out.Location=request.status.get() + '/' + id
sendJSONData(dm);
}else {
request.status = HttpURLConnection.HTTP_NO_CONTENT;
}
}
def onRetrieve() {
logger.INFO {"onGET called"};
def id = zero.util.XMLEncoder.escapeXML(request.params.todosId[0].get());
if (id != null) {
def dm = zero.data.groovy.Manager.create('simpletodo');
def data = dm.update("SELECT id, item, priority, status FROM todos WHERE id = $id");
request.headers.out."Content-Type"="application/json"
request.view="JSON";
request.json.output = data;
render();
}
}
def onUpdate() {
logger.INFO {"onPUT called"};
def id = zero.util.XMLEncoder.escapeXML(request.params.todosId[0].get());
def status = zero.util.XMLEncoder.escapeXML(request.params.status[0].get());
if (id != null && status != null ) {
def dm = zero.data.groovy.Manager.create('simpletodo');
dm.update("UPDATE todos SET status = $status WHERE id = $id");
}
}
def onDelete() {
logger.INFO {"onDELETE called"};
def id = zero.util.XMLEncoder.escapeXML(request.params.todosId[0].get());
logger.INFO {"Item to delete: "+ id};
if (id != null) {
def dm = zero.data.groovy.Manager.create('simpletodo');
dm.update("DELETE FROM todos WHERE id = $id");
}
}
def sendJSONData(dm) {
def data = dm.queryList("SELECT id, item, priority, status FROM todos ORDER BY priority");
logger.INFO {"sending json data: "+data};
request.headers.out."Content-Type"="application/json"
request.view="JSON";
request.json.output = data;
render();
}
Working with the todos.groovy file
For this tutorial, we will assume the only user of our application to be johndoe. You might remember that all the entries that we created in the todo list table had the entry for johndoe as the owner. We will enhance this idea in the next tutorial but for now, johndoe is our only user.
- onList
- Calls the sendJSONData method. Notice the parameter sent to the sendJSONData method: zero.data.groovy.Manager.create('simpletodo'). This method call returns a Zero data manager that we talked about earlier. Why do we pass it the term 'simpletodo'? This matches the key that we used in the zero.config file to match our database. If you refer back, the configuration section started with [/app/db/simpletodo/config] where simpletodo is the key we used to identify the database description that followed. Thus, we can use the API provided by the Zero data manager now to access the database we created. For a complete listing of what operations can be performed using the data manager, see the API article.
- onCreate
- Retrieves the information about the item to be added to the list, obtains a reference to the data manager and executes a query using the data manager API to add the posted item to the todo list.
- onRetrieve
- Retrieves a specific item and returns it. Once again, you should notice the use of todosId to access individual items of our to do list collection. Once we have the id, we obtain a reference to the data manager and execute a query using the data manager API to retrieve the individual item. After that, we call the renderer as before.
- onUpdate
- is used to change the status of a to do list item. We obtain the id and status of the item before using the data manager to update its status. Notice how simple it is to use the data manager API to perform common operations.
- onDelete
- is used to delete a to do list item. We obtain the id of the item before using the data manager to delete it from the todo list. By now, you should be comfortable with the format of obtaining the id of a collection item and accessing it.
- Save the
todos.groovy file
We now have our data access set up. Let's create the Web resource that will access our todo list. This time, we will make use of the dojo toolkit.
Dojo toolkit
What is Dojo? Dojo is an Open Source DHTML toolkit written in JavaScript that provides various tools to ease the development process of a rich internet application. If you are not familiar with Dojo, you can find more information at the dojo toolkit Website. It can dramatically reduce the application development effort and provide easy tools for developing Web 2.0 applications. As before, we will use the todo.gt page for accessing the todo list.
- Select the public folder.
- Click File -> New -> File and name the new file todo.gt as shown on the following screen.
- Add the following contents to the todo.gt file:
<!--
/*
* ============================================================================
* Licensed Materials - Property of IBM
* Project Zero
*
* (C) Copyright IBM Corp. 2007 All Rights Reserved.
*
* US Government Users Restricted Rights - Use, duplication or disclosure
* restricted by GSA ADP Schedule Contract with IBM Corp.
* ============================================================================
*/
-->
<html>
<head>
<title>Todo List</title>
<link REL=StyleSheet HREF="todo.css" TYPE="text/css">
<script type="text/javascript" src="/dojo.js"></script>
<script type="text/javascript">
dojo.require("dojo.io.*");
dojo.require("dojo.widget.TitlePane");
dojo.hostenv.writeIncludes();
function todoCallback(type, data, evt) {
if (type == 'error')
alert('Error retrieving data from the server!');
else if (data != null)
getTodos();
}
function displayItems(data) {
var todolist = "";
var dellist = "";
var todolistSize = 0;
var dellistSize = 0;
for (var i=0; i<data.length; i++) {
var id = data[i].id;
var desc = data[i].item;
var priority = data[i].priority;
var status = (data[i].status == 0 ? false : true);
if (0 == priority)
priority = "high";
else if (1 == priority)
priority = "medium";
else
priority = "low";
var checked = (status ? "checked" : "");
var name = "todo"+i;
var item = '<input type="checkbox" name="'+name+
'" onclick="moveTodo('+name+')" value="'+id+'"'+checked+'>' +
'<SPAN CLASS="'+priority+'">' + desc + '</SPAN> <BR>';
//if status is true (checked), the item goes to delList
//otherwise, it remains in the todo list
if (status) {
dellist += item;
dellistSize++;
} else {
todolist += item;
todolistSize++;
}
}
dojo.widget.byId("todoPane").setLabel("Your todo items: "+todolistSize);
dojo.widget.byId("delPane").setLabel("Your completed items: "+dellistSize);
document.getElementById("todoList").innerHTML = todolist;
document.getElementById("delList").innerHTML = dellist;
}
function addTodo() {
var data = {item: document.todoForm.item.value,
priority: document.todoForm.priority.selectedIndex};
dojo.io.bind({
url: '/resources/todos',
handler: todoCallback,
method: "POST",
mimetype: "text/json",
content: data
});
document.todoForm.reset();
}
function deleteTodos() {
var list = document.delForm;
var size = list.length - 1;
for (var i=0; i<size; i++) {
dojo.io.bind({
url: '/resources/todos/'+list[i].value,
method: "POST",
headers: { 'X-Method-Override' : 'DELETE' },
mimetype: "text/json",
sync: true
});
}
dojo.io.bind({
url: '/resources/todos/'+list[size].value,
handler: todoCallback,
method: "POST",
headers: { 'X-Method-Override' : 'DELETE' },
mimetype: "text/json"
});
}
function moveTodo(checkbox) {
//var data = {id: checkbox.value, status: (checkbox.checked? 1: 0)};
dojo.io.bind({
url: '/resources/todos/'+checkbox.value,
handler: todoCallback,
method: "POST",
headers: { 'X-Method-Override' : 'PUT' },
mimetype: "text/json",
content: {status: (checkbox.checked? 1: 0)}
});
}
function getTodos() {
dojo.io.bind({
handler: getCallback,
url: '/resources/todos',
method: "GET",
mimetype: "text/json"});
}
function getCallback(type, data, evt) {
if (type == 'error')
alert('Error retrieving data from the server!');
else if (data != null)
displayItems(data);
}
dojo.addOnLoad(function() {
getTodos();
});
</script>
</head>
<body>
<div dojoType="TitlePane" label="Your todo items:" labelNodeClass="label" containerNodeClass="content" widgetId="todoPane">
<form name="todoForm">
<div id="todoList"></div>
<br>
<input type="text" id="item">
<select name="priority">
<option value="1">Priority one</option>
<option value="2">Priority two</option>
<option value="3">Priority three</option>
</select>
<input type="button" onClick="addTodo();" value="Add">
</form>
</div>
<div dojoType="TitlePane" label="Your completed items:" labelNodeClass="label" containerNodeClass="content" widgetId="delPane" open="false">
<form name="delForm">
<div id="delList"></div>
<br>
<input type="button" onClick="deleteTodos();" value="Delete">
</form>
</div>
</body>
</html>
Working with the todo.gt file
Since most of this code is Javascript and the Javascript methods have already been discussed in the To do list part II - REST and AJAX tutorial, let's concentrate on what has changed. There is no more usage of the XMLHttpRequest object, which is the backbone of asynchronous communication in the AJAX world. Instead, we see multiple dojo.* statements, the most important of which is dojo.io.bind. The dojo.io.bind call wrappers the traditional method of instantiating an XMLHttpRequest object and uses it for asynchronous communication. The url is invoked using the provided HTTP method method, and upon return, the handler method is called with the result. In our case, the todoCallback method gets called with the response. More information about the dojo.io.bind method can be found here.
- Save the
todo.gt file
Use the following steps to add some style information for our application to make it look better.
- Select the public folder.
- Click File -> New -> File and name the new file todo.css as shown on the following screen.
- Add the following contents to the todo.css file:
.high {
color:red;
font-weight:900;
}
.medium {
color:blue;
font-weight:600;
}
.low {
color:green;
font-weight:300;
}
.footer {
border-top:1px solid #DCDCDC;
clear:left;
color:#666666;
font-size:10px;
line-height:14px;
padding:10px 0pt;
text-align:left;
}
div.container {
background-color: white;
border: 2px solid black;
width: 300px;
height: 600px;
padding: 0px;
margin: 0px;
overflow: hidden;
}
.label , .content {
width: 400px;
}
.label {
background: #CCFF00;
height: 20px;
border-top: 1px solid black;
border-bottom: 1px solid black;
}
.content {
background: #eeeeee;
border-top: 1px solid #eeeeee; /* w/out this, an <h1> on the top line causes a gap between the .content and .label */
}
.innerLabel {
background: #CCFF00;
height: 20px;
border-top: 1px solid black;
border-bottom: 1px solid black;
width: 250px;
}
.innerContent {
background: #aaaaaa;
border-top: 1px solid #aaaaaa; /* w/out this, an <h1> on the top line causes a gap between the .content and .label */
width: 250px;
}
Working with the todo.css file
The todo.css file contains style information for various HTML tags used to render our to do list. We define a different color and font weight to the different priority items on our list in order to highlight the high priority items. We define various classes to enhance the looks of our to do list such as the label class to create tabs for the two sections of the to do list and the content class for creating a light gray background for the to do list. If you are not familiar with Cascaded Style Sheets (CSS), you can find more information here.
- Save
todo.css file.
Running the simpletodo3 application
To run the simpletodo3 application, do the following:
- Right-click the simpletodo3 project and click Run As -> Project Zero Application.
- Using your browser, go to the http://localhost:8080/todo.gt site.
Here is some sample output:
Try clicking the green tabs. The display expands and collapses the two sections dynamically. Pretty cool, huh?
<<< Previous | Next >>>
|