Events

When working with complex data, it can often be useful to perform some additional action when Editor updates the database based on information from the client-side. Consider for example the following use case:

  • Modifying the Editor instance or form fields based on that action being taken
  • Deleting files from the file system when rows are removed
  • Logging information to a database about an action that a user has taken.

to name just a few!

To make this possible, the .NET Editor class emits a number of custom events, that can be subscribed to in the standard .NET event subscription manner.

Listening for events

Events listeners can be added using a standard event subscription in C# and the callback takes two arguments:

  1. The second object
  2. Event arguments. These arguments depend upon which event is being listened for.

No return value is used from the callback.

Multiple listeners for the same event can be added simply by subscripting multiple items. The events will be executed in the same sequence that they were added.

Available events

The Editor class will trigger the following events - the properties of the event arguments object are also documented:

  • PreGet (cancellable) - Triggered immediately prior to reading row data from the database (since 1.6)
    1. (Editor) Editor - The Editor instance that triggered the event
    2. (object) Id - Row id being selected if only a single row is to be selected (i.e. after an edit). Otherwise this value will be null indicating that all rows will be selected.
  • PostGet - Triggered immediately after row information has been read from the database (since 1.6)
    1. (Editor) Editor - The Editor instance that triggered the event
    2. (List<Dictionary<string, object>>) Data - Data list of rows that have been read from the database
    3. (object) Id - Row id being selected if only a single row is to be selected (i.e. after an edit).
  • PreCreate (cancellable) - Triggered immediately prior to creating a new row
    1. (Editor) Editor - The Editor instance that triggered the event
    2. (Dictionary<string, object>) Values - The values submitted by the client-side
  • WriteCreate - ata has been written to the database, but not yet read back, allowing the database to be updated before the data is gathered to display in the table (since 1.6.2)
    1. (Editor) Editor - The Editor instance that triggered the event
    2. (object) Id - ID of the newly created row
    3. (Dictionary<string, object>) Values - The values submitted by the client-side
  • PostCreate - Triggered immediately after a new row has been created
    1. (Editor) Editor - The Editor instance that triggered the event
    2. (object) Id - ID of the newly created row
    3. (Dictionary<string, object>) Values - The values submitted by the client-side
    4. (Dictionary<string, object>) Row - The newly created row's data, as read from the database
  • PreEdit (cancellable) - Triggered immediately prior to updating an existing row
    1. (Editor) Editor - The Editor instance that triggered the event
    2. (object) Id - ID of the row to be edited
    3. (Dictionary<string, object>) Values - The values submitted by the client-side
  • WriteEdit - Data has been written to the database, but not yet read back, allowing the database to be updated before the data is gathered to display in the table (since 1.6.2)
    1. (Editor) Editor - The Editor instance that triggered the event
    2. (object) Id - ID of the row to be edited
    3. (Dictionary<string, object>) Values - The values submitted by the client-side
  • PostEdit - Triggered immediately after an existing row has been updated
    1. (Editor) Editor - The Editor instance that triggered the event
    2. (object) Id - ID of the row that has been edited
    3. (Dictionary<string, object>) Values - The values submitted by the client-side
    4. (Dictionary<string, object>) Row - The updated row's data, as read from the database
  • PreRemove (cancellable) - Triggered immediately prior to deleting an existing row
    1. (Editor) Editor - The Editor instance that triggered the event
    2. (object) Id - ID of the row to be deleted
    3. (Dictionary<string, object>) Values - The values submitted by the client-side (i.e. the row's data set)
  • PostRemove - Triggered immediately after a row has been deleted
    1. (Editor) Editor - The Editor instance that triggered the event
    2. (object) Id - ID of the row that has been deleted
    3. (Dictionary<string, object>) Values - The values submitted by the client-side (i.e. the row's data set)
  • PreUpload (cancellable) - Triggered immediately prior to a file upload being processed (since 1.6.2)
    1. (Editor) Editor - The Editor instance that triggered the event
    2. (DtRequest) Data - Data submitted by the upload form
  • PostUpload - Triggered after a file has been uploaded and information about it read from the database (since 1.6.2)
    1. (Editor) Editor - The Editor instance that triggered the event
    2. (object) Id - ID of the database record for the uploaded file (or the file's unique name if a database record is not used)
    3. (Dictionary<string, Dictionary<string, Dictionary<string, object>>>) Files - The file information that has been read from the database
    4. (DtRequest) Data - Data submitted by the upload form

Please note that for multi-row creation, editing and deletion, the events are triggered once for each row.

Cancellable events

The pre* events are all cancellable as of Editor 1.6, allowing the server-side process to optionally decide if a row should be processed or not. This can be useful as a form of data validation, disallowing users from certain actions (although please note that this is not a replacement for validation - no error will be shown to the end user if the processing of a row is cancelled!).

To cancel the processing of a row, set the Cancel property of the arguments parameter (second parameter passed into the event handler) to be true. Any other rows that were also submitted, which are not cancelled themselves, will be correctly processed and the client-side table appropriately updated.

Examples

Modifying fields

It is quite common to wish to store information in the database that is not directly settable by the end user, and furthermore, to have different information stored based on the action that has been triggered from the client-side. This can easily be achieved with events and the methods of the Field class.

Consider the case where we have information about the user who is using the session available in a session variable (Session["user_id"]). We wish to store in the database this information for who created the entry and who last updated the item.

This can be achieved using the preCreate and preEdit methods:

Controller:

public class StaffController : ApiController
{
    [Route("api/staff")]
    [HttpGet]
    [HttpPost]
    public IHttpActionResult Staff()
    {
        var request = HttpContext.Current.Request;
        var settings = Properties.Settings.Default;

        using (var db = new Database(settings.DbType, settings.DbConnection))
        {
            var editor = new Editor(Db, "staff")
                .Model<StaffModel>();

            editor.Field("created_by")
                .Set( Field.SetType.Create );
            editor.Field("last_updated_by")
                .Set( Field.SetType.Edit );

            editor.PreCreate += (sender, e) =>
                editor.Field("created_by").SetValue(Session["user_id");
            editor.PreEdit += (sender, e) =>
                editor.Field("last_updated_by").SetValue(Session["user_id");

            return Json(
                editor.Process(request).Data()
            );
        }
    }
}

Model:

namespace WebApiExamples.Models
{
    public class StaffModel
    {
        public string first_name { get; set; }
        public string last_name { get; set; }
        public string position { get; set; }
        public string email { get; set; }
        public string office { get; set; }
        public string created_by { get; set; }
        public string last_updated_by { get; set; }
    }
}

For the controller, line by line:

  • Line 1-14: Standard WebAPI controller class set up and initialisation of Editor class, using the StaffModel model. Note how the Editor instance is assigned to a variable so we can attach the events to it (most other examples on this site don't require this).
  • Lines 16-17: created_by is configured to be written for create type requests only
  • Lines 18-19: Similarly for last_updated_by as a write on edit only field
  • Lines 21-22: Add a PreCreate event listener with a lambda function that sets the value to a session variable on create.
  • Lines 23-24: Likewise for the update field on PreEdit.
  • Lines 26-28: Process the request and return the JSON response.

Logging changes

As a further example of how to use the events emitted by Editor, consider the case where we want to log information to a database table that details what user made a change, what the change was and when. For this we can use use either the Pre* or Post* events, but we will use the Post* events here which ensures that data is logged only if it was successfully inserted into the database.

Consider the following code:

public class StaffController : ApiController
{
    [Route("api/staff")]
    [HttpGet]
    [HttpPost]
    public IHttpActionResult Staff()
    {
        var request = HttpContext.Current.Request;
        var settings = Properties.Settings.Default;

        using (var db = new Database(settings.DbType, settings.DbConnection))
        {
            var editor = new Editor(Db, "staff")
                .Model<StaffModel>();

            editor.PostCreate += (sender, e) => _LogChange(db, "create", e.Id, e.Values);
            editor.PostEdit += (sender, e) => _LogChange(db, "create", e.Id, e.Values);
            editor.PostRemove += (sender, e) => _LogChange(db, "create", e.Id, e.Values);

            return Json(
                editor.Process(request).Data()
            );
        }
    }

    private void _LogChange(Database db, string action, object id, Dictionary<string, object> values)
    {
        db.Insert("staff-log", new Dictionary<string, object>{
            { "user", Session["user_id"] },
            { "action", action },
            { "values", JsonConvert.SerializeObject( values ) },
            { "row", id }
        });
    }
}
  • Line 1-14: Again, standard WebAPI controller class set up and initialisation of Editor class, using the StaffModel model.
  • Lines 16-18: Attach listeners to the PostCreate, PostEdit and PostRemove events which will call the _LogChange method with suitable arguments.
  • Lines 20-22: Process the data and return JSON to the client
  • Lines 28-33: Use the Database class to insert a log record to the staff-log table.

.NET API documentation

The .NET API developer documentation for the Editor .NET classes is available for detailed and technical discussion about the methods and classes discussed above.