Adding Tasks

Up until now we have been working with the data that we initially populated the database with at the beginning of the tutorial. Now its time to add the ability to add a brand task.

On the previous page we added an EditTasksDialog dialog with which we can edit an existing task's data, we are going to re-use that dialog for adding new tasks.

Adding a Task

The first step is to add two helper methods to our DataModule class, they're job will be to create a new DataRow class and discard it should the user cancel adding the new task.

Open DataModule.java and add a method called createNewRow. It uses the factory method addNewRow of DataTable which creates a new DataRow object and adds it to the table.

public DataRow createNewRow() {
  DataRow newRow = this.tasksTable.addNewRow();

  return newRow;
}

The new DataRow object is configured to match a row of the Tasks table, but it is not pre-initalised with any data. As we are using both a lookup field and a calculated field it is important that we add some initial data otherwise the JTable will throw an exception when it attempts to render.

```java public DataRow createNewRow() { DataRow newRow = this.tasksTable.addNewRow();

// pre-populate the data newRow.setField("DueDate", new Date()); newRow.setField("Task", ""); newRow.setField("Details", "");

// we need to particularly pre-populate the following entries // as they are used for the Lookup Column and Calculated Column newRow.setField("Done", (long)0); newRow.setField("Priority", (long)2);

return newRow; }

If the user hits cancel on the EditTaskDialog we want to remove the new row from the table. We can achieve that by calling the cancelChangesForRow method of DataTable passing in the row we want to discard.

public void discardRow(DataRow aRow) {
  this.tasksTable.cancelChangesForRow(aRow);
}

Now swap to TodoApp.java and add a new ActionEventListener handler to the initListeners method, this time to the Add Task button.

btnAddTask.addActionListener(actionEvent -> {
});

The first step is to get a new DataRow object using our new createNewRow method.

btnAddTask.addActionListener(actionEvent -> {
  DataRow newRow = this.dataModule.createNewRow();  
});

Now instantiate the EditTaskDialog we created in the last page, configure the title to say Add New Task and pass in the new DataRow object.

btnAddTask.addActionListener(actionEvent -> {
  DataRow newRow = this.dataModule.createNewRow();

  EditTaskDialog editDialog = new EditTaskDialog();
  editDialog.setLocationRelativeTo(this);
  editDialog.setTitle("Add New Task");
  editDialog.setData(newRow);
  editDialog.setVisible(true);

});

Finally check to see if the user canceled the dialog or not. If they did, then discard the new data row as they are obviously not interested in it. Otherwise set the data row to the values in the various UI fields.

btnAddTask.addActionListener(actionEvent -> {
  // snipped code

  if (!editDialog.wasDialogCancelled()) {
    newRow = editDialog.getData();
  } else {
    this.dataModule.discardRow(newRow);
  }
});

Now when you run the add, press the Add Task button and a new task is added to the table and the EditTaskDialog is displayed.

Running app showing the EditTaskDialog to add a new task

Problem

It is not currently obvious but your app now has a major problem. All new tasks are being assigned the user Id of 0, and not the Id of the logged in user.

SQL data from which shows the new task with User Id set to 0

To fix this we need to make a change to the relativity server.

Solution

All changes that are made in the client get passed to Relativity Server inside the Delta objects. There is one delta object per table. Each delta contains one or more changes. Each delta change contains the old (original) and new (modified) values. Within Relativity Server’s business rule scripting model, you can get access to the deltas and changes before the Relativity Server processes them. Using techniques similar to how you filtered data coming from the Relativity Server, you can now ensure that each new Task has its User field set to the ID of the current user before the Relativity Server tries to post it.

Schema Modeler

Go back to the Relativity Admin Tool and open the Schema Modeler. Expand the SCHEMA tree node and select the Scripts entry. Using the Add Event button, add the beforeProcessDeltaChange script handler.

As its name suggests, the beforeProcessDeltaChange event will be called just before processing a particular change from the delta object for the given table. In this script, you just need to obtain the current user ID from the session and update the delta change User field with its value.

// Called before each change is applied to the database
function beforeProcessDeltaChange(delta, change, wasRefreshed, canRemove)
{
  var userId = session['Login.Id'];
  change.newValues['User'] = userId;
  canRemove = false;
}

Back in your running application, try to apply the changes again and hopefully all will work.

An Improved Solution

Even though the application now works, there is a problem. The beforeProcessDeltaChange script will be run for every change in every delta before it gets processed. If you were to ever start adding records for another table in addition to the Tasks table, this script would try to update the User field on that table as well. You need to solve this by ensuring the delta change is for the Tasks table before you try to set the User field.

// Called before each change is applied to the database
function beforeProcessDeltaChange(delta, change, wasRefreshed, canRemove)
{
  if (delta.name == "Tasks") {
    var userId = session['Login.Id'];
    change.newValues['User'] = userId;
    canRemove = false;
  }
}

Records that are being updated should already have the User field set from when the record was added, so although it doesn't hurt to set it again, it's not very clean or efficient, so you might want to ensure that the script only sets the User field when the delta is for an insert.

// Called before each change is applied to the database
function beforeProcessDeltaChange(delta, change, wasRefreshed, canRemove)
{
  if (delta.name == "Tasks" && change.isInsert) {
    var userId = session['Login.Id'];
    change.newValues['User'] = userId;
    canRemove = false;
  }
}

Now when you look at the data in the SQL Database you can see that the new task has a user ID 1 which matches the user we are testing with (Alex).

SQL data showing the User Id is fixed

We've fixed that issue, unfortunately it exposes another issue; namely that the app currently retrieves all the tasks regardless of which user is logged in.

We will fix that issue in a moment in Server-Side Scripting. First we will make it possible to delete a task so we can delete the task with the User Id of 0.