PSEC: Rudimentary GUI, Part I

SUMMARY: I need to be able to add tasks, which means I need to add stuff to the database when I click on a button and get the result back to the GUI so I can generate a new visual task object (VTask) on the screen.

I’m a bit fuzzy-headed on the exact steps I need to take, so let me talk my way through this:

  1. While I’ve gotten the server round-trip to work, capable of sending messages and receiving results, I actually need to consider how to initialize the space with VTasks.
  2. That means that I have to write the application “first time initialization” code.
  3. What does that mean? Well, the application state has to take into consideration what’s currently shown on the screen. If there is nothing, then there are no tasks available yet until you create them.
  4. A VTask can be displayed in a VTaskList, which is an ordered list of VTasks. VTasks contain a TaskID, some state, and the ability to draw themselves with HTML.
  5. Therefore, I need to create a button that allows me to create VTasks. Then everything else follows.

Ok, I made a new version of header.php called header-webapp.php, which gets rid of the extraneous header information.

I added a new div with the id psec-controls, which has a button in it with id addvtask.

Gotta wire-up the button. In second-main.js, which is the main code for this test page, I added the ‘click’ binding to #addvtask to call event handler dsAddVTask(), which is defined in view.js. It in turn calls Controller.AddVTask() defined in controller.js. Right now it just prints a message that it has been called…does it work? Yes.

Next, we need to write the code that actually adds a VTask. In this case it is really adding a NEW VTask, which means requesting both a new TaskID and adding it to the VTasks database. I created a new object called VDB which stores all VTasks. It has a function called Add(tid) which accepts a task ID. So where does this task ID come from? The Controller, which knows all! I added AddVTask() to the Controller, which retrieves the new taskID from Model.GetNewTaskID(), and then passes this back to the View via View.CreateVTaskFromTaskID(tid).

(Many hours pass)

I’ve had to reorganize the code a bit to handle the creation of tasks within the database side of things. I’ve implemented transactions for creating the new task IDs, which fortunately MySQL 5 supports. I’ve moved database functions into their own class, and am now loading it during AJAX calls.

Again, it strikes me at just how heavy-weight these AJAX calls seem to be…wouldn’t these be better handled by a small service running in the background of the server instead of PHP being reloaded for every request? Maybe it’s not as bad as I think.

I refactored the ajax response code on the server a bit. This led to an error in returning values, as I forgot to called the json_encode() function for the new “Nuke Tables” call. This call is designed to erase all the tables so I can start from scratch easily.

The asynchronous nature of some of these calls requires some tricky routing of callbacks and recognizing that anonymous functions can pass parameters that are valid during callback. If that doesn’t make sense, look at this code:

Controller = {
    AddVTask: function(e) {
        DBG.Out("Controller.AddVTask()");
        Model.AllocateNewTaskID ( function( task_id ) {
            DBG.Out("AddVTask: Got "+task_id);
        });
    }
}

Model = {
    AllocateNewTaskID: function ( cbfunc ) {
        DBG.Out("Model.GetNewTaskID:");
        var cmd = Dispatcher.GetNewCommandPacket ( CMD.NEWTASK );
        Dispatcher.QueueCommand ( cmd, function ( response ) {
            if (response.success) {
                // will this work? cbfunc was passed as a parameter, but this code is
                // executing as an anonymous function during a callback
                cbfunc(response.task_id);
            } else {
                DBG.Out("AllocateNewTaskID: Error creating new Task on Server!");
            }
        });
    }
}

The Controller object has a property that is a function called AddVTask(). This is bound to the “Add Task” button’s ‘click’ handler with jQuery. AddVTask() passes the responsibility on to Model.AllocateNewTaskID(), passing it an anonymous callback function that expects to received a task_id. The reason it is a callback function is because Model.AllocateNewTaskID() has to make a server request for a new task identification number, which is an asynchronous request. Therefore, we have to provide code that waits around for the return value to come back from the server:

  • When Model.AllocateNewTaskID() is called, the parameter cbfunc passed on line 11 (our callback function) has the value that we just passed.
  • The next line calls Dispatcher.QueueCommand(), which is what actually sends the AJAX request to the server.
  • When the function returns with a result, QueueCommand() calls another anonymous callback function (lines 15-21).
  • On line 18, we invoked the callback function (cbfunc) from Controller.AddVTask().

Here’s the mysterious thing: at the time the callback function is called, cbfunc somehow is still a valid reference. In other languages, since the callback function is executing LATER, cbfunc would not have been defined because its stack frame would not be available. This is one of the magical aspects of Javascript, known as closures. I seem to recall that this was a problem with Actionscript 2.0 when I was dealing with it, and it took a nuanced understanding of the this keyword in the context of callbacks and copying of object references to make it work.

Still confused? Well, don’t worry about it then. Just know that in Javascript, anonymous functions CAN access the variables of its containing function, even if that function is used as a callback. In other languages, this can be a pain in the ass, but not so in Javascript.

With that out of the way, I’m able to (finally) get a task_id back from the server. Tomorrow I’ll add the code that actually adds a VTask to a display list and shows it on the screen. Then, I’ll have to write something that SAVES changed data back to the database. Whew. Then I should have the rudiments of my TASK LIST PAGE, and can start thinking of how to expand it.

So close, yet so far.