This document supposed to explain all the capabilities of JayData related to event handlers. All the statements are valid in the entire JayData ecosystam (JayData library, JayData Server, JayData Server Pro).
JayData provides a rich set of tools to build application logic around data-related events and implement triggers easily, just as site builders manage event handlers of DOM elements.
To understand the events, we will take a simplified Northwind data scheme as example and demonstrate with the Products and Categories tables, so let’s start with the definition of these entity types and the data context for JayData.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<span class="rem">//Define the Category entity with the Products array property</span> $data.Entity.extend(<span class="str">"Category"</span>, { Id: { key: <span class="kwrd">true</span>, type: <span class="str">"int"</span>, computed: <span class="kwrd">true</span> }, Name: { type: <span class="str">"string"</span>, required: <span class="kwrd">true</span> }, Products: { type: Array, elementType: <span class="str">"Product"</span>, inverseProperty: <span class="str">"Category"</span> } }); <span class="rem">//Define the Product entity with the Category reference</span> $data.Entity.extend(<span class="str">"Product"</span>, { Id: { key: <span class="kwrd">true</span>, type: <span class="str">"int"</span>, computed: <span class="kwrd">true</span> }, Name: { type: <span class="str">"string"</span>, required: <span class="kwrd">true</span> }, Category: { type: <span class="str">"Category"</span>, inverseProperty: <span class="str">"Products"</span> } }); <span class="rem">//Define our context with typed EntitySets (tables), which will provide access to our database</span> $data.EntityContext.extend(<span class="str">"NorthwindContext"</span>, { Categories: { type: $data.EntitySet, elementType: Category }, Products: { type: $data.EntitySet, elementType: Product } }); <span class="rem">//Initialize a WebSQL/SQLite database connection with the defined schema</span> var nw = <span class="kwrd">new</span> NorthwindContext({ provider: <span class="str">"webSql"</span>, databaseName: <span class="str">"northwind"</span> }); nw.onReady(function () { var newDep = <span class="kwrd">new</span> Category({ Name: <span class="str">'Food'</span> }); nw.Categories.add(newDep); nw.saveChanges(function () { console.log(<span class="str">'New category has been changed'</span>); }); }); |
Using the snippet above JayData persists a new category to our in-browser WebSQL database. We will add events to this fist version of the code in the following sections.
Developers can define event handlers on four different levels:
- EntityContext – If we define the event handlers on the EntityContext level, our event handler will be called every time we CRUD any instances of any types in any EntitySets.
- EntitySet – We can assign event handlers to the Products EntitySet, in this case only CRUD actions to the Product EntitySet will call the event handlers.
- Entity Type – Declaring an event handler on the Product type, actions will call it while CRUD-ing the Products or the OldProduct EntitySet.
- Entity Instance – Yes, event handlers can be assigned even to a single entity instance.
|
EntityContext |
EntitySet |
Entity Type |
Entity instance |
Cancellable |
No |
Yes (before events) |
Yes (before events) |
No |
Allows entity modifications |
No |
Only changed fields |
Yes |
No |
Multiple event subscribers |
Yes |
No |
Yes |
Yes |
EntityContext-level events
Available events:
- ‘added’
- ‘updated’
- ‘deleted’
All these events are raised after the underlying actions.
Subscribing to the events
1 2 3 4 5 6 7 8 9 10 11 |
function onCreate(sender, eventData){ console.log(<span class="str">'eventdata'</span>, eventData); console.log(<span class="str">'Product name'</span>, eventData.data.Name); console.log(<span class="str">'Product Id'</span>, eventData.data.Id); } function onCreate2(sender, eventData){ console.log(<span class="str">'oncreate 2'</span>); } nw.addEventListener(<span class="str">'added'</span>, onCreate); nw.addEventListener(<span class="str">'added'</span>, onCreate2); |
Unsubscribing from events
1 |
nw.removeEventListener(<span class="str">'updated'</span>, onUpdate); |
Remarks:
- You can add multiple event subscribers with the same type, all of them will be fired
- Event handlers does not get all the fields of the entity
- added event handler get the fields set before + default values + new id
- updated event handler get the changed properties
- deleted event handler get only the Id in the itemData.data.Id property
EntitySet-level events
Available events:
- beforeCreate / afterCreate
- beforeRead / afterRead
- beforeUpdate / afterUpdate
- beforeDelete / afterDelete
Subscribing to the event:
1 2 3 4 5 6 7 8 9 10 11 |
function beforeCreateHandler(items) { console.log(<span class="str">'beforeCreateHandler'</span>); items.forEach(function (element) { console.log(<span class="str">'element: '</span>, element); }); } $data.EntityContext.extend(<span class="str">"NorthwindContext"</span>, { Categories: { type: $data.EntitySet, elementType: Category }, Products: { type: $data.EntitySet, elementType: Product, beforeCreate: beforeCreateHandler } }); |
Remarks:
- Event handlers get an array of items, if you CRUD only a single entity, it will be in the first element of the array
- Event handlers does not get all the fields of the entity
- beforeCreate and afterCreate events get only the fields set to the new entity
- beforeRead and afterRead events get the underlying EntitySet and the query
- beforeUpdate and afterUpdate events get the changed properties
- beforeDelete and afterDelete events get the Id of the entity
Cancelling the events
Synchronous cancellation:
1 2 3 4 5 |
function beforeCreateHandler(items) { <span class="kwrd">if</span> (...){ <span class="kwrd">return</span> <span class="kwrd">false</span>; } } |
Asynchronous cancellation:
1 2 3 4 5 6 7 8 9 |
function beforeCreateHandler(items) { <span class="kwrd">return</span> function(callback, items){ items.forEach(function(item){ <span class="kwrd">if</span> (item.Name == <span class="str">'Beer'</span>){ callback(<span class="kwrd">false</span>); } }) }; } |
Entity type-level events
Available events:
- beforeCreate / afterCreate
- beforeUpdate / afterUpdate
- beforeDelete / afterDelete
Subscribing to the event:
1 2 3 4 5 6 7 |
function beforeCreate(sender, entity) { console.log(<span class="str">'entity'</span>, entity); } function beforeCreate2(sender, entity) { console.log(<span class="str">'entity once again'</span>, entity); } |
Unsubscribing from the event:
1 |
nw.Products.elementType.removeEventListener(<span class="str">'beforeCreate'</span>, beforeCreate); |
Cancelling the event
1 2 3 4 5 6 |
function beforeCreate(sender, entity) { <span class="kwrd">if</span> (entity.Name == <span class="str">'Beer'</span>){ console.log(<span class="str">'No beer today'</span>); <span class="kwrd">return</span> <span class="kwrd">false</span>; } } |
Remarks:
- You can add multiple event subscribers with the same type, all of them will be fired
- Event handlers does not get all the fields of the entity
- beforeCreate and afterCreate events get only the fields set to the new entity beforeUpdate and afterUpdate events get the changed properties
- beforeDelete and afterDelete events get the Id of the entity
Entity instance-level events
JayData lets you to track all changes of object properties.
Available events
- propertyChanging
- propertyChanged
- propertyValidationError
Subscribing to the events
1 2 3 4 5 6 7 8 9 10 11 |
var newProduct = <span class="kwrd">new</span> Product({ Name: <span class="str">'Beer'</span> }); function onPropertyChangeHandler(sender, eventData){ console.log(<span class="str">'name of changed property'</span>, eventData._propertyName); console.log(<span class="str">'old value'</span>, eventData._oldValue); console.log(<span class="str">'new value'</span>, eventData._newValue); } newProduct.propertyChanging.attach(onPropertyChangeHandler); newProduct.Name = <span class="str">'Beer2'</span>; |
Unsubscribing from the event
1 |
newProduct.propertyChanging.detach(onPropertyChangeHandler); |
Remarks:
- You can add multiple event subscribers with the same type, all of them will be fired. Yo can achive this by multiple attach() instructions.
- You event hander will get the eventData, which contains the _propertyName, _oldValue and _newValue properties.
General behaviors
Using the context to query the database
All event handlers has got access to the database through the this keyword, which returns the current context. It’s recommended to set the value of the this to a new variable (we use self).
1 2 3 4 5 6 7 8 |
function beforeCreateHandlerWithRead(items) { var self = <span class="kwrd">this</span>; <span class="rem">//this is the context</span> items.forEach(function (element) { self.Categories.filter(...).toArray(function(c){ <span class="rem">//use the results of the category query </span> }); }); } |
Did you like this article? Do you want to try these event handlers in action? Download the latest version JayData library or check out JayData server for node.js.