Kendo UI is our flagship product when it comes to HTML5 based UI. Kendo UI provides Web, Data Visualization and Mobile widgets for building HTML5 Apps/Sites. One of the cool features of Kendo UI is the support for storing data offline when using our Data Source. We introduced this offline support as part of our Q2 2014 SP1 release. With offline support the data bound Kendo UI widgets will continue to work even when the server connection is not available. With this support users can continue working with the available data till the network connectivity is resumed.
Offline Feature of Kendo UI Data Source:
The way Kendo UI Data Source supports offline functionality is through a property called “offlineStorage”. We give a unique string value to this property. This value for “offlineStorage” will be used by the data source to create a key in local storage and save the data there. We need to let the data source know if its online of offline. This helps the data source decide whether to ping the remote end point or read from local storage. Data Source exposes a method named “online()” and expects a Boolean property to indicate online or offline.
Here is a code snippet of how you would use the offline feature of data source:
allProductsData = new kendo.data.DataSource({ | |
offlineStorage: “allProducts”, | |
type: "odata", | |
transport: { | |
read: { | |
url: "http://services.odata.org/V2/Northwind/Northwind.svc/Products", | |
dataType: "json", | |
} | |
}, | |
}); | |
allProductsData.online(“true|false”); |
We are setting a type property on data source to a value “odata”. This lets the data source know that it has to communicate using the OData V2 specification. Since we have set the property offlineStorage, data source will automatically store the data it receieved from server in the local storage key “allProducts”. We then let the data source know if we are online or offline by using the online() method.
At this point it’s important to understand the shape of the data that OData service returns when asked in JSON format. Following is a glimpse of the payload we get from an OData V2 GET request on any collection:
{ | |
"d": { | |
"results": [ | |
//records | |
... | |
] | |
} | |
} |
As you can see, the data is in a results array and is part of the padded “d” object. Now, Kendo UI Data Source understands all this and will be able to parse the payload and use the results array as the data. And it will save the data array to local storage also since we have provided an offlineStorage key. Note: data source will not save the entire payload to local storage. It will only store the data array found in the results property.
The Problem:
Everything is all right so far. So what is the problem you may ask? Let me explain it below:
Let’s assume the mobile app will be used by the user when he has network connection. The GetOnline() method will detect the status and return the Boolean value accordingly. So when there is a network connection, we set the online status of data source to true and it will go ahead make a request to the remote OData end point. The service will return the data in JSON format, data source will pass the payload, use the data array in the results property, and then store the data array in the local storage.
Let’s assume that the mobile app needs to be used when there is no network connection. Theoretically it should work. Why you ask – well we had told the data source to store the data offline in local storage. So whenever there is no network connection on the device and when we let the data source know that it is offline, we think that it will read data from local storage and everything should work normally. Well I thought so too. But we get the following error:
“TypeError: Unable to get property ‘results’ of undefined or null reference”
Hmm, it doesn’t work as expected. Well what is wrong then? Remember I said that the data shape of the odata GET on any collection is actually “d.results”. Well when data source sees that its offline, it will simply go ahead read data from the local storage. But the parsing logic is looking for “d.results” property on the saved data. Whereas the saved data in local storage is just a plain data array. It does not contain the padded d object and results property on d object. Hence the parser is looking for the d.results, doesn’t find it and throws up an error.
The Solution:
Kendo UI data source has a schema property exposed. Using the schema property, we can tell the parser where to look for the data in the payload. We do that by setting the data property of schema. In our case we just need to let the parser know that if it finds “d” padded object on the payload, it can then get the data from the results data array. Else just use the payload as it is because probably it’s the local storage data that was read. Just a simple if else condition to get out of this situation. Here is the code snippet:
allProductsData = new kendo.data.DataSource({ | |
offlineStorage: productsStorageKey, | |
type: "odata", | |
transport: { | |
read: { | |
url: "http://services.odata.org/V2/Northwind/Northwind.svc/Products", | |
dataType: "jsonp", | |
} | |
}, | |
schema:{ | |
data:function(response) | |
{ | |
var dataArray = null; | |
if(response.d){ | |
dataArray = response.d.results; | |
} | |
else{ | |
dataArray = response; | |
} | |
return dataArray; | |
} | |
} | |
}); |
With the above code changes, now whenever the data source in in offline mode, it will be able to read the data correctly from the local storage.
The above code is available as a demo here: http://dojo.telerik.com/@kashyapa/itUMe/2
Hope this helps anybody who has faced a similar situation.
Till next time, Happy Coding.
This helped me a great deal. Is there a sample getting offline sync to work to with odata. I find a property __state__ is causing issues but even after I manually delete data.__state__ I am unable to get the sync to work. Thanks again.
John
here is the documentation on how to work offline with Kendo UI DataSource – http://docs.telerik.com/kendo-ui/framework/datasource/offline
hope this helps.