|
|
|
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 the Derby Embedded database though the code will work with MySql or the network version of Derby. 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 Source tab in the editor, add the following lines in the dependencies section, and save the file.
<dependency org="dojo" name="dojo" rev="0.9"/>
<dependency name="zero.data" org="zero" rev="1.0+"/>
<dependency name="zero.data.setup.webtools" org="zero" rev="1.0+"/>
<dependency name="derby" org="org.apache.derby" rev="10.3+"/>
<dependency name="derbyclient" org="org.apache.derby" rev="10.3+"/>
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.
- 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 embedded Derby for this tutorial. Use the following steps to setup the database.
- Right-click the simpletodo3 project, select New -> Folder and name the new folder
sql. Right-click the sql folder and select New -> Folder to create a derby folder underneath the sql folder. We will store our database scripts in this folder. We need to create 3 SQL scripts to create the table, drop the table, and insert sample data into the table.
- Right-click the
derby folder, select New->File and name the file create.sql. Copy the following SQL statement into the file, and save it:
CREATE TABLE todos (
id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
owner VARCHAR(32) NOT NULL,
item VARCHAR(128) NOT NULL,
priority INTEGER DEFAULT 2,
status INTEGER DEFAULT 0,
PRIMARY KEY (id)
);
- Right-click the
derby folder, select New->File and name the file drop.sql. Copy the following SQL statement into the file, and save it:
- Right-click the
derby folder, select New->File and name the file sample.sql. Copy the following SQL statement into the file, and save it:
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);
- Right-click the simpletodo3 project and click Run As -> Project Zero Application.
- Using your browser, go to the http://localhost:8080/setup.
- Enter the database name simpletodo.
- Select Apache Derby (Embedded) for database type
- Click on Create Tables as shown below
- When the Create Tables has successfully returned, click on Add Sample Data. You should see the following on the Eclipse Console:
- Go back to Eclipse and stop the simpletodo3 application
The database is ready. These steps create the data.config file and includes it in the zero.config with an @include statement. 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.
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
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:
/*
* ============================================================================
* (C) Copyright IBM Corporation 2006, 2007. All Rights Reserved.
*
* You may only copy, modify, and distribute these samples internally.
* These samples have not been tested under all conditions and are provided
* to you by IBM without obligation of support of any kind.
*
* IBM PROVIDES THESE SAMPLES "AS IS" SUBJECT TO ANY STATUTORY WARRANTIES THAT
* CANNOT BE EXCLUDED. IBM MAKES NO WARRANTIES OR CONDITIONS, EITHER EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OR
* CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
* NON-INFRINGEMENT REGARDING THESE SAMPLES OR TECHNICAL SUPPORT, IF ANY.
* ============================================================================
*/
def onList() {
logger.INFO {"onGET called"};
sendJSONData(zero.data.Manager.create('simpletodo'));
}
def onCreate() {
logger.INFO {"onPOST called"};
def item = request.params.item.get();
def priority = request.params.priority.get();
logger.FINEST {"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 = request.params.todosId.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 = request.params.todosId.get();
def status = request.params.status.get();
logger.INFO {"todo ID = $id status(1 or 0) = $status" };
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 = request.params.todosId.get();
logger.FINEST {"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.FINEST {"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 [/config/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:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<!--
/*
* ============================================================================
* (C) Copyright IBM Corporation 2006, 2007. All Rights Reserved.
*
* You may only copy, modify, and distribute these samples internally.
* These samples have not been tested under all conditions and are provided
* to you by IBM without obligation of support of any kind.
*
* IBM PROVIDES THESE SAMPLES "AS IS" SUBJECT TO ANY STATUTORY WARRANTIES THAT
* CANNOT BE EXCLUDED. IBM MAKES NO WARRANTIES OR CONDITIONS, EITHER EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OR
* CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
* NON-INFRINGEMENT REGARDING THESE SAMPLES OR TECHNICAL SUPPORT, IF ANY.
* ============================================================================
*/
-->
<html>
<head>
<title>Todo List</title>
<link rel="StyleSheet" href="todo.css" type="text/css">
<script type="text/javascript"
src="<%= getRelativeUri("/dojo/dojo.js.uncompressed.js") %>"
djConfig="parseOnLoad: true, isDebug: false">
</script>
<script type="text/javascript">
dojo.require("dojo.parser");
dojo.require("dijit.TitlePane");
var todoURL = '<%= getRelativeUri("/resources/todos") %>';
function onError(response) {
alert(response);
}
function displayItems(response) {
var todolist = "";
var dellist = "";
var todolistSize = 0;
var dellistSize = 0;
for (var i=0; i<response.length; i++) {
var id = response[i].id;
var desc = response[i].item;
var priority = response[i].priority;
var status = (response[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++;
}
}
var todoPane=dijit.byId("todoPane");
todoPane.setTitle("Your todo items: "+todolistSize);
var delPane=dijit.byId("delPane");
delPane.setTitle("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};
var req = dojo.xhrPost({
url: todoURL,
content: data,
sync: false
});
req.addCallbacks(getTodos, onError);
document.todoForm.reset();
}
function deleteTodos() {
var list = document.delForm;
var size = list.length - 1;
for (var i=0; i<size; i++) {
var req = dojo.xhrDelete({
url: todoURL + '/' +list[i].value,
sync: true
});
}
// do this as two separate requests so that we can
// invoke the callback only when all deletes are finished
var req = dojo.xhrDelete({
url: todoURL + '/' +list[size].value,
sync: false
});
req.addCallbacks(getTodos, onError);
}
function moveTodo(checkbox) {
var req = dojo.xhrPut({
url: todoURL + '/'+ checkbox.value,
content: {status: (checkbox.checked? 1: 0)},
sync: false
});
req.addCallbacks(getTodos, onError);
}
function getTodos() {
var req = dojo.xhrGet({
url: todoURL,
handleAs: 'json-comment-optional',
sync: false
});
req.addCallbacks(displayItems, onError);
}
dojo.addOnLoad(function() {
getTodos();
});
</script>
</head>
<body>
<div dojotype="dijit.TitlePane" title="Your todo items:"
id="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="dijit.TitlePane" title=
"Your completed items:" id="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 are the dojo.xhr* calls. The dojo.xhr* calls wrapper the traditional method of instantiating an XMLHttpRequest object and uses it for asynchronous communication. The url is invoked using the provided HTTP method, and upon return, the registered callback is called with the result. More information about the Dojo IO transports 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;
}
div.container {
background-color: white;
border: 2px solid black;
width: 300px;
height: 600px;
padding: 0px;
margin: 0px;
overflow: hidden;
}
.dijitTitleNode , .dijitTitlePaneContentOuter {
width: 400px;
}
.dijitTitleNode {
background: #CCFF00;
height: 20px;
border-top: 1px solid black;
border-bottom: 1px solid black;
}
.dijitTitlePaneContentOuter {
background: #eeeeee;
border-top: 1px solid #eeeeee; /* w/out this, an <h1> on the top line causes a gap between the .content and .label */
}
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. 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
Here is some sample output:
Try clicking the green tabs. The display expands and collapses the two sections dynamically. Pretty cool, huh?
<<< Previous | Next >>>
|