Telerik blogs

JayData is a JavaScript data manager library that provides a friendly, unified developer experience over a number of different online and offline data APIs and data sources like IndexedDB, WebSQL or OData. For local databases, JayData provides automatic database creation and schema management (keys, indices). For OData services, JayData supports the translation of the service metadata document into an intelligent client environment where you can interact with data naturally using JavaScript.

Why use JayData with Kendo UI

JayData lets you to connect the rich feature-set of Kendo UI controls and the core framework with a number of popular data store options around you. With zero or minimal plumbing you can plug in CRUD capable connectivity to local data APIs like IndexedDB or WebSQL, and online endpoints that support OData or the ASP.NET WebAPI. Kendo UI with JayData just works.

Figure1: This is a sample Kendo UI Grid that manages tasks.  Data can be store in many different places.

Figure 2: Data stored in WebSQL 

Figure 3: Data stored in IndexedDB

Figure 4: Data stored on the server, published with OData

Getting Started With JayData And Kendo UI

You need to include the following scripts to unleash the magic. 

<script src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
<script src="http://cdn.kendostatic.com/2012.3.1315/js/kendo.all.min.js"></script>
<script src="http://include.jaydata.org/datajs-1.0.3.js"></script>
<script src="http://include.jaydata.org/jaydata.js"></script>
<script src="http://include.jaydata.org/jaydatamodules/kendo.js"></script>
<link href="http://cdn.kendostatic.com/2012.3.1315/styles/kendo.common.min.css" rel="stylesheet" type="text/css" />
<link href="http://cdn.kendostatic.com/2012.3.1315/styles/kendo.default.min.css" rel="stylesheet" type="text/css" />

Make sure you have a valid Kendo UI license, even when using the CDN.

Define a simple model

To get started interacting with a local data store using JayData, we first need to create a model. The simplest way to do that in JayData is using the $data.define method. This accepts a model name and a model definition and returns the JavaScript class function for the specified entity type. These types are how JayData interacts with the database.

Our example Task model in the beginning contains only two simple fields. None of them are mandatory.

var Task = $data.define("Task", {
    Todo: String,
    Completed: Boolean
});

Interactive Sample

That is all to it. You can start storing Task data locally. In JayData every model type has a default local database configured - either WebSQL or IndexedDB depending on device / browser capabilities.

Also you can set this default store to be an OData feed or an ASP.NET WebAPI controller. You might want to read more on the ItemStore API.

Beyond JavaScript primitive types such as Boolean, String, Date and Number, you can use a wide set of more complex field types like Array, $data.GeographyPoint or another entity type to create a complex field or a foreign entity relation. These are out of the scope of this post.

Auto CRUD the local database with a Kendo Grid

JayData has multiple integration points for Kendo UI. Arguably the most important one is the ability to provide you with Kendo UI DataSource instances from a JayData entity type. To get a fully configured DataSource instance that retrieves items and creates new rows in the default local store of the given entity type simply, use type.asKendoDataSource()

<body>
    <div id="grid"></div>
    <script>
        var Task = $data.define("Task", {
            Todo: String, 
            Completed: Boolean
        });
        $('#grid').kendoGrid({
            dataSource: Task.asKendoDataSource(),
            filterable: true,
            sortable: true,
            pageable: true,
            editable: 'popup',
            toolbar: ['create', 'save']
        });
    </script>
</body>

Interactive Sample

CRUD the local database with MVVM and a ListView

var Task = $data.define("Task", {
    Todo: { type: String, required: true },
    Completed: Boolean
});

var viewModel = kendo.observable({
    taskSource: Task.asKendoDataSource(),
    addNew: function () { 
     $('#listView').getKendoListView().add();
    },
});

kendo.bind($("#container"), viewModel);

Interactive Sample

To learn more about the Kendo UI DataSource, read this fine post from the Kendo team.

Overriding The Default Local Data Store

By default, JayData configures a local database behind every Entity type. This default store is using the so called local provider, which decides what the backing store should be based on device / browser capabilities. You can override this manually by selecting the provider property in the type configuration to webSql or indexedDb.

WebSQL

//This is the default option if the client supports WebSQL. 
$('#grid').kendoGrid({
    dataSource: Task.asKendoDataSource({ provider: 'webSql', databaseName:'TaskDB' }),
    ...
}); 

As with WebSQL, JayData handles the duties of creating or updating the local database schema (collections, indices) for IndexedDB.

IndexDB

$('#grid').kendoGrid({
    dataSource: Task.asKendoDataSource({ provider: 'indexedDb', databaseName:'TaskDB' }),
    ...
});

Working With OData And WebAPI

You can also have the provider be an OData service or WebAPI controller.

OData

As with WebSQL or IndexedDB, JayData supports the full set of CRUD operations over the OData protocol. Your model definition must be compatible with the schema defined by the service metadata. The url property specifies the endpoint of the OData service.

$('#grid').kendoGrid({
    dataSource: Task.asKendoDataSource({ provider: 'oData', url: '//your.svc/Tasks'}),
    ...
});

Interactive Sample

With OData, you can also have the JayData library infer model types from the service metadata. This can spare you a lot of work; especially when it comes to large, or constantly changing models.

You can create a read/write OData service with WCF Data Services and Entity Framework, or you could create an OData service with just JavaScript and MongoDB. You might even choose to build your own OData service in the cloud using JayStack since it has a super friendly and easy to use interface.

ASP.NET WebAPI

You can use the default ASP.NET WebAPI Controllers to create a simple, query capable store API that persists its data with Entity Framework.

Then you just need to specify the provider as WebAPI.

$('#grid').kendoGrid({
    dataSource: Task.asKendoDataSource({ provider: 'webApi', url: '//api/Tasks'}),
    ...
});

 Taking Control Of Fields

You might have noticed in the local database CRUD interactive sample that an Id column is also present in the listings. This is because entities must have some data that uniquely identifies them, and if you don’t designate the key, JayData will append an auto incrementing integer key for you. The following code defines a string key that is assigned by the user (instead of being an auto increment value). It also makes the Todo field required and assigns a defaultValue to a DueDate field.

var Task = $data.define("Task", {
    Id: { type: String, key: true },
    Todo: { type: String, required: true },
    Completed: Boolean,
    DueDate: { 
        type: Date, 
        defaultValue: new Date() 
    }
});

Interactive Sample

On top of type and required, JayData supports the following field configurations:

  • computed
  • minValue
  • maxValue
  • nullable
  • elementType
  • pattern

Not Just CRUD

On top of creating or editing data, the JayData library combined with the Kendo UI DataSource lets you to filter, sort or page data efficiently – all happening in the local database or on the server.

With WebSQL and IndexedDB this means that the filter statement invoked on a data source instance will actually be compiled into a SQL statement or a cursor operation.

With OData or WebAPI a properly formatted OData query is built and transmitted to the server.

I've setup a working interactive sample so that you can play with this a bit. Try setting some filtering or sorting on the grid. Make sure to watch the console so you can see the requests containing all the filtering/sorting parameters.

Setting DataSource properties

The almighty asKendoDataSource method accepts the standard Kendo UI DataSource options object. If unspecified by the developer, JayData sets the serverFiltering, serverSorting and serverPaging flags to true and sets a default pageSize of 25.

Lets talk about the batch setting for a moment. This controls the behavior of the pending change operations. The default value is false. Setting batch to true will cause the local providers to save the entire change set in one transactions. With the OData provider, the batch setting will switch from solo REST calls to OData $batch protocol providing you better overall completion time when it comes to creating multiple entities. WebAPI does not support batch operations.

Working With Complex Models, Tables and Databases

Beyond very simple cases like a shopping cart or a basic task list, we will need more than just sole entity types. For instance, we might like to have drill down features, master detail listings, drop downs based on domain data, etc. In order to handle these more complex scenarios, we need a container to collect types that are to be stored at the same place. The container holds types similar to the way a database is a container for tables.

In JayData you can define such a container by inheriting from the EntityContext class. We can think of EntityContext classes as client proxies to a local database or a remote data service.

Define a Simple Database Manually

Let’s extend our Task list example to add a categorization feature. This involves defining a Category type, and a Task type with a CategoryId field. Then you need to subclass/inherit from $data.EntityContext and define properties to denote tables.

var Task = $data.define("Task", {
    Todo: String,
    Completed: Boolean,
    CategoryId: Number
})

var Category = $data.define("Category", {
    CategoryName: String
});

var TaskDB = $data.EntityContext.extend("TaskDB", {
    Tasks: { type: $data.EntitySet, elementType: Task },
    Categories: { type: $data.EntitySet, elementType: Category }
});

var database = new TaskDB({ provider: 'local' });
var taskSource = database.Tasks.asKendoDataSource();
var categorySource = database.Categories.asKendoDataSource();

 

With a couple of lines of Kendo UI code, you can have a foreign key field implemented.

Interactive Sample

Meanwhile in the database:

Back to Odata

OData is a fantastic protocol. Not only does it provide standardized ways to access the functionality of the data API, but also exposes metadata in a way that lets the client know about the semantics of the service, like entity types or service function signatures.

In the case of containers, if the OData service has the same origin as the client code or the users's browser supports CORS, JayData provides dynamic metadata processing turning the $metadata document into a fully built client object model. It does all the $data.defines and object extending on your behalf. The story is pretty much the same afterwards…

$data.service("/northwind.svc", function (factory, type) {
    var northwind = factory();
    $('#grid').kendoGrid({
        dataSource: northwind.Products.asKendoDataSource(),
        editable: 'inline',
        pageable: true,
    });
});

Interactive Sample

Please be aware, that CORS only works reliably in the modern browsers.

Working with OData in different origin (without CORS)

As seen in previous sections you can always define any entity model, then direct it to an OData endpoint - and if they at least partially match the CRUD operations will work. With $data.service you can be sure that you are using the correct model as it is generated from the service metadata. But this requires the $metadata document to be accessible from the origin of your app.

If this is not the case you can use the JaySvcUtil which downloads service metadata and generates entity files that you can include in your HTML5 project. From that point JayData will fallback to JSONP. This must be supported on the server and only the GET operations are currently working.

Interactive Sample

Synchronize Online and Offline

Ah, the holy grail of synchronizing your offline datastore with the online one when it becomes available.

In the service init callback we receive a factory method that produces a living data context connected to the OData service. The type parameter is the EntityContext class that we can use to create a local database with the same schema as the online store.

var localDb = new type({ provider: 'local', databaseName: 'localTodoDb' })

A very simple example to have an offline copy of the remote data might look like this:

$data.service("/myDataService.svc", function (factory, type) {
    var northwind = factory();
    var localDB = new type({ provider: 'local', databaseName: 'localCache' });
    $.when(northwind.onReady(), localDB.onReady()).then(function () {
            return northwind
                .Categories
                .toArray(function (categories) {
                    localDB.categories.addMany(categories);
                    return localDB.saveChanges();
                })
        }).then(function () {
            alert("sync is done");
        });
});

 

Note that this example is rather error prone and doesn't scale well, but makes for a good simple example. We have examples for creating a better scaling version with paging and continuation, instead of slurping all the data in at once.

Want More JayData And Kendo UI?

If we've peeked your interest on using JayData with Kendo UI to manage you local and remote data dialects, we have quite a few demos and samples for you to get your hands on today! These include complex operations like cascading dropdown lists and even a mobile application sample! Yeah. We've got you covered.


About the Author

Peter Zentai

Peter Zentai is an architect and developer evangelist at JayStack.com, creator of JayData. Peter has an eternal passion toward data-driven apps and domain driven architecture - as he thinks it is the information schema that should work for you and not the other way around (at least this is what he says when asked about what he is doing at the moment). His current hobby (and also lifestyle) is to bring the OData protocol to JavaScript - in both the client and server scenarios.

Comments

Comments are disabled in preview mode.